[Swift3対応]8種類のUIGestureRecognizerを使う
こんにちは、Swift3が出て1年前のプロジェクトを開くのが怖い@Yuuです。
Swift→Swift2になった時に自動変換機能を使うのを忘れて、1000個以上のエラーが出たのを思い出してガクブル中です...
(((;゚Д゚)))
さて今回は、様々なジェスチャーを扱えるようにする事が出来る「UIGestureRecognizer」のご紹介。
解説を行う環境は以下のとおりです。
Swift3での紹介ですが、この記事を書いている頃はまだXcode8になったばかりなので、Swift2での書き方も一緒にご紹介していきます。
- OS:macOS Sierra 10.12
- Xcode:8.0
- Swift:3.0
スポンサーリンク
UIGestureRecognizerとは
そもそもジェスチャーて何?という話からすると、
- タップ・ダブルタップ
- ピンチイン・ピンチアウト
- 長押し
- スワイプ
のような、指で画面を操作するアクション全般のことを言います。
画像を長押ししたり、地図やブラウザでピンチイン・ピンチアウトしたり...
皆さんも当たり前のように使っている機能ではないでしょうか。
そして、これらジェスチャー全般を扱えるようにするクラスとして「UIGestureRecognizer」が用意されていて、更にそのサブクラスとして6種類のジェスチャーを扱えるクラスが用意されています。
クラス名 | ジェスチャー |
---|---|
UITapGestureRecognizer | タップ、ダブルタップ |
UIPinchGestureRecognizer | ピンチ |
UIPanGestureRecognizer | パン(ドラッグ) |
UISwipeGestureRecognizer | スワイプ |
UIRotationGestureRecognizer | ローテイト |
UILongPressGestureRecognizer | ロングプレス |
例えば「UITapGestureRecognizer」を使用すると、UIView
やUILabel
のようなそのままではタップを検出出来ないオブジェクトに対して、タップを可能に出来ます。
「UITapGestureRecognizer」については以前詳しく解説しているので、もし興味があれば見てみて下さい。
[swift]ボタン以外もタップ出来るようにするUITapGestureRecognizer
お手軽!storyboardでUITapGestureRecognizerを設定する
この記事では以下8種類(ダブルタップを1つとカウント)のジェスチャーを、swiftで扱えるようにする方法をご紹介します。
クラス名 | ジェスチャー |
---|---|
UITapGestureRecognizer | タップ・ダブルタップ |
UIPinchGestureRecognizer | ピンチ |
UIPanGestureRecognizer | パン(ドラッグ) |
UISwipeGestureRecognizer | スワイプ |
UIRotationGestureRecognizer | ローテイト |
UILongPressGestureRecognizer | ロングプレス |
UIScreenEdgePanGestureRecognizer | エッジ |
1番下のエッジ(Safariなどで画面端をスワイプする事で戻れるアクションの事)だけ特殊で、「UIGestureRecognizer」のサブクラスの「UIPanGestureRecognizer」の更にそのサブクラスの「UIScreenEdgePanGestureRecognizer」を使用します。
ジェスチャーを扱う
ではそれぞれのジェスチャーを扱う方法を見ていきます。
タップ・ダブルタップ(UITapGestureRecognizer)
タップされた時は「single」・ダブルタップの時は「double」とログに出力されるようにします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
override func viewDidLoad() { super.viewDidLoad() // ダブルタップ //let doubleTap = UITapGestureRecognizer(target: self, action: #selector(ViewController.tapDouble(_:))) //Swift2.2以前 let doubleTap = UITapGestureRecognizer(target: self, action: #selector(ViewController.tapDouble(sender:))) //Swift3 doubleTap.numberOfTapsRequired = 2 view.addGestureRecognizer(doubleTap) // シングルタップ //let singleTap = UITapGestureRecognizer(target: self, action: #selector(ViewController.tapSingle(_:))) //Swift2.2以前 let singleTap = UITapGestureRecognizer(target: self, action: #selector(ViewController.tapSingle(sender:))) //Swift3 singleTap.numberOfTapsRequired = 1 //singleTap.numberOfTouchesRequired = 2 //こう書くと2本指じゃないとタップに反応しない //これを書かないとダブルタップ時にもシングルタップのアクションも実行される //singleTap.requireGestureRecognizerToFail(doubleTap) //Swift2.2 singleTap.require(toFail: doubleTap) //Swift3 view.addGestureRecognizer(singleTap) } /// シングルタップ時に実行される func tapSingle(sender: UITapGestureRecognizer) { print("single") } /// ダブルタップ時に実行される func tapDouble(sender: UITapGestureRecognizer) { print("double") } |
注意が必要なのは14行目と18行目。
14行目はコメントアウトしてますが、このコメントアウトを外すと2本指じゃないとタッチが反応しません。
また18行目を設定しないとダブルタップの時でもシングルタップのアクションが実行されてしまいます。
ピンチ(UIPinchGestureRecognizer)
ピンチイン・ピンチアウトをする際に「scale」「velocity」の値を取得する事が出来ます。
- scale・・2つのタッチポイントの相対的な拡大縮小率
- velocity・・1秒あたりのピンチの速度
scaleの「タッチポイントの相対的な拡大縮小率」てなんじゃそりゃ?と思ったのですが、ピンチが検知された瞬間の指と指の距離を1とした拡大縮小率を取得するという事のようです。
以下のページが参考になりました。
【Swift】Pinch Gesture Recognizerの使い方。2つの指で部品を拡大縮小する。
ピンチイン・ピンチアウトを取得する方法は以下のようになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
import UIKit class PinchCodeViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() // ピンチを定義 //let pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(PinchCodeViewController.pinchView(_:))) //Swift2.2以前 let pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(PinchCodeViewController.pinchView(sender:))) //Swift3 // viewにピンチを登録 self.view.addGestureRecognizer(pinchGesture) } /// ピンチイン・ピンチアウト時に実行される func pinchView(sender: UIPinchGestureRecognizer) { print("pinch") // ピンチイン・ピンチアウトの拡大縮小率 print("scale: \(sender.scale)") // 1秒あたりのピンチの速度(read-only) print("velocity: \(sender.velocity)") } } |
この「UIPinchGestureRecognizer」を使うと、よくある写真をピンチイン・ピンチアウトして拡大縮小表示するような事が出来ます。
以下のページが参考になりそう。
パン(UIPanGestureRecognizer)
パンはドラックの事で、translation(in:)
を使う事で移動後の相対位置を取得出来ます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
override func viewDidLoad() { super.viewDidLoad() // パンを定義 //let panGesture = UIPanGestureRecognizer(target: self, action: #selector(PanCodeViewController.panView(_:))) //Swift2.2以前 let panGesture = UIPanGestureRecognizer(target: self, action: #selector(PanCodeViewController.panView(sender:))) //Swift3 // viewにパンを登録 self.view.addGestureRecognizer(panGesture) } /// パン時に実行される func panView(sender: UIPanGestureRecognizer) { print("パン") //移動後の相対位置を取得 //let location: CGPoint = sender.translationInView(self.view) //Swift2.2以前 let location: CGPoint = sender.translation(in: self.view) //Swift3 print(location) } |
スワイプ(UISwipeGestureRecognizer)
スワイプはdirection
でスワイプの方向を設定します。
また、タップの時のようにnumberOfTouchesRequired
で何本指以上の場合にスワイプに反応するかも定義出来ます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
override func viewDidLoad() { super.viewDidLoad() // スワイプを定義 //let leftSwipe = UISwipeGestureRecognizer(target: self, action: #selector(SwipeCodeViewController.leftSwipeView(_:))) //Swit2.2以前 let leftSwipe = UISwipeGestureRecognizer(target: self, action: #selector(SwipeCodeViewController.leftSwipeView(sender:))) //Swift3 // レフトスワイプのみ反応するようにする leftSwipe.direction = .left // viewにジェスチャーを登録 self.view.addGestureRecognizer(leftSwipe) // スワイプを定義 //let rightSwipe = UISwipeGestureRecognizer(target: self, action: #selector(SwipeCodeViewController.rightSwipeView(_:))) //Swit2.2以前 let rightSwipe = UISwipeGestureRecognizer(target: self, action: #selector(SwipeCodeViewController.rightSwipeView(sender:))) //Swift3 // ライトスワイプのみ反応するようにする rightSwipe.direction = .right // viewにジェスチャーを登録 self.view.addGestureRecognizer(rightSwipe) } /// レフトスワイプ時に実行される func leftSwipeView(sender: UISwipeGestureRecognizer) { print("left Swipe") } /// ライトスワイプ時に実行される func rightSwipeView(sender: UISwipeGestureRecognizer) { print("right Swipe") } |
ローテイト(UIRotationGestureRecognizer)
ローテイトは2本指で回転させた際のアクションです。
ローテイトをする際に「rotation」「velocity」の値を取得する事が出来ます。
- rotation・・回転した角度(ラジアン)
- velocity・・スピード(ラジアン/秒)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
import UIKit class RotateCodeViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() // ローテイトを定義 //let rotationGesture = UIRotationGestureRecognizer(target: self, action: #selector(RotateCodeViewController.rotationView(_:))) //Swift2.2以前 let rotationGesture = UIRotationGestureRecognizer(target: self, action: #selector(RotateCodeViewController.rotationView(sender:))) //Siwft3 // ローテイトをviewに登録 self.view.addGestureRecognizer(rotationGesture) } /// ローテイト時に実行される func rotationView(sender: UIRotationGestureRecognizer) { print("rotation: \(sender.rotation)") print("velocity: \(sender.velocity)") } } |
ロングプレス(UILongPressGestureRecognizer)
ロングプレスは長押しの事。
そのまんまですね。
minimumPressDuration
で最低何秒押された場合ロングプレスとするかを、
allowableMovement
でロングプレス中の指が動いていい範囲をpxで指定する事が出来ます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
import UIKit class LongPressCodeViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() // ロングプレスを定義 //let longPressGesture = UILongPressGestureRecognizer(target: self, action: #selector(LongPressCodeViewController.longPressView(_:))) //Swift2.2以前 let longPressGesture = UILongPressGestureRecognizer(target: self, action: #selector(LongPressCodeViewController.longPressView(sender:))) //Swift3 longPressGesture.minimumPressDuration = 3 //3秒間以上押された場合にロングプレスとする longPressGesture.allowableMovement = 30 //ロングプレスを判定する指が動いていい範囲、単位はpx self.view.addGestureRecognizer(longPressGesture) } /// ロングプレス時に実行される func longPressView(sender: UILongPressGestureRecognizer) { print("Long Press") } } |
エッジ(UIScreenEdgePanGestureRecognizer)
エッジは画面端をスワイプするアクション。
ブラウザなどで画面左端をスワイプして前のページに戻ったり、画面下からスワイプして通知センターを表示する際にもこのアクションが使われています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
import UIKit class EdgeCodeViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() // エッジを定義 //let edgeGesture = UIScreenEdgePanGestureRecognizer(target: self, action: #selector(EdgeCodeViewController.edgeView(_:))) //Swift2.2 let edgeGesture = UIScreenEdgePanGestureRecognizer(target: self, action: #selector(EdgeCodeViewController.edgeView(sender:))) //Swift3 edgeGesture.edges = .left //左端をスワイプするのを検知する // viewにエッジを登録 self.view.addGestureRecognizer(edgeGesture) } /// エッジ時に実行される func edgeView(sender: UIScreenEdgePanGestureRecognizer) { print("edge") } } |
あとがき
UIGestureRecognizerのサブクラス8種類の使い方をザックリ書いてみました。
UIGestureRecognizerは何かと使う場面があるので、参考になれば幸いです。
次はstoryboardを使ってUIGestureRecognizerを使用出来るようにする記事を書こうと思っているので、storyboardを使ってコーディングされる人はぜひ読んでみて下さい!
ではでは
今回はこの辺で!ヽ(•̀ω•́ )ゝ✧
Swiftでシングルタップとダブルタップとロングプレス(ホールド)に同時に対応する - 酢ろぐ!
2016.11.13追記
UIGestureRecognizerが使えない起きた時の対処法の記事を書きました。
エラーが起きた際には参考にしてみて下さい。