表題の通り、Swift 3 で iPhone 等のシェイクモーションを処理する方法です。
なんか、書き方が微妙に変わったので、とりあえず覚え書き。
特に前置きすることもないので、さくっと本編をどうぞ。
読み込み中です。少々お待ち下さい
Swift 2 時代のシェイク処理
Swift 2 時代には、例えば以下のようなサンプルコードを良く見かけました。
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
self.becomeFirstResponder()
}
override func canBecomeFirstResponder() -> Bool {
return true
}
override func motionBegan(motion: UIEventSubtype, withEvent event: UIEvent?) {
if event?.type == UIEventType.Motion && event?.subtype == UIEventSubtype.MotionShake {
print("shakeBegan")
}
}
override func motionEnded(motion: UIEventSubtype, withEvent event: UIEvent?) {
if event?.type == UIEventType.Motion && event?.subtype == UIEventSubtype.MotionShake {
print("shakeEnded")
}
}
override func motionCancelled(motion: UIEventSubtype, withEvent event: UIEvent?) {
if event?.type == UIEventType.Motion && event?.subtype == UIEventSubtype.MotionShake {
print("shakeCancelled")
}
}
これを踏まえた上で、Swift 3 での書き方を見ていきましょう。
Swift 3 での変更点
まず、canBecomeFirstResponder が func ではなく var になりました。
open class UIResponder : NSObject, UIResponderStandardEditActions {
open var next: UIResponder? { get }
open var canBecomeFirstResponder: Bool { get } // default is NO
// Swift 3 での UIResponder の抜粋。以下省略
しかも、ReadOnly かつ「default is NO」です。
さてと思って試したところ、他にモーションを処理するレスポンダが特に存在しなければ、上書きせずに無視しても動作するようです。その場合「becomeFirstResponder」を呼ぶ必要はありません(というか、呼ぶ意味がない)。
とはいえ、利用する外部ライブラリ等やコードの組み方によっては想定外の動きをすることも考えられますので、必ずそこで処理を行いたい場合は、ちゃんと true を返すように上書きして、適切なタイミングでファーストレスポンダに指定しましょう。
// Swift 2
override func canBecomeFirstResponder() -> Bool { return true }
// Swift 3 ↓
override var canBecomeFirstResponder: Bool { get { return true } }
続いて、Swift 3 では書き方が赤字部分のように微妙に変わります。
// Swift 2
override func motionBegan(motion: UIEventSubtype, withEvent event: UIEvent?)
if event?.type == UIEventType.Motion && event?.subtype == UIEventSubtype.MotionShake {
// Swift 3 ↓
override func motionBegan(_ motion: UIEventSubtype, with event: UIEvent?) {
if event?.type == UIEventType.motion && event?.subtype == UIEventSubtype.motionShake {
いや、うん、まぁ......なんでもないです。
Swift 3 のサンプルコード
Swift 3 での書き方をまとめると、こんな感じ。
新しい Project で Single View Application 等を選択し(種類はなんでもいいです)、ViewController.swift 等に以下のコードを貼り付ければ、そのまま動作します。
override var canBecomeFirstResponder: Bool { get { return true } }
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.becomeFirstResponder()
}
override func motionBegan(_ motion: UIEventSubtype, with event: UIEvent?) {
print("motionBegan - subtype: \(motion.rawValue) with \(event)")
if motion == UIEventSubtype.motionShake {
print(" SHAKE!!")
}
}
override func motionEnded(_ motion: UIEventSubtype, with event: UIEvent?) {
print("motionEnded - subtype: \(motion.rawValue) with \(event)")
if motion == UIEventSubtype.motionShake {
print(" SHAKE!!!")
}
}
override func motionCancelled(_ motion: UIEventSubtype, with event: UIEvent?) {
print("motionCancelled - subtype: \(motion.rawValue) with \(event)")
if motion == UIEventSubtype.motionShake {
print(" SHAKE...")
}
}
motionBegan - subtype: 1 with Optional(<UIMotionEvent: 0x17416b340> timestamp: 0 subtype: 1)
SHAKE!!
motionEnded - subtype: 1 with Optional(<UIMotionEvent: 0x17416b340> timestamp: 0 subtype: 1)
SHAKE!!!
motionBegan - subtype: 1 with Optional(<UIMotionEvent: 0x17416b340> timestamp: 0 subtype: 1)
SHAKE!!
motionCancelled - subtype: 1 with Optional(<UIMotionEvent: 0x17416b340> timestamp: 0 subtype: 1)
SHAKE...
iPhone 等のデバイスを任意の一方向に素早く振ると、まず motionBegan が呼ばれ、あまり間を置かずに続けて逆方向に振ると motionEnded が、振らずに停止してしばらく待つと motionCancelled が呼ばれます(精度は普段利用してる感じからお察し)。
ですので、通常は motionEnded でシェイクモーションを処理すれば良いでしょう。
上で挙げた Swift 2 のサンプルコードと、あえて少々異なる書き方をしているのは、そもそも motion 系に渡される UIEvent は UIMotionEvent であろうことから、わざわざ event.subtype を使わずに motion だけで判別した方がすっきりするからです。
「func の名称が motion なっとっても、UIEvent がモーションイベントとは限らんやんけ」と尤もな心配が拭いきれない方は、Swift 2 のサンプルコードと同じように「event?.type == UIEventType.motion」も判別すると良いと思います。
(というか、なんでこれ press イベントみたいに「func motionXXX(_ motion: UIEventSubtype, with event: UIMotionEvent?)」にしなかったんでしょうね?)
おわりに
シェイクモーションイベントについてのドキュメントは、とりあえずこの辺りをどうぞ(コードが Objective-C ですが)。
- Detecting Shake-Motion Events with UIEvent
(日本語はこちら。情報が最新ではない可能性があります)