ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [SwiftUI] DragGesture
    iOS 2023. 8. 4. 14:17
    DragGesture()
        .updating($translation){ value, state, _ in
            print("๐Ÿ‘€state (ํ˜„์žฌ ์ƒํƒœ) ", state, " ๐Ÿ‘€value.translation.height (์—…๋ฐ์ดํŠธ ๋œ ์ƒํƒœ)", value.translation.height)
            state = translation
        }
    }

    DragGesture๋Š” ๋“œ๋ž˜๊ทธ ์ด๋ฒคํŠธ ์‹œํ€€์Šค๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ ์•ก์…˜์„ ํ˜ธ์ถœํ•˜๋Š” ๋“œ๋ž˜๊ทธ ๋ชจ์…˜์ด๋‹ค.

    ๋ทฐ์—์„œ ๋“œ๋ž˜๊ทธ ์ œ์Šค์ฒ˜๋ฅผ ์ธ์‹ํ•˜๋ ค๋ฉด ์ œ์Šค์ฒ˜๋ฅผ ๋งŒ๋“ค๊ณ  ๊ตฌ์„ฑํ•œ ๋‹ค์Œ, gesture(_:including:) ๋ชจ๋””ํŒŒ์ด์–ด๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋ทฐ์— ์ถ”๊ฐ€ํ•œ๋‹ค.

     

    ๋‹ค์Œ์€ ์›์— ๋“œ๋ž˜๊ทธ ์ œ์Šค์ฒ˜๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ , ์‚ฌ์šฉ์ž๊ฐ€ ๋“œ๋ž˜๊ทธ ์ œ์Šค์ฒ˜๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” ๋™์•ˆ ์›์˜ ์ƒ‰์ƒ์„ ๋ณ€๊ฒฝํ•˜๋Š” ์ฝ”๋“œ๋‹ค.

    struct DragGestureView: View {
        @State private var isDragging = false
    
        var drag: some Gesture {
            DragGesture()
                .onChanged { _ in self.isDragging = true }
                .onEnded { _ in self.isDragging = false }
        }
    
        var body: some View {
            Circle()
                .fill(self.isDragging ? Color.red : Color.blue)
                .frame(width: 100, height: 100, alignment: .center)
                .gesture(drag)
        }
    }
    init(minimumDistance: CGFloat, coordinateSpace: CoordinateSpace)

    ์ œ์Šค์ฒ˜๊ฐ€ ์„ฑ๊ณตํ•˜๊ธฐ๊นŒ์ง€ ์ตœ์†Œ ๋“œ๋ž˜๊ทธ ๊ฑฐ๋ฆฌ์™€ ์ œ์Šค์ฒ˜ ์œ„์น˜์˜ ์ขŒํ‘œ ๊ณต๊ฐ„์„ ์‚ฌ์šฉํ•ด์„œ ๋“œ๋ž˜๊ทธ ์ œ์Šค์ฒ˜๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.

    minimumDistance๋Š” ์ œ์Šค์ฒ˜๊ฐ€ ์„ฑ๊ณตํ•˜๊ธฐ ์ „์˜ ์ตœ์†Œ ๋“œ๋ž˜๊ทธ ๊ฑฐ๋ฆฌ, coordinateSpace๋Š” ์œ„์น˜ ๊ฐ’์„ ์ˆ˜์‹ ํ•  ์ขŒํ‘œ ๊ณต๊ฐ„์ด๋‹ค.

    updating(_:body:)

    ์ œ์Šค์ฒ˜์˜ ๊ฐ’์ด ๋ณ€๊ฒฝ๋˜๋ฉด ์ œ๊ณต๋œ ์ œ์Šค์ฒ˜ ์ƒํƒœ (state) ํ”„๋กœํผํ‹ฐ๋ฅผ ์—…๋ฐ์ดํŠธํ•œ๋‹ค.

    func updating<State>(
        _ state: GestureState<State>,
        body: @escaping (Self.Value, inout State, inout Transaction) -> Void
    ) -> GestureStateGesture<Self, State>

    state๋Š” ๋ทฐ์˜ GestureState ํ”„๋กœํผํ‹ฐ์— ๋Œ€ํ•œ ๋ฐ”์ธ๋”ฉ์ด๊ณ , body๋Š” gesture์˜ ๊ฐ’์ด ๋ณ€๊ฒฝ๋  ๋•Œ SwiftUI๊ฐ€ ํ˜ธ์ถœํ•˜๋Š” ์ฝœ๋ฐฑ์ด๋‹ค.

    body์˜ ํด๋กœ์ € ํƒ€์ž…์€ (Self.Value, inout State, inout Transaction) -> Void์ด๋‹ค.

    ์—ฌ๊ธฐ์„œ Self.Value๋Š” currentState ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ, ์ œ์Šค์ฒ˜์˜ ์—…๋ฐ์ดํŠธ๋œ ์ƒํƒœ๋ฅผ ์˜๋ฏธํ•œ๋‹ค.

    inout State๋Š” gestureState ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ, ์ œ์Šค์ฒ˜์˜ ์ด์ „ ์ƒํƒœ๋ฅผ ์˜๋ฏธํ•œ๋‹ค.

    inout Transaction์€ transaction์„ ์˜๋ฏธํ•˜๊ณ , ์ œ์Šค์ฒ˜์˜ ์ปจํ…์ŠคํŠธ๋ฅผ ์˜๋ฏธํ•œ๋‹ค.

     

    ์ด ๋ฉ”์„œ๋“œ๋Š” ๊ธฐ์กด์˜ ์ œ์Šค์ฒ˜ ๊ฐ’์ด ๋ณ€๊ฒฝ๋˜๋ฉด ์ œ์Šค์ฒ˜์˜ ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธํ•œ๋‹ค.

    ๋˜๋Š” ์‚ฌ์šฉ์ž๋‚˜ ์‹œ์Šคํ…œ์ด ์ œ์Šค์ฒ˜๋ฅผ ์ข…๋ฃŒํ•˜๊ฑฐ๋‚˜ ์ทจ์†Œํ•˜๋ฉด ์ œ์Šค์ฒ˜ ๊ฐ’์„ ์ดˆ๊ธฐ๊ฐ’์œผ๋กœ ์žฌ์„ค์ •ํ•œ๋‹ค.

     

    ์˜ˆ๋ฅผ ๋“ค์–ด ์‚ฌ์šฉ์ž๊ฐ€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ™”๋ฉด์— ์•„์ฃผ ์งง์€ ์‹œ๊ฐ„ ๋™์•ˆ ์œ„์—์„œ ์•„๋ž˜๋กœ ์†๊ฐ€๋ฝ์„ ํ„ฐ์น˜ํ•˜๋Š” ์•ก์…˜์„ ์ทจํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜์ž.

    ์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ž‘์„ฑํ–ˆ๋‹ค.

    DragGesture()
        .updating($translation) { currentState, gestureState, _ in
            print("๐Ÿ‘€gestureState (์ด์ „ ์ƒํƒœ)", gestureState, " ๐Ÿ‘€currentState (์—…๋ฐ์ดํŠธ๋œ ์ƒํƒœ)", currentState.translation.height)
            gestureState = currentState.translation.height // ์ƒํƒœ ์—…๋ฐ์ดํŠธ
        }
    }

     

    ์ด ๋•Œ updating(_:body:) ๋ฉ”์„œ๋“œ ๋‚ด๋ถ€์—์„œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ผ์ด ์ผ์–ด๋‚œ๋‹ค.

    ์ตœ์ดˆ state๋Š” 0.0์ด๊ณ , ์‚ฌ์šฉ์ž๊ฐ€ ์ œ์Šค์ฒ˜๋ฅผ ์ทจํ•˜์ž ์ƒํƒœ๊ฐ€ ์—…๋ฐ์ดํŠธ๋˜์–ด์„œ currentState๊ฐ€ ์—…๋ฐ์ดํŠธ๋œ๋‹ค. 

    ๊ทธ๋ฆฌ๊ณ  ์ด์ „ ์ƒํƒœ๋ฅผ ์ƒˆ๋กœ์šด ์ƒํƒœ๋กœ ์—…๋ฐ์ดํŠธํ•ด์ฃผ๊ธฐ ๋•Œ๋ฌธ์— gestureState๋Š” currentState๋กœ ์—…๋ฐ์ดํŠธ๋œ๋‹ค.

    ์ด์–ด์„œ ๋˜ currentState๊ฐ€ ์—…๋ฐ์ดํŠธ๋˜๋ฏ€๋กœ gestureState ๋˜ํ•œ ์ƒˆ๋กœ์šด ๊ฐ’์œผ๋กœ ์—…๋ฐ์ดํŠธ๋œ๋‹ค.

     

    Respond to gesture callbacks

    ์ œ์Šค์ฒ˜ ๋ชจ๋””ํŒŒ์ด์–ด์— ์ถ”๊ฐ€ํ•œ ์ฝœ๋ฐฑ์— ๋”ฐ๋ผ, ์ œ์Šค์ฒ˜์˜ state๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค SwiftUI๊ฐ€ ์ฝ”๋“œ์— ๋‹ค์‹œ ๋ณด๊ณ ๋ฅผ ํ•œ๋‹ค. ์ œ์Šค์ฒ˜ ๋ชจ๋””ํŒŒ์ด์–ด๋Š” ์—…๋ฐ์ดํŠธ๋ฅผ ์ˆ˜์‹ ํ•˜๊ธฐ ์œ„ํ•ด 3๊ฐ€์ง€ ๋ฐฉ๋ฒ•์„ ์ œ๊ณตํ•œ๋‹ค: update(_:body:), onChanged(_:), onEnded(_:)

    Update transient UI state (์ผ์‹œ์ ์ธ UI ์ƒํƒœ ์—…๋ฐ์ดํŠธ)

    ์ œ์Šค์ฒ˜๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ ๋ทฐ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋ ค๋ฉด, ๋ทฐ์— GestureState ํ”„๋กœํผํ‹ฐ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  update(_:body:) ์ฝœ๋ฐฑ์—์„œ ์—…๋ฐ์ดํŠธํ•œ๋‹ค. SwiftUI๋Š” ์ œ์Šค์ฒ˜๋ฅผ ์ธ์‹ํ•˜๋Š” ์ฆ‰์‹œ, ๊ทธ๋ฆฌ๊ณ  ์ œ์Šค์ฒ˜์˜ ๊ฐ’์ด ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค ์—…๋ฐ์ดํŠธ ์ฝœ๋ฐฑ์„ ํ˜ธ์ถœํ•œ๋‹ค.

    ์˜ˆ๋ฅผ ๋“ค์–ด SwiftUI๋Š” magnification ์ œ์Šค์ฒ˜๊ฐ€ ์‹œ์ž‘๋˜์ž๋งˆ์ž ์—…๋ฐ์ดํŠธ ์ฝœ๋ฐฑ์„ ํ˜ธ์ถœํ•˜๊ณ , ๋‹ค์Œ magnification ๊ฐ’์ด ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค ๋‹ค์‹œ ์—…๋ฐ์ดํŠธ ์ฝœ๋ฐฑ์„ ํ˜ธ์ถœํ•œ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ ์ œ์Šค์ฒ˜๋ฅผ ์ข…๋ฃŒํ•˜๊ฑฐ๋‚˜ ์ทจ์†Œํ•  ๋•Œ์—๋Š” ์—…๋ฐ์ดํŠธ ์ฝœ๋ฐฑ์„ ํ˜ธ์ถœํ•˜์ง€ ์•Š๋Š”๋‹ค. ๋Œ€์‹ , GestureState ํ”„๋กœํผํ‹ฐ๊ฐ€ ์ž๋™์œผ๋กœ state ๊ฐ’์„ ์ดˆ๊ธฐ๊ฐ’์œผ๋กœ ์žฌ์„ค์ •ํ•œ๋‹ค.

    ์˜ˆ๋ฅผ ๋“ค์–ด ์‚ฌ์šฉ์ž๊ฐ€ ๊ธธ๊ฒŒ ๋ˆ„๋ฅด๋Š” ๋™์•ˆ ์ƒ‰์ƒ์ด ๋ณ€๊ฒฝ๋˜๋Š” ๋ทฐ๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด์„œ๋Š” GestureState ํ”„๋กœํผํ‹ฐ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  ์—…๋ฐ์ดํŠธ ์ฝœ๋ฐฑ์—์„œ ์—…๋ฐ์ดํŠธํ•˜๋ฉด ๋œ๋‹ค.

    struct CounterView: View {
        @GestureState private var isDetectingLongPress = false
    
        var body: some View {
            let press = LongPressGesture(minimumDuration: 1)
                .updating($isDetectingLongPress) { currentState, gestureState, transaction in
                                    // gestureState๋ฅผ ์ƒˆ๋กœ์šด ๊ฐ’์œผ๋กœ ์—…๋ฐ์ดํŠธ
                    gestureState = currentState 
                }
    
            return Circle()
                .fill(isDetectingLongPress ? Color.yellow : Color.green)
                .frame(width: 100, height: 100, alignment: .center)
                .gesture(press)
        }
    }

    Update permanent state during a gesture (์˜๊ตฌ์ ์ธ UI ์ƒํƒœ ์—…๋ฐ์ดํŠธ)

    ์ œ์Šค์ฒ˜๊ฐ€ ์ข…๋ฃŒ๋˜์–ด๋„ ์žฌ์„ค์ •๋˜์ง€ ์•Š๋Š” ์ œ์Šค์ฒ˜์˜ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์ถ”์ ํ•˜๋ ค๋ฉด, onChanged(_:) ์ฝœ๋ฐฑ์„ ์‚ฌ์šฉํ•œ๋‹ค.

    ์˜ˆ๋ฅผ ๋“ค์–ด, ์•ฑ์—์„œ ๊ธธ๊ฒŒ ๋ˆ„๋ฅด๋Š” ๋™์ž‘์„ ์ธ์‹ํ•˜๋Š” ํšŸ์ˆ˜๋ฅผ ๊ณ„์‚ฐํ•˜๋ ค๋ฉด onChanged(_:) ์ฝœ๋ฐฑ์„ ์ถ”๊ฐ€ํ•˜๊ณ  ์นด์šดํ„ฐ๋ฅผ ์ฆ๊ฐ€์‹œํ‚ค๋ฉด ๋œ๋‹ค.

    struct CounterView: View {
        @GestureState private var isDetectingLongPress = false
        @State private var totalNumberOfTaps = 0
    
        var body: some View {
            let press = LongPressGesture(minimumDuration: 1)
                .updating($isDetectingLongPress) { currentState, gestureState, transaction in
                    gestureState = currentState
                }.onChanged { _ in
                    self.totalNumberOfTaps += 1
                }
    
            return VStack {
                Text("\(totalNumberOfTaps)")
                    .font(.largeTitle)
    
                Circle()
                    .fill(isDetectingLongPress ? Color.yellow : Color.green)
                    .frame(width: 100, height: 100, alignment: .center)
                    .gesture(press)
            }
        }
    }

    Update permanent state when a gesture ends (์ œ์Šค์ฒ˜๊ฐ€ ์ข…๋ฃŒ๋  ๋•Œ ์˜๊ตฌ์ ์œผ๋กœ ์ƒํƒœ ์—…๋ฐ์ดํŠธ)

    ์ œ์Šค์ฒ˜๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ์™„๋ฃŒ๋œ ์‹œ์ ์„ ์ธ์ง€ํ•˜๊ณ  ์ œ์Šค์ฒ˜์˜ ์ตœ์ข…๊ฐ’์„ ๋ฐ›์œผ๋ ค๋ฉด, onEnded(_:) ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ฝœ๋ฐฑ์—์„œ ์•ฑ์˜ ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธํ•œ๋‹ค. SwiftUI๋Š” ์ œ์Šค์ฒ˜๊ฐ€ ์„ฑ๊ณตํ•  ๋•Œ๋งŒ onEnded(_:) ์ฝœ๋ฐฑ์„ ํ˜ธ์ถœํ•œ๋‹ค.

    ์˜ˆ๋ฅผ ๋“ค์–ด, LongPressGesture ๋„์ค‘์— ์‚ฌ์šฉ์ž๊ฐ€ minimumDuration ์ดˆ๊ฐ€ ๊ฒฝ๊ณผํ•˜๊ธฐ ์ „์— ๋ทฐ ํ„ฐ์น˜๋ฅผ ๋ฉˆ์ถ”๊ฑฐ๋‚˜, maximumDistance๋ณด๋‹ค ๋” ๋งŽ์ด ์†๊ฐ€๋ฝ์„ ์›€์ง์ด๋ฉด, SwiftUI๋Š” onEnded(_:) ์ฝœ๋ฐฑ์„ ํ˜ธ์ถœํ•˜์ง€ ์•Š๋Š”๋‹ค. ์ฆ‰, minimumDuration ์ดˆ๋ฅผ ๋„˜์–ด์„œ ๋ทฐ ํ„ฐ์น˜๋ฅผ ์ง„ํ–‰ํ•˜๊ณ , maximumDistance๋ฅผ ๋„˜์–ด์„œ ์†๊ฐ€๋ฝ์„ ์›€์ง์ด์ง€ ์•Š๋Š” ๊ฒฝ์šฐ์—๋งŒ onEnded(_:) ์ฝœ๋ฐฑ์„ ํ˜ธ์ถœํ•œ๋‹ค.

    ์˜ˆ๋ฅผ ๋“ค์–ด ์‚ฌ์šฉ์ž๊ฐ€ long press๋ฅผ ์™„๋ฃŒํ•œ ํ›„ long press ์‹œ๋„ ํšŸ์ˆ˜ ๊ณ„์‚ฐ์„ ์ค‘์ง€ํ•˜๋ ค๋ฉด, onEnded(_:) ์ฝœ๋ฐฑ์„ ์ถ”๊ฐ€ํ•˜๊ณ  ์ œ์Šค์ฒ˜ ๋ชจ๋””ํŒŒ์ด์–ด๋ฅผ ์กฐ๊ฑด๋ถ€๋กœ ์ถ”๊ฐ€ํ•˜๋ฉด ๋œ๋‹ค.

    struct CounterView: View {
        @GestureState private var isDetectingLongPress = false
        @State private var totalNumberOfTaps = 0
        @State private var doneCounting = false
    
        var body: some View {
            let press = LongPressGesture(minimumDuration: 1)
                .updating($isDetectingLongPress) { currentState, gestureState, transaction in
                    gestureState = currentState
                }.onChanged { _ in
                    self.totalNumberOfTaps += 1
                }
                .onEnded { _ in
                    self.doneCounting = true
                }
    
            return VStack {
                Text("\(totalNumberOfTaps)")
                    .font(.largeTitle)
    
                Circle()
                    .fill(doneCounting ? Color.red : isDetectingLongPress ? Color.yellow : Color.green)
                    .frame(width: 100, height: 100, alignment: .center)
                    .gesture(doneCounting ? nil : press)
            }
        }
    }

     

    https://developer.apple.com/documentation/swiftui/draggesture

    Adding interactivity with gestures | Apple Developer Documentation

    ๋Œ“๊ธ€

Designed by Tistory.