iPhone で Force Touch(感圧タッチ)を実装してみた

  • 34
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

Capture 2015-04-26 00.00.30.png

はじめに

この記事は 3D Touch が発表される前に執筆したものです。iOS 9 ならびに iPhone 6s の 3D Touch とは異なる内容になります。実際に実装するのであれば 3D Touch を採用することをおすすめします。

3D Touch 予習

Force Touch の時代?

感圧タッチ搭載の MacBook や Apple Watch の登場で、タッチ UI に圧力の概念が生まれ、そしていよいよタッチパネルの弱点である「触覚フィードバックの不足」を克服しつつあるようです。実際に新しい MacBook のトラックパッドに触れてみるとその完成度に驚きます。これは次期 iPhone にも期待せざるを得ません。

アップル、次期 iPhone にも感圧タッチを採用?Apple Watchや新MacBookのForce Touch応用(WSJ報道)
http://japanese.engadget.com/2015/03/11/iphone-apple-watch-macbook-force-touch-wsj/

このような記事は妄想の域を出ませんが、現実味がありそうです。 → 3D Touch が登場しました!

その一方で iOS 8には面白そうな機能がしれっと追加されていました。

iOS8からUITouchにタッチ半径を測定するプロパティがある
http://qiita.com/yimajo/items/ce6ae6c2159deb69a781

iOS 8からは画面にタッチしている指の接地領域(大雑把な半径)が取れるようになりました。これをうまく使えば、現状の iPhone であっても Force Touch のようなことは実現できそうです。さすがに触覚フィードバックまではハードウェア(Taptic Engine)が足りないので無理ですが。

UITouch

キモとなるのは2つのプロパティです。

  • majorRadius
  • majorRadiusTolerance

majorRadius が指の接地領域を円と見立てた時の半径、majorRadiusTolerance がデバイスに依存する誤差(?)だそうです。単位は point です。
majorRadius に majorRadiusTolerance を足すと最大半径、引くと最小半径となるようです。ですが、この誤差値を具体的にどう活用すれば良いのか、リファレンスを読んでもよくわかりませんでした。とにかくこのような値が取れることは確かです。

CGFloat majorRadius = touch.majorRadius;
CGFloat tolerance = touch.majorRadiusTolerance;
CGFloat maxRadius = majorRadius + tolerance;
CGFloat minRadius = majorRadius - tolerance;

majorRadius は段階的にしか取れない

実は落とし穴があって、この majorRadius、段階的な値しか取れません。UIControlEventTouchDragInside でログに出してみるとこのようになります。

00:34:30.323 majorRadius: 25.647583, majorRadiusTorelance: 6.411896
00:34:30.689 majorRadius: 38.471375, majorRadiusTorelance: 6.411896
00:34:30.705 majorRadius: 38.471375, majorRadiusTorelance: 6.411896
00:34:31.056 majorRadius: 38.471375, majorRadiusTorelance: 6.411896
00:34:31.072 majorRadius: 38.471375, majorRadiusTorelance: 6.411896
00:34:31.089 majorRadius: 38.471375, majorRadiusTorelance: 6.411896
00:34:31.106 majorRadius: 51.305344, majorRadiusTorelance: 6.411896
00:34:31.122 majorRadius: 51.305344, majorRadiusTorelance: 6.411896
00:34:31.139 majorRadius: 51.305344, majorRadiusTorelance: 6.411896
00:34:31.156 majorRadius: 51.305344, majorRadiusTorelance: 6.411896
00:34:31.422 majorRadius: 51.305344, majorRadiusTorelance: 6.411896
00:34:31.439 majorRadius: 64.129135, majorRadiusTorelance: 6.411896
00:34:31.456 majorRadius: 64.129135, majorRadiusTorelance: 6.411896
00:34:31.472 majorRadius: 64.129135, majorRadiusTorelance: 6.411896
00:34:31.489 majorRadius: 64.129135, majorRadiusTorelance: 6.411896
00:34:31.723 majorRadius: 64.129135, majorRadiusTorelance: 6.411896
00:34:31.739 majorRadius: 76.952927, majorRadiusTorelance: 6.411896
00:34:31.756 majorRadius: 76.952927, majorRadiusTorelance: 6.411896
00:34:31.773 majorRadius: 76.952927, majorRadiusTorelance: 6.411896
00:34:31.789 majorRadius: 76.952927, majorRadiusTorelance: 6.411896
00:34:31.806 majorRadius: 76.952927, majorRadiusTorelance: 6.411896

majorRadius はもっと連続的に取れるものだと思っていたのですが、これはもうどうしようもないので、「こういうものだ」と受け入れるしかありません。

Force Touch 判定処理

新しい MacBook のトラックパッドには感圧センサーが内蔵されており、それによって細かな圧力検知を行っています。オレオレ Force Touch では majorRadius によって指の接地領域の大きさではかるアプローチで攻めてみます。

さて、UITouch から majorRadius が取れれば、あとは適当な閾値を設定して、その前後でアクションを分岐してやれば擬似的な Force Touch が実現できそうです。UIControlEventTouchDragInside に設定したアクションに判定文を仕込んでみます。

Swifter な方は適宜読み替えてください。

// タッチダウン
[self addTarget:self action:@selector(touchDown:withEvent:) forControlEvents:UIControlEventTouchDown];
// タッチダウンからのボタン領域内のドラッグ中
[self addTarget:self action:@selector(touchDown:withEvent:) forControlEvents:UIControlEventTouchDragInside];
- (void)touchDown:(UIControl*)sender withEvent:(UIEvent*)event
{
    UITouch *touch = [[event allTouches] anyObject];
    CGFloat majorRadius = touch.majorRadius;

    if (majorRadius >= 45.0) {
        // 強タッチ
    }
    else {
        // 通常タッチ
    }
}

ここでは閾値を45.0としましたが、これは私のiPhone 6と人差し指で検証して感覚で決め打ちしたに過ぎません。これが親指だと majorRadius が違ってくるはずなので、本来なら指の判定も入れたいところですが、残念ながら現状の UIKit の公開されている API では無理そうです。

応用例

これを UIButton に実装すると、例えば強く押し込んだらボタンのラベルを表示する、バルーンヘルプを表示する、といったことが可能になります。タッチ UI の可能性が広がりますね。

また、majorRadius を円の半径として、Material Design の波紋効果のような演出にも応用できます。アニメーション gif をかなり圧縮する必要があったため、デモとしても伝わりやすくなったかと思います。

forcetouch.gif
通常タッチと強タッチで挙動を区別できている