5 days of learning from a variety of English language tutorials, the transition animation is an unforgettable exploration experience
Better refer to the article customizing UIViewController transition introduction, animation introduction.
Ferry way
First, let’s understand how iOS transitions work:
- UINavigationController push/pop UIViewController transitions to the navigation bar
- UITabBarController Switch Tab transitions
- Present /dismiss mode transition
These are the three basic transition modes provided by iOS. The default transition mode has limited transition styles. For example, in modal transitions, transitions are still stuck at the bottom, despite modalPresentationStyle and modalTransitionStyle Settings for presentation and transition styles. This does not meet our needs in software development, and the animation effects of the sidebar and the top bar are not well realized. After iOS 7.0, Apple provided a complete set of custom transition animation API, for a variety of transition animation implementation with unlimited possibilities.
This article mainly introduces the custom animation of modal transition. Animation for the top bar:
The source code of SlideMune
Modal transitions
Modal transitions are divided into non-interactive transitions and interactive transitions. A non-interactive transition is a normal transition. The animation of a transition cannot interact and the transition cannot be terminated during the animation. Interactive transitions can be transitioned and terminated by swiping through the screen with a finger touch.
Transition animation API
We define: if view controller Apresent is displayed after view controller B. Later, the source view controller is fromVC and the target view controller is toVC.
state | View Controller A | View Controller B |
---|---|---|
Present | Source View Controller | Target view Controller |
Dissmiss | Target view Controller | Source View Controller |
TransitioningDelegate Indicates the transition delegate
Each view controller have a UIViewController transitioningDelegate attribute, the agent shall follow UIViewControllerTransitioningDelegate agreement, provide relevant animation controller.
Whenever you display or close a view controller, UIKit requires its transition agent to use an animation controller. To replace the default animation with your own custom animation, you must implement the transition agent and return it to the appropriate animation controller.
AnimationController AnimationController
The transition agent returns the corresponding animation controller at present/dismiss. Animation controller is the core of transition animation. It does the “heavy lifting” of the animation transition.
TransitioningContext Transition context
The transitional context is implemented and plays a crucial role in the transition process: it encapsulates information about the views and view controllers involved in the transition. The transition context assists the animation controller to animate. As you can see from the figure, I did not implement this protocol. UIKit creates and configures the transition context for you and passes it to the animation controller each time a transition occurs.
The process of non-interactive transition animation
Take the present transition animation for example:
- Through code or
StoryBoard segue
Trigger the modal viewpresentProcess. - UIKit to
toVC
(Target view controller) requests its transitional broker. If not, UIKIt will use standard built-in transitions. - UIKit then requests the animation controller from the transition agent
animationController(forPresented:presenting:source:)
. If the returnnil
, the transition will use the default animation. - UIKit constructs transitional contexts. UIKit calls the animation controller to ask for the duration of the animation
transitionDuration(using:)
. UIKitanimateTransition(using:)
Upregulation in the animation controller to perform the transition animation. - Finally, the animation controller calls
completeTransition(_:)
Transition the context to indicate that the animation is complete.
The steps for the dimiss transition animation are almost the same. In this case, UIKit asks the fromVC view controller (the view controller that is closing) for its transition agent, asking for the animationController(forpresenting :).
Non-interactive transition animations need to provide conditions
- Set up the transition animation agent. Set (target view controller)
transitioningDelegate
Property that sets the transition animation proxy object that the proxy object followsUIViewControllerTransitioningDelegate
Protocol, implementationforPresented
andforDismissed
Two methods, providing view controller instances of present and dismiss, respectively. - Create the animation controller. Create present and Dismiss animation controllers that implement the animation duration and construct the animation method.
Here we complete a simple transition animation from left to right.
1. Construct the Present/Dimiss scenario
The build process is not covered here, but both stroyBoard and pure code are pretty well done. I’m going to do this in storyboard for convenience. The LeftViewController is ViewController. The right ViewController is LeftViewController
2. Create an Animation Controller
The Animation Controller is the core object for custom transition animations. AnimationControlle inheritance to NSObject, follow UIViewControllerAnimatedTransitioning protocol, implement two methods required.
class AnimationController: NSObject.UIViewControllerAnimatedTransitioning {
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
// Requires the animator object's animation duration property to be provided
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
// The implementation effect of the transition animation is also the core method of customizing the transition animation, which needs to build the animation implementation.}}Copy the code
The code for the AnimationController in the Present state is posted here, and the Dismiss state is similar (the animation process executes the reverse process).
import UIKit
class PresentAnimationController: NSObject.UIViewControllerAnimatedTransitioning {
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.6
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
/ / 1
guard let _ = transitionContext.viewController(forKey: .from),
let toVC = transitionContext.viewController(forKey: .to) else {
return
}
/ / 2
let containerView = transitionContext.containerView
containerView.addSubview(toVC.view)
let duration = transitionDuration(using: transitionContext)
toVC.view.frame = CGRect(x: -ScreenWidth, y: 0, width: ScreenWidth, height: ScrennHeight)
/ / 3
UIView.animate(withDuration: duration, animations: {
toVC.view.frame = CGRect(x: 0, y: 0, width: ScreenWidth, height: ScrennHeight) {}) (_) intransitionContext.completeTransition(! transitionContext.transitionWasCancelled) } } }Copy the code
Set the duration of the animation in the first transitionDuration(using:) method.
Build the animation in the second transitionDuration(using:) method.
- Get the view controller and snapshot required for the transition animation. From the transitional context
transitionContext.viewController
We can getSource view controller fromVCandTarget view controller toVCThe transitional context encapsulates the information of the designed view controller and greatly helps us deal with the animation of the view controller. You can also get fromVC and toVCSnapshot screenTo construct more complex and excellent animations. - Container view for managing transitional contextscontainerViewAnd view animation position initialization. UIKit encapsulates the entire transition in a container view to simplify the management of view hierarchies and animations, which are managed by the container view
fromVC.view
andtoVC.view
. Container views created by UIKit only containfromVCThe view. You must add any other views that will participate in the transition.
AddSubview (_:) puts the new view before all the other views in the view hierarchy, so the order in which the subviews are added is important.
- Set the animation effect. Animation has two ways to achieve, one is basic animation
animate
The other is keyframe animationanimateKeyframes
. I’m just going to simply putfromVC.view
Move from the left edge of the screen to the screen. Note: this is called after the animation is completecompleteTransition(_:)
Notify UIKit that the animation is complete. This ensures that the final state is consistent.
3. Set the transition animation agent
Set destinationVC’s modalPresentationStyle to the enumerated property Custom and the transition animation’s proxy to self, the ViewController.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destinationVC = segue.destination as? LeftViewController {
destinationVC.modalPresentationStyle = .custom
destinationVC.transitioningDelegate = self}}Copy the code
Then in (the source view controller) add extensions, follow UIViewControllerTransitioningDelegate agreement, achieve forPresented and forDismissed method.
extension ViewController: UIViewControllerTransitioningDelegate {
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
// The target VC is presented, while the source VC is presented
guard let _ = presented as? LeftViewController.let _ = presenting as? ViewController else {
return nil
}
return PresentAnimationController()}func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
guard let _ = dismissed as? LeftViewController else {
return nil
}
return DismissAnimationController()}}Copy the code
This builds a simple, basic custom transition animation.
Interactive transition animation
Interactive animation makes the user’s animation experience more natural and comfortable without being sharp, leaving room for control of the animation process. There are examples of interactive animations in the iOS native APP Settings.
The progress of the transition animation follows the slide of the finger to start/stop the transition animation, which makes for a good user interaction experience.
How the interactive controller works
Interactive controllers respond to touch events or programming inputs by speeding up, slowing down, or even reversing the transition process. To enable interactive transformations, the transformation agent must provide an interaction controller. You have animated the transitions. Based on transition animations, Apple encapsulates interactive animations as interactive controllers that respond to gestures to manage the animation, rather than allowing it to play back like a video. Apple provides a ready-made UIPercentDrivenInteractiveTransition class, through inherit this class to create their own interactive controller.
1. Create an interactive controller
To build an interactive transition animation from the previous transition animation, we first need to create an interactive controller. Posted here shows an interactive controller PresentInteractionController code, packaging good UIPercentDrivenInteractiveTransition class inheritance.
class PresentInteractionController: UIPercentDrivenInteractiveTransition {
var interactionInProgress = false
private var shouldCompleteTrantision = false
private weak var viewController: UIViewController!
private weak var toViewController: UIViewController!
init(viewController: UIViewController, toViewController: UIViewController) {
super.init(a)self.viewController = viewController
self.toViewController = toViewController
prepareGestureRecognizer(in: self.viewController.view)
}
private func prepareGestureRecognizer(in view: UIView) {
let gesture = UIScreenEdgePanGestureRecognizer(target: self, action: #selector(handleGsture(_:)))
gesture.edges = .left
view.addGestureRecognizer(gesture)
}
@objc func handleGsture(_ gestureRecognizer: UIScreenEdgePanGestureRecognizer) {
let translation = gestureRecognizer.translation(in: gestureRecognizer.view? .superview)var progress = translation.x / 200
progress = CGFloat(fminf(fmaxf(Float(progress), 0.0), 1.0))
switch gestureRecognizer.state {
case .began:
interactionInProgress = true
viewController.present(toViewController, animated: true, completion: nil)
case .changed:
shouldCompleteTrantision = progress > 0.5
update(progress)
case .cancelled:
interactionInProgress = false
cancel()
case .ended:
interactionInProgress = false
if shouldCompleteTrantision {
finish()
} else {
cancel()
}
default:
break}}}Copy the code
interactionInProgress
Bool property indicating whether an interactive scene is occurring.shouldCompleteTrantision
Bool property indicating whether the transition animation should be terminated. For internal management transition.viewController
andtoViewController
To obtain the reference of the source view controller and the target view controller, which is used to manage the transition of a state present view controller, and achieve the function of connecting the interactive controller and the animation controller.prepareGestureRecognizer(in:)
Add screen gesture method for source view, here interactive animation is presented from left to right VC, so add screen for source view corresponding in.left
The gesture.handleGsture(_:)
Method of changing transition animation state for corresponding gesture changes. Track slide progress by declaring local variablestranslation
, according to thetranslation
Gets and calculates the transition progress in the viewprogress
. Gesture starts when you will setinteractionInProgress
fortrue
And triggerprsent
View controller. Gesture is called when movingupdate(_:)
Update the transition progress. This is a basisUIPercentDrivenInteractiveTransition
You pass in the percentage move transition method. If the gesture is cancelled, it is updatedinteractionInProgress
And cancel the transition. Once the action has finished, you use the transition schedule to determinecancel()
orfinish()
.
2. Controller contact
The (source) view controller is associated with the interaction controller
Add the following properties to the viewController:
var presentInteractionController: PresentInteractionController?
Copy the code
And initialize the property in the viewDidLoad() method:
presentInteractionController = PresentInteractionController(viewController: self, toViewController: leftViewController)
Copy the code
The interaction controller is associated with the animation controller
In PresentAnimationController add the following attributes:
let interactionController: PresentInteractionController?
Copy the code
And add init method:
init(interactionController: PresentInteractionController?). {self.interactionController = interactionController
}
Copy the code
3. Implement the transitional agent protocol
Animation controller forPresented method of modified PresentAnimationController object creation.
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
// The PRESENTING VC is presented while the presenting VC is presented
guard let _ = presented as? LeftViewController.let fromVC = presenting as? ViewController else {
return nil
}
return PresentAnimationController(interactionController: fromVC.presentInteractionController)
}
Copy the code
Add the following methods:
func interactionControllerForPresentation(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
guard let animator = animator as? PresentAnimationController.let interactionController = animator.interactionController,
interactionController.interactionInProgress else {
return nil
}
return interactionController
}
Copy the code
The first check for PresentAnimationController involved in the animation controller. If so, it gets a reference to the associated interaction controller and verifies that the user interaction is in progress. If any of these conditions are not met, it returns nil so that the conversion will continue without interaction. Otherwise, it returns the interaction controller to UIKit so that it can manage the transition.
The method of the interactive controller of the dismiss state is similar, and the final result is as follows:
Download the source code