예전에 DragGesture를 통해서 View를 움직이는 방법을 소개한 적이 있는데요. 그 때는 그냥 @State에 CGFloat를 할당해서 구현했었습니다. 하지만 좀 더 Gesture에 특화된 property wrapper가 있는데요. 바로 @GestureState입니다.
@GestureState는 Gesture와 연결하는 특수한 @State 변수입니다. 이 변수를 DragGesture를 통해 업데이트하고 이 값을 View의 offset에 연결하여 View를 움직입니다.
import SwiftUI
struct MainView: View {
@GestureState var dragPosition = CGSize.zero
var body: some View {
Image(systemName: "heart")
.offset(dragPosition)
.gesture(
DragGesture()
.updating($dragPosition) { value, state, transaction in
state = value.translation
}
)
}
}
@GestureState 변수의 초기값은 CGSize.zero입니다. 따라서 View는 원래 위치에 존재합니다. 그리고 DragGesture에 updating이라는 메소드로 연결합니다. binding으로 연결해주어야 하고요. 어떤 방식으로 업데이트할 것인지 클로저로 정의해주어야 합니다.
해당 클로저는 3가지 인자를 받는데요. 첫 번째는 Gesture.Value 타입으로 드래그 동작에 대한 정보를 담고 있습니다. 두 번째는 State로 이 변수에 값을 할당하면 @GestureState의 값이 변화합니다. 마지막으로 Transaction인데요. Animation과 관련된 타입이라고 합니다. (다음에 좀 더 자세하게 알아보겠습니다.)
state에 value.translation을 할당합니다. 이렇게 하면 @GestureState 변수에 드래그의 시작 위치에서 현재 위치까지의 위치 변화가 CGSize 타입으로 할당이 됩니다. 그렇게 되면 이 변수는 .offset()으로 연결되어 있으므로 이미지가 드래그를 따라서 이동하게 됩니다.
좌우로만 이동할 수 있도록 구현하고 싶다면 어떻게 하면 될까요? 바로 아래처럼 state.width에만 value.translaction.width만 할당하면 됩니다. 이렇게 하면 CGSize의 width만 업데이트 되므로 위 아래로 움직이는 제스쳐는 무시되고 좌우 이동만 뷰에 반영이 됩니다.
import SwiftUI
struct MainView: View {
@GestureState var dragPosition = CGSize.zero
var body: some View {
Image(systemName: "heart")
.offset(dragPosition)
.gesture(
DragGesture()
.updating($dragPosition) { value, state, transaction in
state.width = value.translation.width
}
)
}
}
위에 배웠던 클로저의 정의를 그대로 이용해서 함수를 별도로 구현해서 간단하게 사용할 수도 있습니다.
함수에 인자로 전달할 때는 참조를 전달하는 것이 아니라 값을 복사해서 전달합니다. 따라서 inout을 통해서 참조를 전달해야 할 인자는 참조를 전달할 수 있도록 합니다.
private func dragUpdating(_ value: _EndedGesture<DragGesture>.Value, _ state: inout CGSize, _ transaction: inout Transaction) {
state.width = value.translation.width
}
someView
.gesture(dragGesture
.updating($dragAmount) { dragUpdating($0, &$1, &$2) }
)