Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Default Custom Transition for UIViewController

I am developing a library and I want to provide a default custom transition between two view controllers, the user can also provide his own implementation the first idea that comes to my my mind is to override UIViewController and implement the UIViewControllerTransitioningDelegate and then users can subclass my CustomTransitionViewController is it the best way to do it ? any limitations ? is there a more elegant way using just protocols for example with default implementation ?

import UIKit

class CustomTransitionViewController: UIViewController, UIViewControllerTransitioningDelegate {

  required init?(coder aDecoder: NSCoder) {
      super.init(coder: aDecoder)
      self.transitioningDelegate = self
  }

  override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil:Bundle?)   {
      super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
      self.transitioningDelegate = self
  }

  func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
      return FadeInAnimator(transitionDuration: 0.5, startingAlpha: 0.8)
  }

  func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
      return FadeInAnimator(transitionDuration: 0.5, startingAlpha: 0.8)
  }
}
like image 982
iOSGeek Avatar asked Dec 18 '16 19:12

iOSGeek


1 Answers

I think one of the most elegant (and protocol oriented) ways to do this would be with a UIViewControllerTransitioningDelegate extension. Extend the protocol and provide a default implementation for animationController(forPresented: presenting: source:) and animationController(forDismissed:). The extension would look something like this:

extension UIViewControllerTransitioningDelegate where Self: UIViewController {
    func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return FadeInAnimator(transitionDuration: 0.5, startingAlpha: 0.8)
    }

    func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return FadeInAnimator(transitionDuration: 0.5, startingAlpha: 0.8)
    }
}

And then tell your users to extend their view controllers to conform to this protocol. Since you already implemented the only requirements in a protocol extension, all they need to do is add the conformance declaration, like so:

class MyViewController: UIViewController, UIViewControllerTransitioningDelegate { }

If you want to extend all UIViewControllers to do so, you can do it with an empty extension:

extension UIViewController: UIViewControllerTransitioningDelegate { }

That should work. In my opinion, it's a very clean, elegant solution that doesn't require unnecessary subclassing. It also makes it easy to provide different implementations of animationController(forPresented: presenting: source:) and animationController(forDismissed:) in each view controller. Your users can just add their own implementations of the two aforementioned functions wherever they want. Also, you don't need to deal with messy Objective C stuff like associated objects.

If you're providing this functionality in some sort of prepackaged bundle and you want to hide the underlying implementation, you could declare your own protocol, make it a subprotocol of UIViewControllerTransitioningDelegate, extend it similarly, and have your users conform to your custom protocol instead of UIViewControllerTransitioningDelegate:

protocol MyProtocol: UIViewControllerTransitioningDelegate {
    //Add your own requirements (if you have any).
}

extension MyProtocol where Self: UIViewController {
    func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return FadeInAnimator(transitionDuration: 0.5, startingAlpha: 0.8)
    }

    func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return FadeInAnimator(transitionDuration: 0.5, startingAlpha: 0.8)
    }

    //Satisfy your requirements (or let your users do it).
}

class MyViewController: UIViewController, MyProtocol { }

extension UIViewController: MyProtocol { }

Whichever route you take, this solution gives you what you want without making you deal with extraneous subclassing or ugly Objective C concepts. Good luck!

like image 52
pranjal Avatar answered Sep 19 '22 20:09

pranjal