📜 ⬆️ ⬇️

The story of one controller view that wanted to show off beautifully

There was a modest view controller VCYellow . And he had no pictures, no text, or even a tiny business logic. He lived a normal view-controller life.


His fellow controller view VCMain sometimes presented it to the world:


class VCMain: UIViewController { ... @IBAction func onBtnTapMeTapped(_ sender: Any) { let vcYellow = self.storyboard!.instantiateViewController(withIdentifier: "VCYellow") as! VCYellow self.present(vcYellow, animated: true, completion: nil) } 

And VCYellow, in turn, was hiding with the help of a single "X" button, which, by the way, he was very proud of:


 class VCYellow: UIViewController { ... @IBAction func onBtnCloseTapped(_ sender: Any) { self.dismiss(animated: true, completion: nil) } 

And it looked not so bad, but boring and mundane:



But our hero had a dream to learn how to show and hide in beauty. Yes, so that you can change this beauty later on holidays or simply in honor of a good mood.



It has been a year ... and it would have remained a dream, if it had not learned VCYellow about magic called:


 UIViewControllerTransitioningDelegate 

And the power of this magic is that it gives the opportunity to slip the corresponding animator to show and hide the view controller. Just what our controller dreamed of.
He read in the ancient scrolls how to use the spell and began to prepare.
I wrote myself a cheat sheet with the spell itself, so as not to forget:


 extension VCYellow: UIViewControllerTransitioningDelegate { func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { return AnimatorPresent(startFrame: self.startFrame) } func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { return AnimatorDismiss(endFrame: self.startFrame) } } 

In it, he carefully wrote that to show you need to use AnimatorPresent , and when closing AnimatorDismiss .
Well, as an aid to both animators, it was decided to transfer the frame of the main button from VCMain


And then he morally tuned. Because without the right mood, as you know, no magic works:


 override func viewDidLoad() { super.viewDidLoad() self.modalPresentationStyle = .custom self.transitioningDelegate = self } 

He asked his friend VCMain to present himself to check how the magic worked and ... it worked in any way ...
It turned out that AnimatorPresent and AnimatorDismiss do not appear by themselves.


It was too late to stop, and our hero decided to create the necessary animators. I dig into the necessary section of the ancient scrolls and learned that two things are enough to create an animator.


First, you need to set the time allotted for the animation:


 func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { return 0.3 } 

and second, mark the animation itself:


 func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { //1 guard let vcTo = transitionContext.viewController(forKey: .to), let snapshot = vcTo.view.snapshotView(afterScreenUpdates: true) else { return } //2 let vContainer = transitionContext.containerView //3 vcTo.view.isHidden = true vContainer.addSubview(vcTo.view) //4 snapshot.frame = self.startFrame vContainer.addSubview(snapshot) UIView.animate(withDuration: 0.3, animations: { //5 snapshot.frame = (transitionContext.finalFrame(for: vcTo)) }, completion: { success in //6 vcTo.view.isHidden = false snapshot.removeFromSuperview() transitionContext.completeTransition(true) }) } 

  1. Pull out the presented view controller (in our case VCYellow) and take it. The photo is needed to simplify the animation.
  2. Get the view where the animated witch will be. Let's call its context.
  3. To fasten a view of the final controller on a context and to hide it. Show
  4. it was decided after the animation ended.
  5. Prepare a photo for animation. Reduce to initial size and throw on the context.
  6. Split pictures into full screen, thereby animating the process of presentation.
  7. After the animation ends, show the real view of the final controller,
  8. get rid of the pictures and report that the action is over.

As a result, this animator came out to show:


 import UIKit class AnimatorPresent: NSObject, UIViewControllerAnimatedTransitioning { let startFrame: CGRect init(startFrame: CGRect) { self.startFrame = startFrame } func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { return 0.3 } func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { guard let vcTo = transitionContext.viewController(forKey: .to), let snapshot = vcTo.view.snapshotView(afterScreenUpdates: true) else { return } let vContainer = transitionContext.containerView vcTo.view.isHidden = true vContainer.addSubview(vcTo.view) snapshot.frame = self.startFrame vContainer.addSubview(snapshot) UIView.animate(withDuration: 0.3, animations: { snapshot.frame = (transitionContext.finalFrame(for: vcTo)) }, completion: { success in vcTo.view.isHidden = false snapshot.removeFromSuperview() transitionContext.completeTransition(true) }) } } 

After that, it was easy to write an animator for hiding, which does roughly the same thing, but vice versa:


 import UIKit class AnimatorDismiss: NSObject, UIViewControllerAnimatedTransitioning { let endFrame: CGRect init(endFrame: CGRect) { self.endFrame = endFrame } func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { return 0.3 } func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { guard let vcTo = transitionContext.viewController(forKey: .to), let vcFrom = transitionContext.viewController(forKey: .from), let snapshot = vcFrom.view.snapshotView(afterScreenUpdates: true) else { return } let vContainer = transitionContext.containerView vContainer.addSubview(vcTo.view) vContainer.addSubview(snapshot) vcFrom.view.isHidden = true UIView.animate(withDuration: 0.3, animations: { snapshot.frame = self.endFrame }, completion: { success in transitionContext.completeTransition(true) }) } } 

Having finished all the finishing touches , VCYellow again asked his friend VCMain to present himself and about a miracle!



The magic worked! VCYellow's dream came true! Now he can show and hide as he pleases and nothing will limit his imagination!


An example project can be downloaded here.


The article I used for inspiration is here.


')

Source: https://habr.com/ru/post/424853/


All Articles