iOS customized transition tutorial in Swift

0/5 No votes

Report this app

Description

[ad_1]

On this tutorial, you will discover ways to substitute the push, pop and modal animations with customized transitions & p.c pushed interactions.

UIKit

UIKit customized transition API – a theoretical lesson

There are numerous courses and delegates concerned through the course of of constructing a customized transition, let’s stroll by this stuff actual fast, and do some coding afterwards.

UIViewControllerTransitioningDelegate

Each view controller can have a transition delegate, in that delegate implementation you possibly can present the customized animation and interplay controllers. These objects will probably be chargeable for the precise animation course of, and this delegate is the place the place you possibly can “inject your code” to the UIKit framework. ๐Ÿ’‰๐Ÿ’‰๐Ÿ’‰

UINavigationControllerDelegate

The navigation controller delegate additionally has two strategies which can be chargeable for customized push and pop animations. It is nearly the identical because the transitioning delegate for the view controllers, however you will see this in motion afterward. ๐Ÿ’ฅ

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 shouldn’t be 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 mainly that is the place the place you implement the flowery customized view animations. ๐Ÿ˜‰

UIViewControllerContextTransitioning

This context encapsulates all the information concerning the transitioning, you will get the collaborating views, controllers and lots of extra from this object. The transitioning context is accessible so that you can use it through 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 offers you the magical capability to swipe a navigation controller interactively again (and forth if you happen to modified your thoughts) together with your fingers from the sting of the display screen. ๐Ÿ“ฑ


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 possibly can see making a customized transition animation is actually easy. You simply need to implement two delegate strategies. Certainly one of them will return the period of the animation, and the opposite will comprise the precise transition.

The transition context offers a customized containterView object that you need to use within the animation, additionally you possibly can 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 need to implement the navigation controller’s delegate technique so as to substitute the built-in UIKit system animations. ๐Ÿ› 

extension MainViewController: UINavigationControllerDelegate {

    func navigationController(_ navigationController: UINavigationController,
                              animationControllerFor operation: UINavigationController.Operation,
                              from fromVC: UIViewController,
                              to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        swap operation {
        case .push:
            return FadePushAnimator()
        case .pop:
            return FadePopAnimator()
        default:
            return nil
        }
    }
}

Be aware that you do not have to make two separate courses (pop & push), you can even move the operation and implement the animations in a single animated tarnsitioning class.


% pushed interactive transitions

So, now you know the way to implement a customized transition, however it is time to make it interactive! The method is fairly easy, you will solely want a gesture recognizer and a correct delegate technique 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 p.c = translate.x / gesture.view!.bounds.dimension.width

        swap gesture.state {
        case .started:
            self.interactionController = UIPercentDrivenInteractiveTransition()
            self.navigationController?.popViewController(animated: true)
        case .modified:
            self.interactionController?.replace(p.c)
        case .ended:
            let velocity = gesture.velocity(in: gesture.view)

            if p.c > 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 probably be popped you possibly can take possession of the navigation controller’s delegate and implement the interactive transition controller utilizing a left display screen edge pan gesture recognizer. This complete 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 ultimate instance code you will discover the “subclassed model” of the interactive transition. ๐Ÿ˜…


Navigation vs modal presentation

Okay, let’s cowl another factor actual fast: customizing modal presentation animations for view controllers. There’s a minor distinction between customizing the navigation stack animations and modal presentation types. 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 introduced: UIViewController,
                             presenting: UIViewController,
                             supply: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return FadePushAnimator()
    }

    func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return FadePopAnimator()
    }
}

When you run the code and current the modal view controller, that’ll work simply tremendous. The issue happens if you attempt to dismiss the introduced view controller. The entire app will flip to a black display screen of loss of life (BSOD). ๐Ÿ–ฅ

(pop != dismiss) && (push != current)

It’s a must to modify the pop animation so as 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 simplest 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 possibly can safely use the identical animators for modally introduced view controllers as properly. ๐Ÿ˜ฌ

The pattern code is inside The.Swift.Dev. tutorials repository, you will discover examples for changing the default push & pop navigation animations with customized ones.

Be aware that the navigation bar will all the time use a fade animation, sadly that may not be personalized. Additionally I’ve made a customized modal presentation, and the whole lot is utilizing the interactive transitions too. Clearly there may be much more, however under are some hyperlinks that you may comply with if you happen to hit an impediment throughout your journey.

Additionally if you happen to do not wish to manually implement customized animation results you need to use Hero the elegant transition library.

[ad_2]

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.