swiftUI에는 다양한 Gesture를 보다 쉽게 구현할 수 있도록 도와준다. Gesture를 감지하는 변수를 GestureState 키워드를 통해 바인딩을 하여 변수가 true일 때만 guesture를 실행하는 식으로 Gesture를 제공하고 있다.
Gesture는 총 5가지 종류의 제스쳐가 있다.
— TapGesture : 하나 이상의 탭을 인식하는 제스쳐이다.
— LongPressGesture : 길게 누르면 인식하는 제스쳐이다.
— DragGesture : 드래그 이벤트가 변경될 때 작업을 호출하는 제스쳐이다.
— MagnificationGesture : 확대를 할 때 인식하는 제스쳐이다.
—RotationGesture : 회전 동작을 인식하고 회전 각도를 추적하는 제스쳐이다.
LongPressGesture
import SwiftUI
struct ContentView : View {
@GestureState var isLongPressed = false
var body: some View {
let longPress = LongPressGesture()
.updating($isLongPressed) { (value, state, transcation) in
state = value
}
return Rectangle()
.fill(isLongPressed ? Color.purple : Color.red)
.frame(width: 300, height: 300)
.cornerRadius(8)
.shadow(radius: 8)
.padding()
.scaleEffect(isLongPressed ? 1.1 : 1)
.gesture(longPress)
.animation(.easeInOut, value: isLongPressed)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
LongPressGesture를 사용하여 버튼을 길게 클릭하면 사각형의 생상과 크기가 변경되는 코드이다.
제일 먼저 필요한건 @GestureState로 바인딩된 변수이다. State랑 똑같은 역할을 하는데 Gesture랑 구분을 하기위해서 다른 키워드로 바인딩을 하는 것 같다.
LongPressGesture
Parameter
— minimumDuration(Double) : 제스처가 성공하기까지의 최소시간을 입력한다.
— maximumDuration(CGFloat) : 제스처가 성공하기까지의 최대시간을 입력한다.
Function
updating : 제스처 값이 변경되면 제스처 상태 속성을 업데이트한다.
.updating($isLongPressed) { (value, state, transcation) in
//value : 파라미터의 현재 상태, state : 제스쳐의 상태, transcation : 계층간의 애니메이션 전달
state = value
}
onChanged : 제스처 값이 변경될 때 수행할 작업을 추가한다.
onEnded : 제스처가 끝날 때 수행 할 작업을 추가한다.
DragGesture
드래그 이벤트가 발생할 때 작업을 호출하는 제스쳐이다.
Example
import SwiftUI
struct ContentView : View {
@State private var offset: CGSize = .zero
var body: some View {
let drag = DragGesture()
.onChanged { self.offset = $0.translation}
.onEnded {
//드래그 가로의 위치가 -100보다 작은 위치로 가면 실행
if $0.translation.width < -100 {
self.offset = .init(width: -1000, height: 0)
//드래그 가로의 위치가 100보다 커지면 실행
} else if $0.translation.width > 100 {
self.offset = .init(width: 1000, height: 0)
//아니면 원래 위치로 돌아감
} else {
self.offset = .zero
}
}
return PersonView()
.background(Color.red)
.cornerRadius(8)
.shadow(radius: 8)
.padding()
.offset(x: offset.width, y: offset.height)
.gesture(drag)
.animation(.interactiveSpring(), value: offset)
}
}
struct PersonView: View {
var body: some View {
VStack(alignment: .leading) {
Rectangle()
.fill(Color.gray)
.cornerRadius(8)
.frame(height: 300)
Text("Swift UI")
.font(.title)
Text("Tutorial")
.font(.body)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
DragGesture을 통해 일정 거리만큼 좌우로 드래그를 하면 해당 컴포넌트가 사라지는 예제이다.
offset을 @State 바인딩을 하여 선언해서 width와 height가 바뀔 때마다 제스쳐를 수행한다.
onChanged 함수를 통해 드래그하는 방향으로 컴포넌트의 위치를 이동하게 만들었다.
onEnded 함수를 사용해서 width가 일정 범위를 벗어나면 offset을 화면에서 안보이는 위치로 바꾸어서 컴포넌트가 사라진 것처럼 구현하였다.
Parameter
— minimumDistance(CGFloat) : 제스처가 성공하기 전 최소 거리를 정해준다.
— coordinateSpace(CoordinateSpace) : 위치 값을 받을 좌표 공간이다.
simultaneously()
여러 제스쳐를 동시에 사용하고 싶을 때 사용하는 함수이다.
Example
import SwiftUI
struct ContentView : View {
@State private var offset: CGSize = .zero
@GestureState var isLongPressed = false
var body: some View {
let longPressAndDrag = LongPressGesture()
.updating($isLongPressed) { (value, state, transition) in
state = value
//드래그 제스쳐 추가
}.simultaneously(with: DragGesture()
.onChanged { self.offset = $0.translation }
.onEnded { _ in self.offset = .zero}
)
return Rectangle()
.fill(isLongPressed ? Color.purple : Color.red)
.frame(width: 300, height: 300)
.cornerRadius(8)
.shadow(radius: 8)
.padding()
.scaleEffect(isLongPressed ? 1.1 : 1)
.offset(x: offset.width, y: offset.height)
.gesture(longPressAndDrag)
.animation(.interactiveSpring(), value: offset)
.animation(.interactiveSpring(), value: isLongPressed)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
첫번째 예제의 LongPressGesture에 simultaneously 함수를 사용하여 DragGesture도 같이 적용하는 코드이다. 드래그가 끝나면 컴포넌트는 다시 원래 위치로 돌아온다.