UIViewController transfer animation, uiviewcontroller
UIViewControllerAnimatedTransitioning
In UINavigationController, how does one use UIViewControllerAnimatedTransitioning?
Reference: How to use UIViewControllerAnimatedTransitioning with UINavigationController?
1. The FromViewController of the animation must follow the UINavigationControllerDelegate protocol.
2. In FromViewController, a custom transition animation object is returned in the following proxy method:
- (id <UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC { TransitionAnimator *animator = [TransitionAnimator new]; animator.presenting = (operation == UINavigationControllerOperationPush); return animator; }
3. To create a custom animation class, follow the UIViewControllerContextTransitioning Protocol and implement the following proxy methods:
-(NSTimeInterval) transitionDuration :( id <UIViewControllerContextTransitioning>) transitionContext {// return the animation execution time return 0.5f;}-(void) animateTransition :( id <interval>) transitionContext {// Grab the from and to view controllers from the contextUIViewController * fromViewController = [transitionContext viewControllerForKey: Secret]; UIViewController * toViewController = [transitionContext viewControllerForKey: Secret]; // Set our ending frame. we'll modify this later if we have toCGRect endFrame = CGRectMake (80,280,160,100); if (self. presenting) {fromViewController. view. userInteractionEnabled = NO; [transitionContext. containerView addSubview: fromViewController. view]; [transitionContext. containerView addSubview: toViewController. view]; CGRect startFrame = endFrame; startFrame. origin. x ++ = 320; toViewController. view. frame = startFrame; [UIView animateWithDuration: [self transitionDuration: transitionContext] animations: ^ {fromViewController. view. tintAdjustmentMode = UIViewTintAdjustmentModeDimmed; toViewController. view. frame = endFrame;} completion: ^ (BOOL finished) {[transitionContext completeTransition: YES] ;}] ;} else {toViewController. view. userInteractionEnabled = YES; [transitionContext. containerView addSubview: toViewController. view]; [transitionContext. containerView addSubview: fromViewController. view]; endFrame. origin. x ++ = 320; [UIView animateWithDuration: [self transitionDuration: transitionContext] animations: ^ {toViewController. view. tintAdjustmentMode = UIViewTintAdjustmentModeAutomatic; fromViewController. view. frame = endFrame;} completion: ^ (BOOL finished) {[transitionContext completeTransition: YES] ;}}}
For more information about UIViewControllerAnimatedTransitioning, see:
- Easy to learn-iOS uses the Runtime custom controller POP gesture Animation
- New Features of iOS 7: View Controller switching API
- ViewController's advanced transfer Animation
- View Controller Transfer
The following content is from
Introduction to Custom View Controller Transitions and Animations
To create a custom transition, follow these three steps:
- Create a class that implements the UIViewControllerAnimatedTransitioning protocol. Here you will write code that performs the animation. This class is referred to as the animation controller.
- Before presenting a view controller, set a class as its transitioning delegate. The delegate will get a callback for the animation controller to be used when presenting the view controller.
- Implement the callback method to return an instance of the animation controller from the first step.
Custom Present Transition (Custom Present Transition)
First, create an animation controller. CreateCustomPresentAnimationControllerClass inherited from NSObject
class CustomPresentAnimationController: NSObject, UIViewControllerAnimatedTransitioning {
The UIViewControllerAnimatedTransitioning protocol has two required methods.
func transitionDuration(transitionContext: UIViewControllerContextTransitioning) -> NSTimeInterval { return 2.5}func animateTransition(transitionContext: UIViewControllerContextTransitioning) { let fromViewController = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)! let toViewController = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)! let finalFrameForVC = transitionContext.finalFrameForViewController(toViewController) let containerView = transitionContext.containerView() let bounds = UIScreen.mainScreen().bounds toViewController.view.frame = CGRectOffset(finalFrameForVC, 0, bounds.size.height) containerView.addSubview(toViewController.view) UIView.animateWithDuration(transitionDuration(transitionContext), delay: 0.0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.0, options: .CurveLinear, animations: { fromViewController.view.alpha = 0.5 toViewController.view.frame = finalFrameForVC }, completion: { finished in transitionContext.completeTransition(true) fromViewController.view.alpha = 1.0 })}
The current controller must follow the UIViewControllerTransitioningDelegate protocol.
class ItemsTableViewController: UITableViewController, UIViewControllerTransitioningDelegate {
UIViewController has a transitionDelegate attribute that supports custom transitions. When a View controller transitions to another View controller, the system checks this attribute to determine whether to use a custom transition. The custom transition is provided by UIViewControllerTransitioningDelegate.
let customPresentAnimationController = CustomPresentAnimationController()override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { if segue.identifier == "showAction" { let toViewController = segue.destinationViewController as UIViewController toViewController.transitioningDelegate = self }}func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? { return customPresentAnimationController}
If you want a different effect, you can use the code in CustomPresentAnimationController. swift:
toViewController.view.frame = CGRectOffset(finalFrameForVC, 0, bounds.size.height)
Change the origin position of the View controller frame to the following format:
toViewController.view.frame = CGRectOffset(finalFrameForVC, 0, -bounds.size.height)
Custom Dismiss Transition (Custom Dismiss Transition)
UIViewControllerTransitioningDelegateYou can also specify an animation controller to dismiss a view controller.
Create a CustomDismissAnimationController class that inherits from NSObject. As follows:
class CustomDismissAnimationController: NSObject, UIViewControllerAnimatedTransitioning {
Add the following code to this class:
func transitionDuration(transitionContext: UIViewControllerContextTransitioning) -> NSTimeInterval { return 2}func animateTransition(transitionContext: UIViewControllerContextTransitioning) { let fromViewController = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)! let toViewController = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)! let finalFrameForVC = transitionContext.finalFrameForViewController(toViewController) let containerView = transitionContext.containerView() toViewController.view.frame = finalFrameForVC toViewController.view.alpha = 0.5 containerView.addSubview(toViewController.view) containerView.sendSubviewToBack(toViewController.view) UIView.animateWithDuration(transitionDuration(transitionContext), animations: { fromViewController.view.frame = CGRectInset(fromViewController.view.frame, fromViewController.view.frame.size.width / 2, fromViewController.view.frame.size.height / 2) toViewController.view.alpha = 1.0 }, completion: { finished in transitionContext.completeTransition(true) })}
Add the following attributes to ItemsTableViewController:
let customDismissAnimationController = CustomDismissAnimationController()
Add the following methods to the class:
func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { return customDismissAnimationController}
UIViewControllerTransitioningDelegateThe Protocol provides the above method, which is used to obtain the animation controller of the dismiss view controller.
The running result is as follows:
Animation effects are not what we want. The reason is that changing the frame of a view does not affect its subview. You can use snapshotting of UIView to modify it.
The snapsho of UIView is a screenshot of UIView, making it a lightweight UIView. We will use this screenshot in the animation process, rather than the real view.
Replace the animateTransition () function with the following form:
func animateTransition(transitionContext: UIViewControllerContextTransitioning) { let fromViewController = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)! let toViewController = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)! let finalFrameForVC = transitionContext.finalFrameForViewController(toViewController) let containerView = transitionContext.containerView() toViewController.view.frame = finalFrameForVC toViewController.view.alpha = 0.5 containerView.addSubview(toViewController.view) containerView.sendSubviewToBack(toViewController.view) let snapshotView = fromViewController.view.snapshotViewAfterScreenUpdates(false) snapshotView.frame = fromViewController.view.frame containerView.addSubview(snapshotView) fromViewController.view.removeFromSuperview() UIView.animateWithDuration(transitionDuration(transitionContext), animations: { snapshotView.frame = CGRectInset(fromViewController.view.frame, fromViewController.view.frame.size.width / 2, fromViewController.view.frame.size.height / 2) toViewController.view.alpha = 1.0 }, completion: { finished in snapshotView.removeFromSuperview() transitionContext.completeTransition(true) }) }
Navigation controller transition)
The custom mnmnavigationanimationcontroller class inherits from NSObject, as follows:
class CustomNavigationAnimationController: NSObject, UIViewControllerAnimatedTransitioning {
Add the following content to this class. Use a simple cube animation for this animation controller. A reverse variable is used to determine the animation direction.
var reverse: Bool = falsefunc transitionDuration(transitionContext: UIViewControllerContextTransitioning) -> NSTimeInterval { return 1.5}func animateTransition(transitionContext: UIViewControllerContextTransitioning) { let containerView = transitionContext.containerView() let toViewController = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)! let fromViewController = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)! let toView = toViewController.view let fromView = fromViewController.view let direction: CGFloat = reverse ? -1 : 1 let const: CGFloat = -0.005 toView.layer.anchorPoint = CGPointMake(direction == 1 ? 0 : 1, 0.5) fromView.layer.anchorPoint = CGPointMake(direction == 1 ? 1 : 0, 0.5) var viewFromTransform: CATransform3D = CATransform3DMakeRotation(direction * CGFloat(M_PI_2), 0.0, 1.0, 0.0) var viewToTransform: CATransform3D = CATransform3DMakeRotation(-direction * CGFloat(M_PI_2), 0.0, 1.0, 0.0) viewFromTransform.m34 = const viewToTransform.m34 = const containerView.transform = CGAffineTransformMakeTranslation(direction * containerView.frame.size.width / 2.0, 0) toView.layer.transform = viewToTransform containerView.addSubview(toView) UIView.animateWithDuration(transitionDuration(transitionContext), animations: { containerView.transform = CGAffineTransformMakeTranslation(-direction * containerView.frame.size.width / 2.0, 0) fromView.layer.transform = viewFromTransform toView.layer.transform = CATransform3DIdentity }, completion: { finished in containerView.transform = CGAffineTransformIdentity fromView.layer.transform = CATransform3DIdentity toView.layer.transform = CATransform3DIdentity fromView.layer.anchorPoint = CGPointMake(0.5, 0.5) toView.layer.anchorPoint = CGPointMake(0.5, 0.5) if (transitionContext.transitionWasCancelled()) { toView.removeFromSuperview() } else { fromView.removeFromSuperview() } transitionContext.completeTransition(!transitionContext.transitionWasCancelled()) }) }
Open ItemsTableViewController. swift and modify the class declaration as follows:
class ItemsTableViewController: UITableViewController, UIViewControllerTransitioningDelegate, UINavigationControllerDelegate {
ByUINavigationControllerDelegateTo provide the animation controller.
Add the following attributes to the class.
let customNavigationAnimationController = CustomNavigationAnimationController()
Add the following code to viewDidLoad:
navigationController?.delegate = self
Add the following method to the class:
func navigationController(navigationController: UINavigationController, animationControllerForOperation operation: UINavigationControllerOperation, fromViewController fromVC: UIViewController, toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? { customNavigationAnimationController.reverse = operation == .Pop return customNavigationAnimationController}
Making it Interactive
IOS built-in apps have this feature
We need an interaction controller. Interaction controller uses the UIViewControllerInteractiveTransitioning protocol. Navigation controller delegate or transitioning delegate. After requesting an animation controller, requests also has an optional interaction controller.
To create an interaction controller. Create a new class named CustomInteractionController and inherit from UIPercentDrivenInteractiveTransition.
UIPercentDrivenInteractiveTransition implements the UIViewControllerInteractiveTransitioning protocol.
To use UIPercentDrivenInteractiveTransition, your animation controller must have a UIView animation so that the animation can be stopped, restored, and played.
Add the following code to this class:
var navigationController: UINavigationController!var shouldCompleteTransition = falsevar transitionInProgress = falsevar completionSeed: CGFloat { return 1 - percentComplete}func attachToViewController(viewController: UIViewController) { navigationController = viewController.navigationController setupGestureRecognizer(viewController.view)}private func setupGestureRecognizer(view: UIView) { view.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: "handlePanGesture:"))}func handlePanGesture(gestureRecognizer: UIPanGestureRecognizer) { let viewTranslation = gestureRecognizer.translationInView(gestureRecognizer.view!.superview!) switch gestureRecognizer.state { case .Began: transitionInProgress = true navigationController.popViewControllerAnimated(true) case .Changed: var const = CGFloat(fminf(fmaxf(Float(viewTranslation.x / 200.0), 0.0), 1.0)) shouldCompleteTransition = const > 0.5 updateInteractiveTransition(const) case .Cancelled, .Ended: transitionInProgress = false if !shouldCompleteTransition || gestureRecognizer.state == .Cancelled { cancelInteractiveTransition() } else { finishInteractiveTransition() } default: println("Swift switch must be exhaustive, thus the default") }}
Use interaction controller and add the following attributes to ItemsTableViewController. swift:
let customInteractionController = CustomInteractionController()
In navigationController (_: animationControllerForOperation:
FromViewController: toViewController :) method start position, add the following code:
if operation == .Push { customInteractionController.attachToViewController(toVC)}
Then add the following method:
func navigationController(navigationController: UINavigationController, interactionControllerForAnimationController animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? { return customInteractionController.transitionInProgress ? customInteractionController : nil}
Download the completed project here
Other references:
- How To Make A View Controller Transition Animation Like in the Ping App
- CREATING A CUSTOM FLIP VIEW CONTROLLER TRANSITION
- INTERACTIVE TRANSITIONS
- IOS7 interactive transitions
- Custom UIViewController Transitions
Another open-source tool is recommended to achieve slide return in projects:
Fdfullscreenpopgsture