It’s been a while and after some long due time off I’m finally back with a new iOS tutorial.
I wanted to add some more details to my previous post showing you how to implement an interactive transition and that’s exactly what I’m going to do with this article!
This short video shows a preview of what we’re going to create with this tutorial:
Interactive vs Animated transition
The main difference between these two ways of performing a transition is obviously how they progress from start to finish.
With an animated transition we define the timing, the initial and final state with transitionDuration and animateTransition methods of UIViewControllerAnimatedTransitioning. So the animation just runs from 0 to 100% in a pre-defined time. Yes, you could also perform keyframe animation having some more control over the flow, but you still end up with a complete animation cycle, from 0 to 100%.
The interactive transition is a great way to create a synergy with the user. He (or she) becomes the director of the transition, driving the animation flow and cancelling the transition if need be.
Custom transition, quick recap
In my previous post we’ve created a custom transition to present a modal controller essentially following these steps:
1) Creating a UIViewControllerTransitioningDelegate. Its main role is returning an object responsible for the animation.
2) Creating that object. It has to implement the UIViewControllerAnimatedTransitioning protocol.
3) The method animateTransition is part of that protocol and here we’ve “drawn” the animation thanks to the transition context. The context (UIViewControllerContextTransitioning) comes with important information such as the “from”, the “to” controllers and the containerView where the animation takes place.
You can take a look at my
Go Interactive, the easiest way
So far so good, those steps are still needed to build an interactive transition, but obviously we have to add something more to this procedure to achieve the interactivity.
Our goal is basically to tie the user’s touch position to the current animation progress, so we have to implement a gesture handler that takes that into account. Then we can convert this information in the transition progress:
First, we have to implement two more methods from UIViewControllerTransitioningDelegate. We have already implemented the two that return the UIViewControllerAnimatedTransitioning object (step 2 of the previous list), namely animationControllerForPresentedController:etcetc… and animationControllerForDismissedController.
Now we have to inform the system that we want to perform an interactive transition, so it needs an object that implements the UIViewControllerInteractiveTransitioning protocol. These two methods are interactionControllerForPresentation and interactionControllerForDismissal. Again, the system needs an object to guide the presentation and the dismissal of the controller, and again, this object has to implement a protocol, the UIViewControllerInteractiveTransitioning.
This logic is really similar to what we did to create the “standard” custom transition.
Let’s code
Open the project and check the group “Controllers”. We have 2 classes here: mainController and modalController. Their roles are obvious: mainController is the presenter of the modalController, nothing more.
In the group Transition you can find the core of this tutorial: the TransitionManager. This object contains all the transition logic, from the animation to the gesture handler, and it implements all the protocols that I’ve previously told you about.
The gesture recogniser
We want to tie the user interaction to the transition progress. So let’s review the code needed for this operation. Open the TransitionManager.m file.
The function setMainController is the setter for the mainController property, that is just a reference to the current Main Controller.
- (void)setMainController:(MainViewController *)mainController{
_mainController = mainController;
UIScreenEdgePanGestureRecognizer *panGesture = [[UIScreenEdgePanGestureRecognizer alloc]initWithTarget:self action:@selector(gestureHandler:)];
panGesture.edges = UIRectEdgeLeft;
[[self.mainController.view window] addGestureRecognizer:panGesture];
}
While we set this property we attach a gesture recognizer for the UIScreenEdgePanGestureRecognizer to the window view and we set its side as left.
Note: we use the window view because that way the gesture is recognised in the entire application. How to set the recogniser obviously depends on your specific needs.
At this point, when user moves his finger from the left side of the screen to the right, the gesture handler is called.
UIPercentDrivenInteractiveTransition
Now that a part of the user interactions is tied to the transitionManager it’s time to grab this information and put it to good use.
I previously mentioned the UIViewControllerInteractiveTransitioning, but as you can see the TransitionManager is not implementing it. It is, in fact, subclassing the UIPercentDrivenInteractiveTransition.
`A percent-driven interactive transition object drives the custom animation between the disappearance of one view controller and the appearance of another. It relies on a transition animator delegate (a custom object that adopts the UIViewControllerAnimatorTransitioning protocol) to set up and perform the animations.`
This class has 3 important methods that work together with the gesture handler to drive the animation.
• updateInteractiveTransition: used to set the progress of the transition from 0.0 to 1.0.
• cancelInteractiveTransition: in case we wanted to abort the transition (for example when the user aborts the gesture)
• finishInteractiveTransition: to mark that the transition can be completed in cases when the user only partially completes the gesture (but we want to assume he intends to complete the transition).
So, having already created the animation (it is described in the animateTransition: method), we now build the gesture handler that, together with the UIPercentDrivenInteractiveTransition method, is going to make the interaction work.
The gesture recognizer handler
Stay in the TransitionManager.m file and check the gestureHandler function.
This is a standard gesture handler that, in this case, has to recognize pan gestures from the left side of the screen, nothing special.
When the gesture state is UIGestureRecognizerStateChanged we instantiate the modalController using the same routine we adopt for the animated custom transition:
// Instantiate the modal controller
self.modalController = [[ModalViewController alloc]init];
self.modalController.transitioningDelegate = self;
self.modalController.modalPresentationStyle = UIModalPresentationCustom;
// Here we could set the mainController as delegate of the modal controller to get/set useful information
// Example: self.modalController.delegate = self.mainController;
// Present the controller
[self.mainController presentViewController:self.modalController animated:YES completion:nil];
1) set a transition delegate (in this case, the TransitionManager itself).
2) set the modal presentation style to UIMoldaPresentationCustom.
Note: in the previous tutorial I set this property to the mainController. Mistake. Fixed.
3) present the modalController from the mainController
Whit the gesture state UIGestureRecognizerStateChanged we just convert the touch location into a value ranging from 0 to 1 as it was shown in the previous image. In this code we take as reference the whole screen. So the left side is 0 and the right side is 1.
CGFloat animationRatio = location.x / CGRectGetWidth([self.mainController.view window].bounds);
[self updateInteractiveTransition:animationRatio];
With these 2 rows we are translating the finger position to the progress of the animation.
Last but not least, the state UIGestureRecognizerStateEnded. Within this state we check wether the user wants to cancel the transition or complete it. To check user intention we just take into account the gesture velocity – not to be confused with the speed, velocity can be intended as the direction -.
If the direction is right we complete the transition otherwise we cancel it.
// 3. Complete or cancel the animation when gesture ends
else if (recognizer.state == UIGestureRecognizerStateEnded) {
//[self.finger removeFromSuperview];
if (self.transitionTo == MODAL) {
if (velocity.x > 0) {
[self finishInteractiveTransition];
}
else {
[self cancelInteractiveTransition];
}
self.modalController = nil;
}
}
At this point we have implemented the interactivity with the transition and it works like a charm.
Final notes
You can see that the transition now works in two different ways.
The presentation uses the interactive transition, while the dismiss keeps the previous custom transition, but both use the same animation! This means that you can create a really complete user experience mixing together a full user interaction with some driven call to action (like a button to dismiss the current controller).
Have fun with transitions and show me your results pinging me on Twitter!
Yari D'areglia
https://www.thinkandbuild.itSenior iOS developer @ Neato Robotics by day, game developer and wannabe artist @ Black Robot Games by night.