[ad_1]
On this tutorial, you may discover ways to exchange the push, pop and modal animations with customized transitions & % pushed interactions.
UIKit
UIKit customized transition API – a theoretical lesson
There are numerous lessons and delegates concerned throughout the course of of constructing a customized transition, let’s stroll by means of these things actual fast, and do some coding afterwards.
UIViewControllerTransitioningDelegate
Each view controller can have a transition delegate, in that delegate implementation you may present the customized animation and interplay controllers. These objects will likely be accountable for the precise animation course of, and this delegate is the place the place you may “inject your code” to the UIKit framework. 💉💉💉
UINavigationControllerDelegate
The navigation controller delegate additionally has two strategies which are accountable for customized push and pop animations. It is virtually the identical because the transitioning delegate for the view controllers, however you may see this in motion in a while. 💥
UINavigationController.Operation
The navigation controller operation is simply an enum which accommodates the “path” of the navigation animation. Normally push or pop.
Presenting and dismissing one thing modally just isn’t precisely the identical factor as pushing & popping view controllers inside a navigation stack. Extra on this later.
UIViewControllerAnimatedTransitioning
These objects are returned by the transition delegate, so principally that is the place the place you implement the flamboyant customized view animations. 😉
UIViewControllerContextTransitioning
This context encapsulates all the data in regards to the transitioning, you will get the collaborating views, controllers and lots of extra from this object. The transitioning context is offered so that you can use it throughout the animation.
UIPercentDrivenInteractiveTransition
An object that drives an interactive animation between one view controller and one other.
In a nutshell, that is the factor that provides you the magical capability to swipe a navigation controller interactively again (and forth in case you modified your thoughts) together with your fingers from the sting of the display. 📱
Customized transition animations programmatically
Let’s do some actual coding! I am going to present you easy methods to make a fundamental fade animation between view controllers inside a navigation stack. First we’ll begin with the push animation.
open class FadePushAnimator: NSObject, UIViewControllerAnimatedTransitioning {
open func transitionDuration(utilizing transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.5
}
open override func animateTransition(utilizing transitionContext: UIViewControllerContextTransitioning) {
guard
let toViewController = transitionContext.viewController(forKey: .to)
else {
return
}
transitionContext.containerView.addSubview(toViewController.view)
toViewController.view.alpha = 0
let period = self.transitionDuration(utilizing: transitionContext)
UIView.animate(withDuration: period, animations: {
toViewController.view.alpha = 1
}, completion: { _ in
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
})
}
}
As you may see making a customized transition animation is de facto easy. You simply must implement two delegate strategies. Considered one of them will return the period of the animation, and the opposite will comprise the precise transition.
The transition context supplies a customized containterView object that you should utilize within the animation, additionally you may seize the collaborating views and controllers from this object as I discussed it earlier than. Now let’s reverse this animation. 👈
open class FadePopAnimator: CustomAnimator {
open func transitionDuration(utilizing transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.5
}
open override func animateTransition(utilizing transitionContext: UIViewControllerContextTransitioning) {
guard
let fromViewController = transitionContext.viewController(forKey: .from),
let toViewController = transitionContext.viewController(forKey: .to)
else {
return
}
transitionContext.containerView.insertSubview(toViewController.view, belowSubview: fromViewController.view)
let period = self.transitionDuration(utilizing: transitionContext)
UIView.animate(withDuration: period, animations: {
fromViewController.view.alpha = 0
}, completion: { _ in
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
})
}
}
Lastly you simply must implement the navigation controller’s delegate methodology with a purpose to exchange the built-in UIKit system animations. 🛠
extension MainViewController: UINavigationControllerDelegate {
func navigationController(_ navigationController: UINavigationController,
animationControllerFor operation: UINavigationController.Operation,
from fromVC: UIViewController,
to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
change operation {
case .push:
return FadePushAnimator()
case .pop:
return FadePopAnimator()
default:
return nil
}
}
}
Word that you do not have to make two separate lessons (pop & push), you can even move the operation and implement the animations in a single animated tarnsitioning class.
P.c pushed interactive transitions
So, now you understand how to implement a customized transition, however it is time to make it interactive! The method is fairly easy, you may solely want a gesture recognizer and a correct delegate methodology to make issues work. ⌨️
class DetailViewController: UIViewController {
var interactionController: UIPercentDrivenInteractiveTransition?
override func viewDidLoad() {
tremendous.viewDidLoad()
self.view.backgroundColor = .lightGray
let edge = UIScreenEdgePanGestureRecognizer(goal: self,
motion: #selector(self.handleEdgePan(_:)))
edge.edges = .left
self.view.addGestureRecognizer(edge)
}
override func viewDidAppear(_ animated: Bool) {
tremendous.viewDidAppear(animated)
self.navigationController?.delegate = self
}
@objc func handleEdgePan(_ gesture: UIScreenEdgePanGestureRecognizer) {
let translate = gesture.translation(in: gesture.view)
let % = translate.x / gesture.view!.bounds.measurement.width
change gesture.state {
case .started:
self.interactionController = UIPercentDrivenInteractiveTransition()
self.navigationController?.popViewController(animated: true)
case .modified:
self.interactionController?.replace(%)
case .ended:
let velocity = gesture.velocity(in: gesture.view)
if % > 0.5 || velocity.x > 0 {
self.interactionController?.end()
}
else {
self.interactionController?.cancel()
}
self.interactionController = nil
default:
break
}
}
}
extension DetailViewController: UINavigationControllerDelegate {
func navigationController(_ navigationController: UINavigationController,
interactionControllerFor animationController: UIViewControllerAnimatedTransitioning)
-> UIViewControllerInteractiveTransitioning? {
return self.interactionController
}
}
Contained in the controller that will likely be popped you may take possession of the navigation controller’s delegate and implement the interactive transition controller utilizing a left display edge pan gesture recognizer. This entire code often goes into a brand new subclass of UIPercentDrivenInteractiveTransition however for the sake of simplicity this time we’ll skip that, and go along with this very easy resolution. Within the closing instance code you may discover the “subclassed model” of the interactive transition. 😅
Navigation vs modal presentation
Okay, let’s cowl yet one more factor actual fast: customizing modal presentation animations for view controllers. There’s a minor distinction between customizing the navigation stack animations and modal presentation kinds. If you wish to customise a view controller transition you’d often do one thing like this. 👍
class DetailViewController: UIViewController {
override func put together(for segue: UIStoryboardSegue, sender: Any?) {
tremendous.put together(for: segue, sender: sender)
guard let controller = segue.vacation spot as? ModalViewController else {
return
}
controller.transitioningDelegate = self
controller.modalPresentationStyle = .customized
controller.modalPresentationCapturesStatusBarAppearance = true
}
}
Right here comes the transitioning delegate, utilizing the identical objects that we have already got.
extension DetailViewController: UIViewControllerTransitioningDelegate {
func animationController(forPresented offered: UIViewController,
presenting: UIViewController,
supply: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return FadePushAnimator()
}
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return FadePopAnimator()
}
}
If you happen to run the code and current the modal view controller, that’ll work simply high-quality. The issue happens whenever you attempt to dismiss the offered view controller. The entire app will flip to a black display of demise (BSOD). 🖥
(pop != dismiss) && (push != current)
It’s important to modify the pop animation with a purpose to assist modal dismissal animations. In brief: the issue is with inserting views and reminiscence administration.
open class FadePopAnimator: NSObject, UIViewControllerAnimatedTransitioning {
public enum TransitionType {
case navigation
case modal
}
let kind: TransitionType
let period: TimeInterval
public init(kind: TransitionType, period: TimeInterval = 0.25) {
self.kind = kind
self.period = period
tremendous.init()
}
open func transitionDuration(utilizing transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return self.period
}
open override func animateTransition(utilizing transitionContext: UIViewControllerContextTransitioning) {
guard
let fromViewController = transitionContext.viewController(forKey: .from)
else {
return
}
if self.kind == .navigation, let toViewController = transitionContext.viewController(forKey: .to) {
transitionContext.containerView.insertSubview(toViewController.view, belowSubview: fromViewController.view)
}
let period = self.transitionDuration(utilizing: transitionContext)
UIView.animate(withDuration: period, animations: {
fromViewController.view.alpha = 0
}, completion: { _ in
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
})
}
}
The most straightforward resolution is to introduce a brand new property so you may make a call to pop or dismiss the view controller primarily based on that flag. Now you may safely use the identical animators for modally offered view controllers as properly. 😬
The pattern code is inside The.Swift.Dev. tutorials repository, you may discover examples for changing the default push & pop navigation animations with customized ones.
Word that the navigation bar will at all times use a fade animation, sadly that may not be personalized. Additionally I’ve made a customized modal presentation, and all the things is utilizing the interactive transitions too. Clearly there’s much more, however beneath are some hyperlinks which you could comply with in case you hit an impediment throughout your journey.
Additionally in case you do not wish to manually implement customized animation results you should utilize Hero the elegant transition library.
[ad_2]
