-
Notifications
You must be signed in to change notification settings - Fork 810
Custom Transitions Guide
The custom transition API in ECSlidingViewController is inspired by the custom transition API introduced to UIKitin iOS 7. In fact, ECSlidingViewController uses some of the same protocols. There are a few slight differences, but the basic principles are the same. This guide covers how custom transitions work in ECSlidingViewController.
A transition (custom or not) begins with a call to one of the following transitioning methods:
anchorTopViewToRightAnimated:anchorTopViewToRightAnimated:onComplete:anchorTopViewToLeftAnimated:anchorTopViewToLeftAnimated:onComplete:resetTopViewAnimated:resetTopViewAnimated:onComplete
Before the transition occurs, these methods will first check the sliding view controller's delegate for objects to handle the animation or interaction. Depending on what is returned from the delegate, the resulting three types of transitions can occur: default, custom animation, or custom animation with a custom interaction.
The default transition occurs when the delegate method slidingViewController:animationControllerForOperation:topViewController is not implemented or returns nil.
The transition uses ECSlidingAnimationController for the default animation controller and ECSlidingInteractiveTransition for the default interaction controller.
A custom animation transition occurs when the delegate method slidingViewController:animationControllerForOperation:topViewController returns an animation controller and slidingViewController:interactionControllerForAnimationController: returns nil.
The returned animation controller's animateTransition: method is then called to run the transition.
This type of custom transition can still use the default interaction by making use of the sliding view controller's panGesture.
A custom animation transition occurs when the delegate method slidingViewController:animationControllerForOperation:topViewController returns an animation controller and slidingViewController:interactionControllerForAnimationController: returns an interaction controller.
The returned interaction controller's startInteractiveTransition: is called to run the transition.
A couple things to note here:
- Both delegate methods must return an object for this type of transition to occur. It is possible to return an instance of
ECSlidingAnimationControllerfor the animation controller if you wanted to use the default animation with a custom interaction. - The animation controller's
animateTransition:method is NOT called. It is up to the interaction controller to do so if it is needed. - It may be useful to conditionally return
nilfor the interaction controller. This will cause the transition to callanimateTransition:on the animation controller.
The animation controller returned from slidingViewController:animationControllerForOperation:topViewController is expected to conform to this protocol. It contains these two methods:
- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext;
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext;transitionDuration: is straight forward and is expected to return the duration of the animation. You are expected to follow a couple rules when implementing animateTransition:. Note that this is the same protocol that UIKit uses from custom transition so the same rules apply here.
- You are responsible for adding subviews to the container view.
- The passed in
transitionContextobject must receive a call tocompleteTransition:when the animation is done. - The start and end frames for the views involved in the transition must obey the frames returned from the
transitionContext. If your animation requires a special starting/ending frame, then you have to customize the layout (see Customization in the Layout Guide). - For custom animation transitions that support any kind of interaction, the
transitionContext'stransitionWasCancelledshould be taken into account. Cancelled transitions must set the frames back to the initial frames as reported by thetransitionContext. Otherwise, the frames should be set to the final frames. - If
finalFrameForViewController:returnsCGRectZero, then the view should be removed at the end of the transition. - If
initialFrameForViewController:returnsCGRectZero, then the view should be added at the beginning of the transition. - Clean up after yourself. Be sure to remove any temporary views or snapshots that are no longer needed.
The start and end states are well defined by the transitionContext, you are free to do whatever in between as long as the rules above are followed.
The interaction controller returned from slidingViewController:interactionControllerForAnimationController: is expected to conform to this protocol. You are required to implement this method:
- (void)startInteractiveTransition:(id <UIViewControllerContextTransitioning>)transitionContext;This is the same protocol that UIKit uses for custom transitions, so the same rules apply for implementing this method.
- The
transitionContextshould be continuously updated with the percentage of completion for the interaction by callingupdateInteractiveTransition:. - The
transitionContextmust be notified if the interaction was cancelled or finished by callingcancelInteractiveTransitionorfinishInteractiveTransition. - The rules for
animateTransition:apply here as well.
A percent driven interaction controller can control the animation controller's timeline. 0% represents the start of the animation and 100% represents the end of the animation.
Percent driven interaction controllers are implemented by subclassing ECPercentDrivenInteractiveTransition. This class conforms to the UIViewControllerInteractiveTransitioning protocol. Your subclass is expected to follow the rules, but instead of calling the transitionContext directly, you are to call the convenience methods from the superclass.
Note: Even though UIPercentDrivenInteractiveTransition conforms to the UIViewControllerInteractiveTransitioning protocol, it does not work because it calls methods on the transitionContext that don't exist in the UIViewControllerContextTransitioning protocol. Therefore, it is not compatible with ECSlidingViewController.
Most interaction controllers will use some kind of gesture recognizer to trigger and update a transition. The gesture recognizing code will typically be a part of the interaction controller. The sequence of events in the interaction controller with a gesture is as follows:
- Gesture is recognized and goes into the
UIGestureRecognizerStateBeganstate. - The "gesture began" state calls one of the transitioning methods on the sliding view controller to trigger a transition.
-
startInteractiveTransition:gets called because a transition was triggered. Some state is setup to begin the transition here. - The gesture moves to the
UIGestureRecognizerStateChangedstate. The "gesture changed" state can now callupdateInteractiveTransition:on thetransitionContext. It can do this while inspecting the state that was setup instartInteractiveTransition:. - When the user ends the gesture or if it gets cancelled, the gesture recognizer can complete the transition however it sees fit. The transition could end with a
UIViewanimation or it could end usingUIKit Dynamicsfor a physics based finish. Anything can be done to end a transition as long as the rules are followed forstartInteractiveTransition:.
It may be unintuitive to go from "gesture began" -> startInteractiveTransition: -> "gesture changed". Just remember when the transition is triggered in "gesture began", startInteractiveTransition: is called immediately as a result of that.