Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
79
Help us understand the problem. What is going on with this article?
@paming

UICollectionViewをジェスチャーで拡大縮小したい(iOS7〜)

More than 5 years have passed since last update.

iOS7から、UICollectionViewのレイアウトを「アニメーションしながら」変更するために、以下の2種類のAPI群が追加されています。

  • タップなどでアニメーションする場合
    • 途中のコントロールが効かない代わりに簡単に使える
    • setCollectionViewLayout:animated:(iOS6から)
    • setCollectionViewLayout:animated:completion:(iOS7から)
  • ジェスチャーなどでアニメーションする場合
    • 途中のコントロールが可能で、「つまんで拡大縮小」といった事が出来る
    • startInteractiveTransitionToCollectionViewLayout:completion: (iOS7から)
    • finishInteractiveTransition(iOS7から)
    • cancelInteractiveTransition(iOS7から)

今回、ピンチジェスチャーでUICollectionViewを拡大縮小したかったので、startInteractiveTransitionToCollectionViewLayout:completion:なAPIを使って実装しました。

サンプルコード

Github

video

処理の流れ

UICollectionViewのinteractiveTransitionとジェスチャーを組み合わせ、以下のような流れで処理が進みます。

  • ジェスチャーの開始
    • 次に表示するUICollectionViewLayoutオブジェクトを生成
    • startInteractiveTransitionToCollectionViewLayout:completion:でTransitionを開始
  • ジェスチャーの状態が変更
    • PinchGestureの場合scaleの値がとれるので、これを元に0〜1の進捗(Progress)を計算し、UICollectionViewTransitionLayoutのtransitionProgressにセットする
    • このprogressに応じて、元のレイアウトと新しいレイアウトの間の状態が自動で計算され、表示される
  • ジェスチャーが終了
    • progressの値に応じて、finishInteractiveTransitionもしくはcancelInteractiveTransitionを呼び出す
    • ジェスチャーを一時停止する(B)
  • (隙間時間が発生)(A)
  • アニメーションの完了
    • start時にしていたcompletionブロックが呼び出される
    • ジェスチャーを再開する(B)

ここで注意するのは、ジェスチャーの終了からアニメーションの完了までに隙間時間(A)がある事です。progressが0.6等の半端な場合にfinishInteractiveTransitioncancelInteractiveTransitionを呼び出すと、progress1.0になるまでアニメーションして(体感1秒程度かかるときがある)、その後にcompletionブロックが呼び出されます。
この隙間時間に次のtransitionを開始しようとするとクラッシュします。
次のTransitionが始まったら、隙間時間をすっ飛ばして次に行きたい所ですが、自分にはそのやり方がわからなかったので、ジェスチャーを一時停止する事にしました(B)。

ソースコードの抜粋

ジェスチャー部分だけ抜粋します。 色々なコードを逃がしているので、全体像はGithubを見てください。

#define kPAMProgressThreshold 0.5
-(void)pinchAction:(UIPinchGestureRecognizer *)gesture
{
    switch(gesture.state){
        case UIGestureRecognizerStateBegan:
        {
            NSLog(@"begin scale=%f",gesture.scale);
            // 拡大なのか縮小なのかを記録しておく。
            // これをしておかないと、拡大で開始して途中で縮小したりするとおかしな事になる。
            self.zoomingStatus = gesture.pam_zoomStatus;

            // Transitionする先のLayoutを用意
            NSUInteger nextItemCount = [self nextHoraizontalItemCount];
            UICollectionViewLayout *nextLayout=[UICollectionViewFlowLayout layoutWithHorizontalItemCount:nextItemCount];

            // Transitionの開始
            [self.collectionView startInteractiveTransitionToCollectionViewLayout:nextLayout
                                                                       completion:^(BOOL completed, BOOL finish) {
                                                                           NSLog( @"completion");
                                                                           // Transitionの完了時にジェスチャーを止めるので、ここで再開
                                                                           [self enableGesture];
                                                                       }];
        }
            break;
        case UIGestureRecognizerStateChanged:
            // Transitionの進捗(progress)を0.0〜1.0で更新
            self.transitionLayout.transitionProgress = [gesture pam_transitionProgressWithZoomStatus:self.zoomingStatus];
            NSLog( @"transitionProgress=%f",self.transitionLayout.transitionProgress);
            break;
        case UIGestureRecognizerStateEnded:
            // Transitionを完了
            if( self.transitionLayout.transitionProgress > kPAMProgressThreshold ){
                [self.collectionView finishInteractiveTransition];
                self.currentHoraizontalItemCount = [self nextHoraizontalItemCount];
            }else{
                [self.collectionView cancelInteractiveTransition];
            }
            // 最後のアニメーションが終わるまでジェスチャーを停止
            [self disableGesture];
            break;
        default:
            break;
    }
}

他のアニメーション

今回は1つのUICollectionViewControllerの中でレイアウトを変更しましたが、UIViewController間でのアニメーションも可能なようです。WWDC 2014のビデオ(要ログイン)の中の「Custom Transitions Using View Controllers」にいろいろ紹介されています。なかなかややこしそうですが、やれる事が増えそうです。

79
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
paming
Android修行中

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
79
Help us understand the problem. What is going on with this article?