37
34

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2015-04-25

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
通常タッチと強タッチで挙動を区別できている

37
34
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
37
34

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?