Custom transition animation of presentViewController (Swift)
Preface:
IOS defaultpresentViewController
The switching animation is pushed from the bottom, and disappears from the top. However, by default, the iOS system is applicable to all transfer contexts. For specific transfer context, we can make better results.
Tips: the so-called transfer context is the start View and end View of the transfer, and the corresponding ViewController
Target Effect
Final Effect
Preparations
First, writeCollectionView
Each Cell is an image. Because the core of this article is how to transferCollectionView
. This is the result after writing.
Click a CollectionView Cell to view the larger image, and then click the larger image to disappear.
How to Implement custom transfer Animation
After iOS 8, we can setViewController
OftransitioningDelegate
To set a proxy to process the transition animation.
You can see through the documenttransitioningDelegate
Is an implementationUIViewControllerTransitioningDelegate
The object of the Protocol. This article mainly uses the following two methods:
AnimationControllerForDismissedController animationControllerForPresentedController
The purpose of the two methods is to provideUIViewControllerAnimatedTransitioning
The object of the protocol, and then the object is used to actually process the session. We can see from the name that one is processing the present and the other is processing the dismiss.
The design in this article isViewController
IndidSelectItemAtIndexPath
, Set pvc transfer proxy
dvc.transitioningDelegate = self
Then, write an extension to implement the Protocol.
extension ViewController:UIViewControllerTransitioningDelegate{ func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { return nil } func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? { return nil }}
When you run the project again, you will find that there is no change, or the default transfer mode. Because we have not provided the actual animation. When the above two proxy Methods return nil, the system will use the default method
Implement UIViewControllerAnimatedTransitioning
Create a new file named Animator. swift, and create a new class to process the transferred field of the Present (similar to Dismiss), and implement the UIViewControllerAnimatedTransitioning protocol.
class PresentAnimator: NSObject,UIViewControllerAnimatedTransitioning{}
At this time, an error will be reported that the Protocol is not implemented. Then, we add the Protocol method. At this time, the Animator. swift is as follows:
Import Foundationimport UIKitclass PresentAnimator: NSObject, duration {let duration = 0.5 // animation time var originFrame = CGRectZero // click the frame func transitionDuration (transitionContext: duration?) of the Cell ?) -> NSTimeInterval {return duration} func animateTransition (transitionContext: UIViewControllerContextTransitioning ){}}
Briefly introduce the Protocol method here
TransitionDuration: returns the animation time animateTransition. The actual animation is used to obtain the fromView, toView, fromViewController, and toViewController of the transition through the transitionContext parameter. Transfer Principle
When the transfer starts, the FromView is automatically added to the inview when the transfer ends, and the FromView is automatically removed from the ContainView
So what developers need to do is
Add toView to inview, and define the initial position and status of toView. Define the State creation animation at the end of FromView and ToView. Real transfer Animation
Based on the above principle, we modify the actual Animation
func animateTransition(transitionContext: UIViewControllerContextTransitioning) { let containView = transitionContext.containerView() let toView = transitionContext.viewForKey(UITransitionContextToViewKey)! let finalFrame = toView.frame let xScale = originFrame.size.width/toView.frame.size.width let yScale = originFrame.size.height/toView.frame.size.height toView.transform = CGAffineTransformMakeScale(xScale, yScale) toView.center = CGPointMake(CGRectGetMidX(originFrame), CGRectGetMidY(originFrame)) containView?.addSubview(toView) UIView.animateWithDuration(duration, animations: { () -> Void in toView.center = CGPointMake(CGRectGetMidX(finalFrame), CGRectGetMidY(finalFrame)) toView.transform = CGAffineTransformIdentity }) { (finished) -> Void in transitionContext.completeTransition(true) } }
Then, add an attribute in viewcontroller.swf it.
let presentAnimator = PresentAnimator()
ModifydidSelectItemAtIndexPath
override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) { let dvc = DetailViewController() dvc.image = UIImage(named: "image.jpg") dvc.transitioningDelegate = self let cell = collectionView.cellForItemAtIndexPath(indexPath) as! FullImageCell presentAnimator.originFrame = cell.convertRect(cell.imageview.frame, toView: nil) self.presentViewController(dvc, animated: true, completion: nil) }
Modify proxy method
func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? { return presentAnimator }
At this time, the animation effect is like Gif.
Similarly, add a transfer animation for dismiss.
Add a new class to Animator. swift.
Class DismisssAnimator: NSObject, UIViewControllerAnimatedTransitioning {let duration = 0.6 var originFrame = CGRectZero func transitionDuration (transitionContext: UIViewControllerContextTransitioning ?) -> NSTimeInterval {return duration} func animateTransition (transitionContext: interval) {let containView = transitionContext. containerView () let toView = transitionContext. viewForKey (UITransitionContextToViewKey )! // Collection View let fromView = transitionContext. viewForKey (UITransitionContextFromViewKey )! // Full screen imageview let xScale = originFrame. size. width/toView. frame. size. width let yScale = originFrame. size. height/toView. frame. size. height containView ?. AddSubview (toView) containView ?. BringSubviewToFront (fromView) UIView. animateWithDuration (duration, animations: {()-> Void in fromView. center = CGPointMake (CGRectGetMidX (self. originFrame), CGRectGetMidY (self. originFrame) fromView. transform = CGAffineTransformMakeScale (xScale, yScale)}) {(finished)-> Void in transitionContext. completeTransition (true )}}}
Then, in ViewController. swiftdidSelectItemAtIndexPath
Set presentAnimator. frame to add
dismissAnimator.originFrame = cell.convertRect(cell.imageview.frame, toView: nil)
DismissAnimator is an attribute of ViewController.
let dismissAnimator = DismisssAnimator()
Then, in the proxy method, return
func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { return dismissAnimator; }
Final Effect