[iOS] UIPanGestureRecognizer: ๋ทฐ๋ฅผ ๋๋๊ทธ ํ๊ธฐ
UIPanGestureRecognizer
UIPanGestureRecognizer๋ GestureRecognizer์ ์๋ธํด๋์ค ์ค ํ๋๋ก ์ฌ์ฉ์๊ฐ ํ๋ฉด์ ํญ ํ๊ณ ์์ง์ด๋ ๋์์ ์ธ์ํ์ฌ ์ฒ๋ฆฌํ ์ ์๊ฒ ํด์ค๋๋ค. UIPanGestureRecognizer์ ๋ฉ์๋๋ translation(์์ง์)๊ณผ velocity(์๋)๋ฅผ ์ป์ด์ฌ ์ ์๋๋ก ํฉ๋๋ค. ์ค๋์ UIPanGestureRecognizer๋ฅผ ํตํด์ ์๋ ๋ณด์ด๋ ๋๊ทธ๋ ์์ ๋ค๋ฅธ ์์น๋ก ์์ง์ผ ์ ์๊ฒ ํ๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ฐ๋จํ๊ฒ ๋ง๋ค์ด๋ณด๊ฒ ์ต๋๋ค.
UIPanGestureRecognizer ์ถ๊ฐํ๊ธฐ
์คํ ๋ฆฌ๋ณด๋๋ก ์ถ๊ฐํ๊ธฐ
์คํ ๋ฆฌ ๋ณด๋๋ก ์ถ๊ฐํ ๋๋ ํญ์ ํ๋ ๊ฒ์ฒ๋ผ ์คํ ๋ฆฌ๋ณด๋์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ํตํด ์ถ๊ฐํ ์ ์์ต๋๋ค.
๋ผ์ด๋ธ๋ฌ๋ฆฌ์์ PanGestureRecognizer๋ฅผ ์ ํํ๊ณ ,
์ ์ค์ฒ ์ธ์๊ธฐ๋ฅผ ์ถ๊ฐํ๊ณ ์ ํ๋ ๋ทฐ์ ๋์ด๋์ผ๋ฉด ์ธ์๊ธฐ๊ฐ ์ถ๊ฐ๋ฉ๋๋ค. ์ ์ค์ฒ๊ฐ ์ ์ธ์๋๋์ง ํ์ธํ๊ธฐ ์ํด์ ํ๋จ์ ๋ ์ด๋ธ์ ํ๋ ์ถ๊ฐํ๊ณ ๋๋๊ทธ๊ฐ ์ธ์๋๋ฉด ์ํ๋ฅผ ๋ณด์ฌ์ฃผ๋๋ก ํ๊ฒ ์ต๋๋ค.
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var statusLabel: UILabel!
@IBOutlet weak var targetCircle: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func panGestureHandler(_ sender: Any) {
self.statusLabel.text = "Recognized"
}
}
์ฝ๋๋ก ์ถ๊ฐํ๊ธฐ
์ฝ๋๋ก ์ถ๊ฐํ ๋๋ UIPanGestureRecognizer ์ธ์คํด์ค๋ฅผ ์์ฑํ ๋ค, ์ํ๋ ๋ทฐ์ ์ถ๊ฐํด์ค ์ ์์ต๋๋ค.
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var targetCircle: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(panGestureHandler))
self.targetCircle.addGestureRecognizer(panGestureRecognizer)
}
@objc func panGestureHandler(sender: UIPanGestureRecognizer) {
print("pan gesture recognized")
}
}
action ์ธ์์๋ ์ ์ค์ฒ๊ฐ ์ธ์๋์์ ๋ ์คํํ๊ณ ์ ํ๋ ๋์๋ค์ ๋ฉ์๋๋ก ์ ์ํด์ฃผ๋ฉด ๋๋๋ฐ์, ์คํ ๋ฆฌ ๋ณด๋์์ ์ถ๊ฐํ์ ๋์ IBAction๊ณผ ๋์ผํฉ๋๋ค. ์์ฒ๋ผ ์ฝ๋๋ฅผ ์์ฑํ๊ณ ์คํํด๋ณด๋ฉด ๋ค์๊ณผ ๊ฐ์ด ์ํ๋ ์์ญ์ ๋๋๊ทธํ์ ๋๋ง ์ ์ค์ฒ ์ธ์์ด ๋์ผํ๊ฒ ์ ๋์ํ๋ ๊ฒ์ ํ์ธํ ์ ์์ต๋๋ค.
UIPanGestureRecognizer๊ฐ ์๋ ค์ฃผ๋ ์ ๋ณด 1: ์ํ
UIPanGesutreRecognizer๋ ๋ค๋ฅธ GestureRecognizer๋ค์ฒ๋ผ ์ฌ์ฉ์์ ์ ์ค์ฒ ์ํ๋ฅผ ์๋ ค์ค๋๋ค. GestureRecognizer์ ์ํ์ ๋ํ ์ด์ผ๊ธฐ๋ ๋ค๋ฅธ ํฌ์คํธ์์ ๋ ์์ธํ ๋ค๋ฃจ๋๋ก ํ๊ฒ ์ต๋๋ค.
UIPanGestureRecognizer์์ ์ฐ๋ฆฌ๊ฐ ์ฃผ๋ชฉํด์ผ ํ ์ํ๋ Began, Changed, Ended์ ๋๋ค. Began์ ์ ์ค์ฒ ์ธ์๊ธฐ๊ฐ ์ฌ์ฉ์์ ์ ์ค์ฒ๋ฅผ ์ธ์ํ์ฌ ๋๋๊ทธ๊ฐ ์์ํ์์ ์๋ฏธํ๊ณ , Changed๋ ๋๋๊ทธ๊ฐ ๊ณ์๋์ด ์์น๊ฐ ๋ณํ๋จ์, ๊ทธ๋ฆฌ๊ณ Ended๋ ์ฌ์ฉ์์ ์๋๋๋ก(์๋ํ์ง ์์ ๊ฒฝ์ฐ๋ ์์ต๋๋ค. ์๋ฆผ์ด ๋ฌ๋ค๋์ง, ์ ํ๊ฐ ์จ๋ค๋์ง..) ์๊ฐ๋ฝ์ ํ๋ฉด์์ ๋ผ์ด์ ๋๋๊น ์ด ์ข ๋ฃ๋์์์ ์๋ฏธํฉ๋๋ค.
๊ทธ๋ผ ํ๋จ ๋ ์ด๋ธ์ ์ํ๊ฐ ๋ํ๋๋๋ก ํด๋ณผ๊น์?
@IBAction func panGestureHandler(_ sender: UIPanGestureRecognizer) {
switch sender.state {
case .possible:
self.statusLabel.text = "Possible"
case .began:
self.statusLabel.text = "Began"
case .changed:
self.statusLabel.text = "Changed"
case .cancelled:
self.statusLabel.text = "Cancelled"
case .ended:
self.statusLabel.text = "Ended"
case .failed:
self.statusLabel.text = "Failed"
}
}
์ด๋ ๊ฒ ์ ์ค์ฒ ์ธ์๊ธฐ๊ฐ ๋์ํ ๋๋ง๋ค ๊ฐ ์ํ๋ฅผ ๋ ์ด๋ธ์ ๋ํ๋ด๋๋ก ์ฝ๋๋ฅผ ์์ฑํ๊ณ ์คํํ๋ฉด
๋น ๋ฅด๊ฒ Begin์ ๊ฑฐ์ณ Changed ๊ฐ ๋์๋ค๊ฐ ๋๋๊ทธ๋ฅผ ์ค๋จํ๋ฉด Ended ๊ฐ ๋๋ ๊ฒ์ ํ์ธํ ์ ์์ต๋๋ค.
๋ง์ฝ ์ ์ค์ฒ๊ฐ ์ฒ์ ์ธ์๋์์ ๋ ์ด๋ค ์์ ์ ํ๊ณ ์ถ๋ค๋ฉด Began ์ํ์ ํด๋น ๋ก์ง์ ์์ฑํ๊ณ , ์ ์ค์ฒ๊ฐ ๋๋ฌ์ ๋ ์ด๋ค ์์ ์ ์ง์ ํ๊ณ ์ถ๋ค๋ฉด Ended ์ํ์ ๋ก์ง์ ์์ฑํ๋ฉด ๋ฉ๋๋ค.
UIPanGestureRecognizer๊ฐ ์๋ ค์ฃผ๋ ์ ๋ณด 2: Translation
๋์ ๋ถ์์ ์ค๋ช ํ๋ ๊ฒ์ฒ๋ผ UIPanGestureRecognizer๋ Translation ๋ฉ์๋๋ฅผ ์ ๊ณตํฉ๋๋ค.
translation(in:)
Interprets the pan gesture in the coordinate system of the specified view.
Declaration
func translation(in view: UIView?) -> CGPoint
Parameters
view
The view in whose coordinate system the translation of the pan gesture should be computed. If you want to adjust a view's location to keep it under the user's finger, request the translation in that view's superview's coordinate system.
Return Value
A point identifying the new location of a view in the coordinate system of its designated superview.
๊ณต์ ๋ฌธ์์ ์ค๋ช ์ ๋ณด๋ฉด ์ดํดํ๊ธฐ๊ฐ ์ฌ์ด๋ฐ์, ์ธ์๋ก ์ ๋ฌ๋๋ ๋ทฐ์ ์ขํ๊ณ์ ๋ฐ๋ผ์ ์ฒ์ ์ ์ค์ฒ๊ฐ ์ธ์๋์์ ๋๋ถํฐ ์ผ๋ง๋ ์ขํ๊ฐ ์ด๋๋์๋์ง๋ฅผ ๋ฐํํด์ค๋๋ค. ์ฐ๋ฆฌ๋ ๋๊ทธ๋ผ๋ฏธ ๋ทฐ์ ์ ์ค์ฒ๋ฅผ ๋ฌ์์ผ๋ ๊ทธ ์์ ๋ทฐ๋ฅผ ์ธ์๋ก ๋๊ฒจ์ ์ขํ๋ฅผ ์ป๊ฒ ํด์ผ ํฉ๋๋ค.
์ด๊ฒ๋ ํ๋จ ๋ ์ด๋ธ์ ํ์ํด์ ๋ณํ๋ฅผ ํ์ธํ ์ ์๋๋ก ํด๋ณผ๊ฒ์.
@IBAction func panGestureHandler(_ sender: UIPanGestureRecognizer) {
self.statusLabel.text = "\(sender.translation(in: self.view).x.rounded()) \(sender.translation(in: self.view).y.rounded())"
}
์์์ ์ด ๋์ค๋ฉด ๋ณด๊ธฐ๊ฐ ๋๋ฌด ์ด๋ ค์ธ ๊ฒ ๊ฐ์์ translation์ ๊ฒฐ๊ณผ๋ก ๋ฐํ๋ ์ขํ์ X์ Y๋ฅผ ๋ฐ์ฌ๋ฆผํด์ ๋ ์ด๋ธ์ ํ์ํ๋๋ก ํด๋ดค์ต๋๋ค.
์ด๋ ๊ฒ ๋๋๊ทธํ๋ฉด์ ์ด๋ํ ์ขํ๊ฐ ์ฒ์ ๋๋๊ทธ๋ฅผ ์์ํ ์ง์ ์ผ๋ก๋ถํฐ ์ผ๋ง๋ ๋จ์ด์ ธ ์๋์ง ์ ์ ์์ต๋๋ค. ์ฐ๋ฆฌ๋ ์์ ์ด๋์ํค๊ณ ์ ํ๋๊น ์ด ์ขํ๋ฅผ ์ฌ์ฉํด์ ์์ ๊ณ์ ์์ง์ฌ์ฃผ๋ฉด ๋ ๊ฒ ๊ฐ๋ค์. ์ด๊ฑธ ์ฝ๋๋ก ์์ฑํ๋ฉด ์๋์ฒ๋ผ ์์ฑํ ์ ์๊ฒ ์ต๋๋ค!
@IBAction func panGestureHandler(_ sender: UIPanGestureRecognizer) {
let translation = sender.translation(in: self.view)
self.targetCircle.center.x += translation.x
self.targetCircle.center.y += translation.y
}
์คํํด๋ณผ๊น์?
ํฐ์น๋ฅผ ์์ง์ด๋๋๋ก ๋ทฐ๊ฐ ์ด๋ํ ๊ฒ์ ๊ธฐ๋ํ์ง๋ง, ์ค์ ๋ก๋ ๊ทธ๋ ์ง ์์์ต๋๋ค.. ๋ฐ๋ก ์ฝ๋์์ ๋ง์ง๋ง ํ ์ค์ ์์ฑํ์ง ์์๊ธฐ ๋๋ฌธ์ด์ฃ . ์ฐ๋ฆฌ๋ setTranslation() ๋ฉ์๋์ ํธ์ถ์ ๋ง์ง๋ง์ ์ถ๊ฐํด์ฃผ์ด์ผ ํฉ๋๋ค.
@IBAction func panGestureHandler(_ sender: UIPanGestureRecognizer) {
let translation = sender.translation(in: self.view)
self.targetCircle.center.x += translation.x
self.targetCircle.center.y += translation.y
sender.setTranslation(CGPoint.zero, in: targetCircle)
}
์ด๋ ๊ฒ ํ ์ค์ ์ถ๊ฐํ๊ณ ๋ค์ ์คํํด๋ณด๋ฉด!
์ํ๋ ๋๋ก ์ ๋์ํ๋ ๊ฒ์ ํ์ธํ ์ ์์ต๋๋ค! ๊ทธ๋ผ setTranslation์ด ์ด๋ค ์ญํ ์ ํด์ฃผ๋ ๊ฑธ๊น์? ์ ํ ๋ฌธ์์๋ ๋ช ์พํ ํด๋ต์ด ๋์ค์ง ์์์ ๊ตฌ๊ธ๋ง์ ํ๋ ์ค์ ์คํ์ค๋ฒํ๋ก์ฐ์ ์ข์ ๋ต๋ณ์ ๋ฐ๊ฒฌํ์ต๋๋ค.
https://stackoverflow.com/questions/29558622/pan-gesture-why-need-settranslation-to-zero
๋ต๋ณ์ ํต์ฌ์ ์ธ ๋ด์ฉ์ "UIPanGestureRecognizer๋ ๋ทฐ๊ฐ ์ด๋ํ๋ค๋ ๊ฒ์ ์์ง ๋ชปํ๋ค"์ ๋๋ค. ์์์ translation ๋ฉ์๋๊ฐ ๋๋๊ทธ๊ฐ ์ฒ์ ์์๋ ์ง์ ์ผ๋ก๋ถํฐ์ ์ด๋์ขํ๋ฅผ ๋ฐํํ๋ค๊ณ ์ค๋ช ํ์ต๋๋ค. ํ์ง๋ง ์ฐ๋ฆฌ๋ ๋ทฐ๋ฅผ ์ด๋์์ผฐ๊ธฐ ๋๋ฌธ์ ๋๋๊ทธ๊ฐ ์ฒ์ ์์๋ ์ง์ ์ ์ขํ๋ ๋ ์ด์ ๋ทฐ์ ์ขํ๊ฐ ์๋๋๋ค. UIPanGestureRecognizer๋ ์ด ์ฌ์ค์ ์์ง ๋ชปํ๊ณ ๊ณ์ํด์ ํด๋น ์ง์ ์ผ๋ก๋ถํฐ์ ๊ฑฐ๋ฆฌ๋ฅผ ๊ณ์ฐํ๊ธฐ ๋๋ฌธ์ ๋ทฐ๊ฐ ์ด์ํ ์์น๋ก ์ด๋ํ๋ ๊ฒ์ด์์ต๋๋ค.
๋ฐ๋ผ์ ๋ง์ง๋ง์ setTranslation ๋ฉ์๋๋ฅผ ํธ์ถํด์ translation ๋ฉ์๋๊ฐ ๊ณ์ฐํ ๊ธฐ์ค ์ขํ๋ฅผ ํ์ฌ ๋ทฐ์ ์ขํ๋ก ๋ณ๊ฒฝํด์ฃผ๋ ๊ฒ์ผ๋ก ๋๋๊ทธ์ ์์์ ์ ๊ณ์ํด์ ์ ๋ฐ์ดํธํด์ฃผ์ด์ผ ์ฐ๋ฆฌ๊ฐ ์ํ๋ ๋๋๊ทธ์ ๋์์ด ์ํ๋์๋ ๊ฒ์ด์ฃ .
UIPanGestureRecognizer๊ฐ ์๋ ค์ฃผ๋ ์ ๋ณด 2: Velocity
๋ง์ง๋ง์ผ๋ก velocity์ ๋ํด์ ์์๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค! velocity() ๋ฉ์๋๋ฅผ ์คํํ๋ฉด, ๊ทธ ๋ฐํ ๊ฐ์ผ๋ก๋ ์๋๋ฅผ ์ป๊ฒ ๋ฉ๋๋ค. ์ฌ๊ธฐ์ ์๋๋ "points per second"๋ก ํํ๋๋ค๊ณ ๊ณต์๋ฌธ์์ ๋์์๋ค์. ์ด ๋น ์์ง์์ ์๋ฏธํฉ๋๋ค! ์๋ ๊ฐ์ horizontal, vertical๋ก ๋ถ๋ฆฌ๋์ด์ ์ป์ ์ ์๊ณ ์ด๋ฅผ ํํํ๊ธฐ ์ํด x, y ์ขํ๊ณ๋ฅผ ์ฌ์ฉํ๋ CGPoint๋ฅผ ๋ฐํํ๋ ๊ฒ์ด์ฃ !
์ด ๊ฐ์ ์ด์ฉํด์ ์ฐ๋ฆฌ๋ ์ด๋ค ๊ฑธ ์ ์ ์์๊น์? ๋ฐ๋ก ๋ฐฉํฅ์ ๋๋ค! x, y์ ์/์์ ๋ฐ๋ผ ์ฐ๋ฆฌ๋ ๋ฐฉํฅ์ ์ ์ ์๊ฒ ๋ฉ๋๋ค. ๊ทธ๋ผ ํ ์คํธํด๋ณด๊ฒ ์ต๋๋ค!
let velocity = sender.velocity(in: self.targetCircle)
let x = velocity.x
let y = velocity.y
switch (x, y) {
case _ where x == 0.0 && y < 0.0:
self.statusLabel.text = "โฌ๏ธ"
case _ where x > 0.0 && y < 0.0:
self.statusLabel.text = "โ๏ธ"
case _ where x > 0.0 && y == 0.0:
self.statusLabel.text = "โก๏ธ"
case _ where x > 0.0 && y > 0.0:
self.statusLabel.text = "โ๏ธ"
case _ where x == 0.0 && y > 0.0:
self.statusLabel.text = "โฌ๏ธ"
case _ where x < 0.0 && y > 0.0:
self.statusLabel.text = "โ๏ธ"
case _ where x < 0.0 && y == 0.0:
self.statusLabel.text = "โฌ
๏ธ"
case _ where x < 0.0 && y < 0.0:
self.statusLabel.text = "โ๏ธ"
default:
self.statusLabel.text = "๋ฐฉํฅ์?"
}
์ด 8 ๋ฐฉํฅ์ ์ ์ํ์ต๋๋ค. x ์ถ์ ์ค๋ฅธ์ชฝ์ผ๋ก ์ด๋ํ ๋ ์์, ์ผ์ชฝ์ผ๋ก ์ด๋ํ ๋ ์์๊ฐ ๋๊ณ , y์ถ์ ์๋ก ์ด๋ํ ๋ ์์, ์๋๋ก ์ด๋ํ ๋ ์์๊ฐ ๋จ์ ์ด์ฉํ๋ฉด ๋๊ฐ์ ๊น์ง ๋ชจ๋ 8๊ฐ์ ๋ฐฉํฅ์ ์ ์ํ ์ ์์ต๋๋ค.
๋ฐฉํฅ์ด ์ ํ์ธ๋๋ค์~! ์ด๋ ๊ฒ ๋ฐฉํฅ์ ํ์ธํ๋ ์ฉ๋๋ก Velocity๋ฅผ ์ฌ์ฉํ๋ฉด ๋ฉ๋๋ค!
์ ์ฒด ์์ ์ฝ๋
@IBAction func panGestureHandler(_ sender: UIPanGestureRecognizer) {
// ์ํ ํ์ธ
switch sender.state {
case .began:
print("Began")
case .changed:
print("Changed")
case .ended:
print("Ended")
default:
break
}
// ๋ทฐ ์ด๋
let translation = sender.translation(in: self.view)
self.targetCircle.center.x += translation.x
self.targetCircle.center.y += translation.y
// ๋ฐฉํฅ ํ์ธ
let velocity = sender.velocity(in: self.targetCircle)
let x = velocity.x
let y = velocity.y
switch (x, y) {
case _ where x == 0.0 && y < 0.0:
self.statusLabel.text = "โฌ๏ธ"
case _ where x > 0.0 && y < 0.0:
self.statusLabel.text = "โ๏ธ"
case _ where x > 0.0 && y == 0.0:
self.statusLabel.text = "โก๏ธ"
case _ where x > 0.0 && y > 0.0:
self.statusLabel.text = "โ๏ธ"
case _ where x == 0.0 && y > 0.0:
self.statusLabel.text = "โฌ๏ธ"
case _ where x < 0.0 && y > 0.0:
self.statusLabel.text = "โ๏ธ"
case _ where x < 0.0 && y == 0.0:
self.statusLabel.text = "โฌ
๏ธ"
case _ where x < 0.0 && y < 0.0:
self.statusLabel.text = "โ๏ธ"
default:
self.statusLabel.text = "๋ฐฉํฅ์?"
}
sender.setTranslation(CGPoint.zero, in: targetCircle)
}
์ฌ๊ธฐ๊น์ง UIPanGesture์ ๊ธฐ๋ณธ์ ์ธ ์ฌ์ฉ๋ฐฉ๋ฒ๋ค์ ์ ๋ฆฌํด๋ณด์์ต๋๋ค! ์๋ชป๋ ๋ด์ฉ์ด ์๊ฑฐ๋ ๊ต์ ์ด ํ์ํ ๋ด์ฉ์ด ์๋ค๋ฉด ๋๊ธ ์ฃผ์ธ์ ์ค๋๋ ์ฝ์ด์ฃผ์ ์ ๊ฐ์ฌํฉ๋๋ค๐