제스처는 화면을 통해 전달된 사용자의 입력
GestureRecognizer는 제스처 인식기라고도 하며 여러 터치 이벤트를 인식. 특정 제스처 이벤트가 일어나면 각 타겟에 맞는 액션 메시지를 보내 제스처 관련 이벤트를 처리.
어떤 UIView객체가 이 Recognizer를 받아서 해당하는 제스처를 인식할 수 있도록 설정(제스처를 인식하는 파트)
-> GestureRecognizer를 생성하면 인식(View에게 이 GestureRecognizer를 사용하라고 요청)(오직 View만 제스처를 인식할 수 있고 Controller는 불가능)
그 Recognizer가 제스처를 인식햇을 때 처리(GestureHandler가 담당)
GestureHandler는 GestureRecognizer가 해당하는 제스처를 인식하는 상태기계로 갈때 호출 됨
GestureRecognizer를 생성하고 View에 추가하는건 일반적으로 Contoller가 처리(어떤 View들은 자체적으로 GestureRecognizer를 추가 ex.. ScrollView)
Controller는 제스처를 인식할 수 없음. 오직 GestureRecognizer를 가지고 있는 View만 가능
Handler는 Controller에서 처리할 수도 있고, View에서 처리할 수도 있음. 아예 다른 곳에서 처리할 수도 있지만 UI와 독립된 구조이기 때문에 모델에서 처리할 일은 없음.
상태기계
상태기계는 한 번에 하나의 상태를 가지고, 어떤 이벤트에 의해서 다른 상태로 전이 된다.
탭 체스처 인식
.Ended로 탭동작이 끝났는지 확인
var numberOfTapsRequired: Int//탭 횟수
var numberOfTouchesRequired: Int//손가락 개수
1. 스토리 보드에서 추가하기
shift + command + L 에서 Tap Gesuture Recognizer선택 후 드래그하여 추가(탭 횟수나 손가락 개수는 attributes inspector에서 설정)control + 드래그하여 controller와 연결 후 동작 설정@IBAction func toggleEyes(_ recognizer: UITapGestureRecognizer) {
if recognizer.state == .ended{
switch expression.eyes{
case .Open : expression.eyes = .Closed
case .Closed : expression.eyes = .Open
case .Squinting : break
}
}
}
//FaceViewController 클래스
@IBOutlet weak var faceView: FaceView! {
didSet {// 처음 뷰가 연결이 될 때 호출
let blinkEyesTapGestureRecognizer = UITapGestureRecognizer(
target: self, action: #selector(FaceViewController.blinkEyes)
)//target은 FaceViewController이므로 self(FaceViewController안에 faceView와 blinkEyes존재)
faceView.addGestureRecognizer(blinkEyesTapGestureRecognizer)
updateUI()// MVC가 생성된 직후에 iOS가 나타나고 이 outlet이 연결될때 호출
}
}
//FaceViewController 클래스
@objc func blinkEyes(){
if expression.eyes == .Open {
expression.eyes = .Closed
}else {
expression.eyes = .Open
}
}
핀치(두 손가락을 벌리거나 좁히는) 제스처 인식
var scale: CGFloat //비율
var velocity: CGFloat { get }//핀치 속도
//FaceViewController클래스
@IBOutlet weak var faceView: FaceView! {
didSet {// 처음 뷰가 연결이 될 때 호출
faceView.addGestureRecognizer(UIPinchGestureRecognizer(
target: faceView, action: #selector(FaceView.changeScale(_ : ))
))
updateUI()// MVC가 생성된 직후에 iOS가 나타나고 이 outlet이 연결될때 호출
}
}
//FaceView 클래스
@objc func changeScale(_ recognizer: UIPinchGestureRecognizer){
switch recognizer.state{
case .changed, .ended:
scale *= recognizer.scale
recognizer.scale = 1.0
default:
break
}
}
스와이프 제스처 인식
사용전에 설정해야함(어느방향으로 쓸어내릴지, 손가락 몇 개로 할지)
var direction: UISwipeGestureRecognizerDirection
var numberOfTouchesRequired: Int
1. 코드로 구현
//FaceViewController 클래스
@IBOutlet weak var faceView: FaceView! {
didSet {// 처음 뷰가 연결이 될 때 호출
let happierSwiperGestureRecognizer = UISwipeGestureRecognizer(
target: self, action: #selector(FaceViewController.increaseHappiness)
)
let sadderSwipeGestureRecognizer = UISwipeGestureRecognizer(
target: self, action: #selector(FaceViewController.decreaseHappiness)
)
happierSwiperGestureRecognizer.direction = .up//위로 스와이프할 때
faceView.addGestureRecognizer(happierSwiperGestureRecognizer)
sadderSwipeGestureRecognizer.direction = .down//아래로 스와이프 할때
faceView.addGestureRecognizer(sadderSwipeGestureRecognizer)
updateUI()// MVC가 생성된 직후에 iOS가 나타나고 이 outlet이 연결될때 호출
}
}
//FaceViewController 클래스
@objc func increaseHappiness(){
expression.mouth = expression.mouth.happierMouth()
}
@objc func decreaseHappiness(){
expression.mouth = expression.mouth.sadderMouth()
}
회전 제스처 인식
var rotation: CGFloat// 몇 라디안만큼 회전했는지
var velocity: CGFloat { get }//회전 속도
팬(드래그) 제스처 인식
func translationInView(UIView) -> CGPoint//view좌표에서 팬이 얼마나 움직였는지
func velocityInView(UIView) -> CGPoint//팬이 얼마나 빨리 진행되는지
func setTranslation(CGPoint, inView: UIView)//현재 뷰의 위치 설정
1. 코드로 구현하기
@IBOutlet weak var pannableView: UIView {
didSet{
let recognizer = UIPanGestureRecognizer(
target: self, action: #selector(ViewController.pan(_:))//Objective-c 런타임 호환 selector
)
pannableView.addGestureRecognizer(recognizer)
}
}
func pan(gesture: UIPanGestureRecognizer){
switch gesture.state{
case .Changed: fallthrough
case .Ended:
let translation = gesture.translationInView(pannableView)
//update anything that depends on the pan gesture using translation.x and .y
gesture.setTranslation(CGPointZero, inView: pannableView)
default: break;
}
}
화면 가장자리 팬 제스처 인식
길게 누르는 제스처 인식
호버(마우스나 펜 같은 포인터 장치를 사용할 때, 화면 위에서 움직이는) 제스처 인식
GestureRecognizer는 state변수를 가지고 있음
var state: UIGestureRecognizerState { get } : 핸들러에서 제스처가 어떤 상태에 있는지 확인할 수 있음. 모든 GestureRecognizer는 .Possible상태에서 시작
스와이프인 경우 감지하면 바로 .Recognized로 상태가 바뀜(스와이프를 인식한 것)
팬이나 핀치같은 지속되는 제스처는 팬이 눌리는 순간부터 바로 .Began이라는 상태가 됨. 팬이 움직이는 순간부터 .Changed라는 상태로 바뀜. 핸들러도 .Changed의 상태가 될 때마다 반복적으로 호출.
손가락이 화면에서 뗴지면 .Ended상태로 가게 됨
추가적으로 .Failed, .Cancelled도 존재
@objc func handleSwipeRight(_ gestureRecognizer: UISwipeGestureRecognizer) {
switch gestureRecognizer.state {
case .possible:
print("possible")
case .began:
print("began")
case .changed:
print("changed")
case .ended:
print("ended")
case .cancelled:
print("cancelled")
case .failed:
print("failed")
case .recognized:
print("recognized")
@unknown default:
fatalError()
}
}