UIViewController轉場動畫,uiviewcontroller

來源:互聯網
上載者:User

UIViewController轉場動畫,uiviewcontroller
UIViewControllerAnimatedTransitioning

在UINavigationController中如何使用UIViewControllerAnimatedTransitioning?
參考:How to use UIViewControllerAnimatedTransitioning with UINavigationController?

1.動畫的FromViewController要遵循UINavigationControllerDelegate協議。
2.FromViewController中,在如下的代理方法中返回自訂的轉場動畫對象:

    - (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.建立自訂的動畫類,需遵循UIViewControllerContextTransitioning協議,並實現如下的代理方法:

- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext {    //返回動畫執行的時間    return 0.5f;    }- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext     {// Grab the from and to view controllers from the contextUIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];// 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];    }];}}

關於UIViewControllerAnimatedTransitioning的資料可參考:

  • 輕鬆學習之二——iOS利用Runtime自訂控制器POP手勢動畫
  • iOS 7 新特性:視圖控制器切換API
  • ViewController的進階轉場動畫
  • View Controller 轉場

以下內容來自
Introduction to Custom View Controller Transitions and Animations
建立自訂的transition,按如下的3步:

  • 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.
自訂Present過渡(Custom Present Transition)

首先建立一個animation controller。如下,建立一個CustomPresentAnimationController 類繼承自NSObject

class CustomPresentAnimationController: NSObject, UIViewControllerAnimatedTransitioning {

UIViewControllerAnimatedTransitioning協議有兩個必須實現的方法。

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    })}

當前的控制器要遵循UIViewControllerTransitioningDelegate協議

class ItemsTableViewController: UITableViewController, UIViewControllerTransitioningDelegate {

UIViewController有一個transitionDelegate屬性,支援自訂的transitions。當一個View controller 過渡到另一個View controller的時候,系統會檢查這個屬性來決定是否使用自訂的transition。由UIViewControllerTransitioningDelegate來提供這個自訂的transition。

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}

如果想要一個不同的效果,可以把CustomPresentAnimationController.swift中的代碼:

toViewController.view.frame = CGRectOffset(finalFrameForVC, 0, bounds.size.height)

改為如下的形式,把View controller的frame的原點位置改在螢幕的上面:

toViewController.view.frame = CGRectOffset(finalFrameForVC, 0, -bounds.size.height)

自訂Dismiss過渡(Custom Dismiss Transition)

UIViewControllerTransitioningDelegate 也允許指定一個animation controller來dismiss一個view controller。
建立一個CustomDismissAnimationController類,繼承自NSObject。如下:

class CustomDismissAnimationController: NSObject, UIViewControllerAnimatedTransitioning {

在這個類中添加如下的代碼:

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)    })}

在ItemsTableViewController中加入如下的屬性:

let customDismissAnimationController = CustomDismissAnimationController()

並在類中添加如下的方法:

func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {    return customDismissAnimationController}

UIViewControllerTransitioningDelegate 協議提供了如上的方法,這個方法用來擷取dismiss view controller的animation controller。
啟動並執行效果如下:

動畫效果並不是我們想要的。原因是改變view的frame並不會對其子view有影響。可以使用UIView的snapshotting來修改它。
UIView的snapshotting是UIView的截屏,使其成為輕量級的UIView。我們將會在動畫過程中使用這個截屏,而不是真正的view。

把animateTransition()函數替換成如下形式:

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 transitions)

自訂CustomNavigationAnimationController類繼承自NSObject,如下:

class CustomNavigationAnimationController: NSObject, UIViewControllerAnimatedTransitioning {

把這個類中添加如下的內容。給這個animation controller使用一個簡單的立方體動畫。使用了一個reverse變數,用來決定動畫的方向。

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())    })        }

開啟ItemsTableViewController.swift,修改類的聲明如下:

class ItemsTableViewController: UITableViewController, UIViewControllerTransitioningDelegate, UINavigationControllerDelegate {

UINavigationControllerDelegate 來提供animation controller。
添加如下的屬性到類中。

let customNavigationAnimationController = CustomNavigationAnimationController()

在viewDidLoad()中添加如下的代碼:

navigationController?.delegate = self

在類中添加如下的方法:

func navigationController(navigationController: UINavigationController, animationControllerForOperation operation: UINavigationControllerOperation, fromViewController fromVC: UIViewController, toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {    customNavigationAnimationController.reverse = operation == .Pop    return customNavigationAnimationController}

Making it Interactive

iOS內建的app有這個特徵

我們需要一個interaction controller。interaction controller使用UIViewControllerInteractiveTransitioning協議。navigation controller delegate或者transitioning delegate,在requesting一個animation controller之後還會requests 一個可選的 interaction controller。
來建立interaction controller。建立一個新的類叫做CustomInteractionController,繼承自UIPercentDrivenInteractiveTransition。
UIPercentDrivenInteractiveTransition實現了UIViewControllerInteractiveTransitioning協議。
要想使用UIPercentDrivenInteractiveTransition,你的animation controller必須有一個UIView動畫,這樣這個動畫可以被停止、恢複和播放。
在這個類中加入如下的代碼:

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")    }}

使用interaction controller,在ItemsTableViewController.swift,加入如下的屬性:

let customInteractionController = CustomInteractionController()

在navigationController(_:animationControllerForOperation:
fromViewController:toViewController:)方法的開始位置,加入如下的代碼:

if operation == .Push {    customInteractionController.attachToViewController(toVC)}

然後再加入如下的方法:

func navigationController(navigationController: UINavigationController, interactionControllerForAnimationController animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {    return customInteractionController.transitionInProgress ? customInteractionController : nil}

download the completed project here

其它參考文章:

  • 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

另推薦一個開源工具,可以很好的在項目中實現滑動返回:
FDFullscreenPopGesture

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.