iPhone6S, 6S Plusで新しく追加された3D Touchの機能を利用するために class ForceTouchGestureRecognizer: UIGestureRecognizer
を作りました。
そこで、
- 3D Touchについて
- UIGestureRecognizerのサブクラスの作り方
の2つを紹介します。
作ったやつ
https://github.com/milkit/ForceTouchGesture
公式ドキュメント Adopting 3D Touch on iPhone
3D Touch API概要
3D Touchとはスクリーンを押した強さを認識してくれる新しいiPhoneの機能です。公式ドキュメントによると以下の4つに分類されます。
- HomeScreen quick action
- UIKit peek and pop API
- Web view peek and pop API
- UITouch force properties
今回は4の紹介です。
UITouch force properties
UITouchクラスに今回追加されたのは2つのプロパティです。
var force: CGFloat { get }
var maximumPossibleForce: CGFloat { get }
名前のままで、force
でスクリーンを押した強さ、maximumPossibleForce
でその最大許容値を取得できます。
また、3D Touchが有効かどうかを取得するAPIも公開されています。
3D Touchはユーザー自身で設定からオフにすることも可能なので、端末から識別できません。
UITraitCollectionクラスの
var forceTouchCapability: UIForceTouchCapability { get }
で判別することができます。以下のように定義されています。
enum UIForceTouchCapability : Int {
case Unknown
case Unavailable
case Available
}
traitCollection: UITraitCollection
というプロパティがUIViewController, UIView, UIWindow, UIScreenなどにあります。正確に言うと、UITraitEnvironmentプロトコルを採用しているクラスはプロパティを持っています。
ここで注意点としては、 例えば、UIViewのtraitCollection
から判別するときに、そのUIViewのインスタンスがビュー階層に追加されていないと、Unknown
が返ってきます。 ここ要注意です。
UIGestureRecognizerのサブクラスの作り方
UIGestureRecognizerのサブクラスを作るときはフレームワークをimportする必要があります。Swiftだと以下です。
import UIKit.UIGestureRecognizerSubclass
そして、サブクラスを作っていきます。
public class ForceTouchGestureRecognizer: UIGestureRecognizer {
public private(set) var force: CGFloat = 0
public private(set) var forceTouchAvailable = false
public var minimumForce: CGFloat = 3.0
// 省略
}
そして以下4つのメソッドをオーバーライドしてカスタマイズしていきます。
public func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent)
public func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent)
public func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent)
public func touchesCancelled(touches: Set<UITouch>, withEvent event: UIEvent)
例えば、touchesBeganではジェスチャーを認識したいときは, stateを.Beganにして、それ以外を.Failedにします。
override public func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent) {
super.touchesBegan(touches, withEvent: event)
guard let touch = touches.first where self.forceTouchAvailable && touches.count == 1 else {
self.state = .Failed
return
}
if #available(iOS 9, *) {
self.minimumForce = min(self.minimumForce, touch.maximumPossibleForce)
self.force = touch.force
self.state = .Began
} else {
self.state = .Failed
}
}
また例えば、touchesMovedではminimumForce
に設定した値を超えた場合はstateを.Endedにしてジェスチャーを終了しています。
override public func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent) {
super.touchesMoved(touches, withEvent: event)
if self.state == .Failed {
return
}
if #available(iOS 9.0, *) {
self.force = touches.first!.force
if self.force > self.minimumForce {
self.state = .Ended
self.vibrateIfNeeded()
}
}
}
おまけ
iPhone6Sの3D Touchのときに3D Touch専用のバイブレーションがあって、すごく気持ちが良かったので、バイブレーションの機能をつけたいなと思って探しました。しかし、通知時とかの普通のバイブレーションしかなかったです。残念。以下実装方法の紹介です。
import AudioToolbox
// バイブレーションしたいタイミングで
AudioServicesPlayAlertSound(kSystemSoundID_Vibrate)
これだけです。