これが私がたどり着いたAPIです。これには 3 つのコンポーネントがあります。別のビュー コントローラーへの遷移を作成する通常のビュー コントローラー、カスタム コンテナー ビュー コントローラー、および遷移クラスです。遷移クラスは次のようになります。
@interface TZInteractiveTransition : NSObject
@property(strong) UIView *fromView;
@property(strong) UIView *toView;
// Usually 0–1 where 0 = just fromView visible and 1 = just toView visible
@property(assign, nonatomic) CGFloat phase;
// YES when the transition is taken far enough to perform the controller switch
@property(assign, readonly, getter = isCommitted) BOOL committed;
- (void) prepareToRun;
- (void) cleanup;
@end
この抽象クラスから、プッシュ、回転などの具体的なトランジションを派生させます。ほとんどの作業はコンテナー コントローラーで行われます (少し単純化されています)。
@interface TZTransitionController : UIViewController
@property(strong, readonly) TZInteractiveTransition *transition;
- (void) startPushingViewController: (TZViewController*) controller withTransition: (TZInteractiveTransition*) transition;
- (void) startPoppingViewControllerWithTransition: (TZInteractiveTransition*) transition;
// This method finishes the transition either to phase = 1 (if committed),
// or to 0 (if cancelled). I use my own helper animation class to step
// through the phase values with a nice easing curve.
- (void) endTransitionWithCompletion: (dispatch_block_t) completion;
@end
もう少し明確にするために、これが移行の開始方法です。
- (void) startPushingViewController: (TZViewController*) controller withTransition: (TZInteractiveTransition*) transition
{
NSParameterAssert(controller != nil);
NSParameterAssert([controller parentViewController] == nil);
// 1. Add the new controller as a child using the containment API.
// 2. Add the new controller’s view to [self view].
// 3. Setup the transition:
[self setTransition:transition];
[_transition setFromView:[_currentViewController view]];
[_transition setToView:[controller view]];
[_transition prepareToRun];
[_transition setPhase:0];
}
これTZViewController
は、遷移コントローラーへのポインターを保持する単純なUIViewController
サブクラスです (プロパティと非常によく似ていnavigationController
ます)。に似たカスタム ジェスチャ レコグナイザーを使用しUIPanGestureRecognizer
て遷移を駆動します。ビュー コントローラーのジェスチャ コールバック コードは次のようになります。
- (void) handleForwardPanGesture: (TZPanGestureRecognizer*) gesture
{
TZTransitionController *transitionController = [self transitionController];
switch ([gesture state]) {
case UIGestureRecognizerStateBegan:
[transitionController
startPushingViewController:/* build next view controller */
withTransition:[TZCarouselTransition fromRight]];
break;
case UIGestureRecognizerStateChanged: {
CGPoint translation = [gesture translationInView:[self view]];
CGFloat phase = fabsf(translation.x)/CGRectGetWidth([[self view] bounds]);
[[transitionController transition] setPhase:phase];
break;
}
case UIGestureRecognizerStateEnded: {
[transitionController endTransitionWithCompletion:NULL];
break;
}
default:
break;
}
}
結果に満足しています。かなり単純で、ハックを使用せず、新しいトランジションで簡単に拡張でき、View Controller のコードは適度に短くシンプルです。私の唯一の不満は、カスタム コンテナー コントローラーを使用する必要があることです。そのため、それが標準のコンテナーとモーダル コントローラーでどのように機能するかわかりません。