View Controllers
UIViewController
is one of the most-used APIs in iOS.
Lifecycle
A common interview question, if simple.
init
awakeFromNib
(if available)viewDidLoad
viewWillAppear
viewDidLayoutSubviews
(And any subsequent times the view’s subviews are laid out)viewDidAppear
Ending:
viewWillDisappear
viewDidDisappear
deinit/dealloc
Transitions
Adding custom transitions, specifically.
Theory
- Set the controller’s
modalPresentationStyle
property to.custom
. - Set the
transitioningDelegate
on your view controller. Leaving it unset will use the default. - Have the
transitioningDelegate
’sanimationController(forPresented:presenting:source:)
andanimationController(forDismissed:)
methods return an appropriateUIViewControllerAnimatedTransitioning
.- This object needs to implement
transitionDuration(using:)
andanimateTransition(using:)
. transitionDuration(using:)
return the length of the animation.animateTransition(using:)
actually does the animation.- You do not implement a
UIViewControllerContextTransitioning
, this is passed in by the system.
- You do not implement a
- This object needs to implement
Example
Because these are all objective-c protocols, they need to be NSObject
subclasses, so:
class PageCurlAnimatedTransitioning: NSObject, UIViewControllerAnimatedTransitioning {
func transitionDuration(using context: UIViewControllerContextTransitioning?) -> TimeInteral {
return 0.25
}
func animateTransition(using context: UIViewControllerContextTransitioning) {
guard let fromVC = context.viewController(forKey: .from),
let toVC = context.viewController(forKey: .to),
let snapshot = toVC.view.snapshotView(afterScreenUpdates: true) else {
return
}
let containerView = context.containerView
let startFrame = context.initialFrame(for: fromVC)
let finalFrame = context.finalFrame(for: toVC)
snapshot.frame = startFrame
containerView.addSubview(toVC.view)
containerView.addSubview(snapshot)
toVC.view.isHidden = true
UIView.animate(
withDuration: self.transitionDuration(using: context),
delay: 0,
options: [.transitionCurlUp, .preferredFramesPerSecond60],
animations: {
snapshot.frame = finalFrame
},
completion: { _ in
snapshot.removeFromSuperview()
toVC.view.isHidden = false
})
}
}
class MyTransitioningDelegate: NSObject, UIViewControllerTransitioningDelegate {
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return PageCurlAnimatedTransitioning()
}
}
And these would be used like so:
self.transitioner = MyTransitioningDelegate() // hold a strong reference to this.
let viewControllerToPresent = // ...
viewControllerToPresent.modalPresentationStyle = .custom
viewControllerToPresent.transitioningDelegate = self.transitioner
vc.present(viewControllerToPresent, animated: true, completion: nil)
Last updated: 2020-06-07 16:24:37 -0700