はじめに
こんな感じの動きが、 UIGestureRecognizer (UISwipeGestureRecognizer) を使用して作れます。Apple WWDC 2011 ビデオの、Advanced Scroll View Techniques [Session 104] で説明されていた情報を元に作りました。素晴らしいセッションが公開されているので、ぜひとも多くの開発者が益を受けて欲しいと思い,ソースを公開したので、良かったらご覧下さい。
WWDCビデオから得られた情報
GitHubのソースはこちら!
tomohisa/SwipeMenu – GitHub
WWDC のビデオはこちら
WWDC 2011 Session Videos – Apple Developer
Advanced Scroll View Techniques Session 104
です。ここでは、
- UIScrollView で無限スクロールする方法
- 上下はスクロールするものの、左右にスクロールしないサブビューの作成方法
- カスタマイズされたタッチ処理
- ズーム後に詳細に再描画する方法
スクロールとスワイプの共用
が取り上げられています。サンプルコードもリンクされていますが、四番目に関するサンプルだけです。今回の
UISwipeGestureRecognizer でスワイプでシュッと出して閉じるメニュー
は、Notification Center や、iPadのメールなどで、画面の端からスワイプすると、隠れていたビューがシュッとでてくるという、機能を実現することが出来ます。個人的には,この動作が気に入っていて、実現したいと思っていたので、今作成中のプロジェクトに導入してみました。アイコンだけだと意味が分かりにくいものも、シュッと出して、メニューの詳細をいつでも確認出来るというのは大きいですね。見栄えを良く維持しつつも、分かりやすさを犠牲にしないものだと思いました。
シュッとスワイプ出来るメニューを作る手順
PDFに詳細がありますが、手順としては、
-
[cc lang=”ObjC”]
- スワイプを検出するGesture Recognizerを作成する
- スワイプの方向を決定する
- スワイプのデリゲートを指定する
- self (この場合は、対象のスクロール)に対してジェスチャーを登録
- ジェスチャーの優先順位を指定
- スワイプのタッチ領域を限定する
- locationInView:
- CGRectContainsPoint(rect, point)
- タッチが認識された時に、アニメーションをする
swipeRight = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handleSwipeRight:)];
swipeRight.direction = UISwipeGestureRecognizerDirectionRight;
swipeRight.delegate = self;
[/cc]
[cc lang=”ObjC”]
[self addGestureRecognizer:swipeRight];
[/cc]
[cc lang=”ObjC”]
[self.panGestureRecognizer requireGestureRecognizerToFail:swipeRight];
[self.innerScrollView.panGestureRecognizer requireGestureRecognizerToFail:swipeRight];
[/cc]
スクロールビューには、スクロールビューないで、パン(移動)するためのジェスチャーを使用しており、これも、UIPanGestureRecognizerを使用しています。プログラムとして、どちらを重要視するかを決定する必要があるので、シュッとスワイプするものが先に判断されるように、[requireGestureRecognizerToFail:] メソッドで、スワイプが失敗するまで、動かさないように指定します。
こうすると、スワイプが有効になるのですが、このままだと、どこからクリックしてもスワイプに反応してしまいます。それで、スワイプは特定の位置だけ反応して、それ以外はスワイプを失敗させて、スクロールにタッチをゆだねる必要があります。
それが以下のコードです。
[cc lang=”ObjC”]
– (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldReceiveTouch:(UITouch *)touch
{
if (gestureRecognizer==swipeRight) {
UIScrollView *scrollView = self;
CGPoint touchPoint = [touch locationInView:scrollView];
if (!CGRectContainsPoint(viewMenu.frame,touchPoint))
return NO;
}
return YES;
}
[/cc]
幸い、CocoaTouchには、
タッチを、ビューの座標に変換する
pointが、rectの正方形ないに入っているか。
などの便利な関数があるので、スワイプエリアに入っているかをかんたんに判別出来ます。
[cc lang=”ObjC”]
-(void) handleSwipeRight:(id)sender {
[UIView animateWithDuration:.1 delay:.2 options:UIViewAnimationTransitionNone animations:^{
if (swipeRight.direction == UISwipeGestureRecognizerDirectionRight) {
viewMenu.frame = CGRectMake(0, self.bounds.size.height – 0 – imageViewMenu.image.size.height, imageViewMenu.image.size.width, imageViewMenu.image.size.height);
}else{
viewMenu.frame = CGRectMake(44 – imageViewMenu.image.size.width, self.bounds.size.height – 0 – imageViewMenu.image.size.height, imageViewMenu.image.size.width, imageViewMenu.image.size.height);
}
} completion:^(BOOL finished) {
if (swipeRight.direction == UISwipeGestureRecognizerDirectionRight) {
swipeRight.direction = UISwipeGestureRecognizerDirectionLeft;
}else{
swipeRight.direction = UISwipeGestureRecognizerDirectionRight;
}
}];
}
[/cc]
これは、かんたんに言うと、
右にしゅっとしたときは、ビューを右に動かして、次に判別するジェスチャーを左向きにして、
左にしゅっとしたときは、ビューを左に動かして、次に判別するジェスチャーを右向きにする。
という感じです。
iOS 4から使える、Blocksによって、コードが非常に簡潔に書けるようになっているのにお気づきでしょうか?わざわざ別関数を作成して、そこに、終了時のコードを書く必要なく、blocks の形で、終了した時にこれを行っといてねという事を先に登録しておくことが出来ます。
以上で、基本的な動きを定義出来ます。このほかにも、指定のページだけ特定のたすきを付けたかったり、縦にだけ動くスクロールと、横にだけ動くスクロールを混ぜ合わせたりするコードも含まれているので、是非ご覧下さい。
ちなみに、このコードは、ARC を有効にしているので、Release、Retainを書いていないのにもご注意ください。ARCに慣れてしまうと、かなりコードを節約出来ますね。
おさらい
このように、iOS3.2から有効になった、UIGestureRecognizerは、非常に強力で、これ無しだと、条件分をたくさん書かなければならないものを、非常にシンプルに記述することが出来ます。Twitterアプリ,Facebookアプリ,標準のメールアプリ、notification centorなどに慣れた、たくさんのユーザは、その快適さに慣れて、いろんなところをシュッとスワイプするに違いありません。その時に、ユーザーの思い通りに動いて、ユーザの思考を妨げない事が、ユーザビリティに繋がるのではないでしょうか?素晴らしいフレームワーク、またビデオなどの情報に感謝するとともに、引き続き新たな技術を取り入れ続ける事が重要だと感じます。