iOS 응용 (SwiftUI)

[iOS - SwiftUI] 뷰를 버튼처럼 만드는 방법 (DragGesture, pressed color 효과, 커스텀 버튼)

jake-kim 2024. 10. 14. 01:43

뷰를 버튼처럼 만드는 방법

  • 일반 SwiftUI의 버튼은 Button(action:label:)형태이며, presssed color는 label부분이 투명해지는 효과가 디폴트로 들어간 상태

(코드)

struct ContentView: View {
    @State private var isPressed = false
    @State private var isChecked = false
    
    var body: some View {
        VStack {
            normalButton
        }
    }
    
    @ViewBuilder
    private var normalButton: some View {
        Button(action: {
            isChecked.toggle()
        }, label: {
            HStack(spacing: 6) {
                Image(systemName: isChecked ? "circle.fill" : "circle")
                Text("button")
            }
        })
        .background(isPressed ? Const.color : Const.pressedColor)
    }
}
  • Button을 안쓰고 싶거나 pressed color를 주는 커스텀 버튼으로 만들고 싶은 경우?
    • 일반 뷰에다 .gesture()로 제스처를 등록하고, 사용자가 누르고 있는 동안은 색상이 변경되어 보여야하므로 pressed 효과를 주기 위해 DragGesture를 활용
    • drag의 상태가 onChnaged인 경우에는 isPressed를 true로 넣고, end상태인 경우 isPressed를 false로 세팅

커스텀 버튼 만들기

  • @ViewBuilder로 선언
@ViewBuilder
private var customButton: some View {
}
  • 이미지와 Text가 들어가 있는 버튼을 만들어야 하기 때문에 HStack 사용
    @State private var isChecked = false
    @State private var isPressed = false
    
    HStack(spacing: 6) {
        Image(systemName: isChecked ? "circle.fill" : "circle")
        Text("button")
    }
    .background(isPressed ? Const.color : Const.pressedColor)
  • 여기에 DragGesture를 등록
    • 단순히 한 점을 클릭해도 동작해야하므로 minimumDistance를 0으로 설정
    .gesture(
        DragGesture(minimumDistance: 0)
            .onChanged { _ in
                isPressed = true
            }
            .onEnded { _ in
                isPressed = false
                isChecked.toggle()
            }
    )

 

완성) 

 

전체 코드) 

import SwiftUI

struct ContentView: View {
    @State private var isPressed = false
    @State private var isChecked = false
    
    var body: some View {
        VStack {
            normalButton
            
            customButton
        }
    }
    
    @ViewBuilder
    private var normalButton: some View {
        Button(action: {
            isChecked.toggle()
        }, label: {
            HStack(spacing: 6) {
                Image(systemName: isChecked ? "circle.fill" : "circle")
                Text("button")
            }
        })
        .background(isPressed ? Const.color : Const.pressedColor)
    }
    
@ViewBuilder
private var customButton: some View {
    HStack(spacing: 6) {
        Image(systemName: isChecked ? "circle.fill" : "circle")
        Text("button")
    }
    .background(isPressed ? Const.color : Const.pressedColor)
    .gesture(
        DragGesture(minimumDistance: 0)
            .onChanged { _ in
                isPressed = true
            }
            .onEnded { _ in
                isPressed = false
                isChecked.toggle()
            }
    )
}
}

#Preview {
    ContentView()
}

private enum Const {
    static let color = Color.gray
    static let pressedColor = Self.color.opacity(0.8)
}