我有一个基于导航的应用程序,我想改变推送和弹出动画的动画。我该怎么做呢?

编辑2018

这个问题有很多答案,现在已经有很长一段时间了,我重新选择了我认为最相关的答案。如果有人有不同的想法,请在评论中告诉我


当前回答

虽然这里所有的答案都很棒,而且大多数工作得很好,但有一个稍微简单一点的方法可以达到同样的效果……

推动:

  NextViewController *nextViewController = [[NextViewController alloc] init];

  // Shift the view to take the status bar into account 
  CGRect frame = nextViewController.view.frame;
  frame.origin.y -= 20;
  frame.size.height += 20;
  nextViewController.view.frame = frame;

  [UIView transitionFromView:self.navigationController.topViewController.view toView:nextViewController.view duration:0.5 options:UIViewAnimationOptionTransitionFlipFromRight completion:^(BOOL finished) {
    [self.navigationController pushViewController:nextViewController animated:NO];
  }];

流行:

  int numViewControllers = self.navigationController.viewControllers.count;
  UIView *nextView = [[self.navigationController.viewControllers objectAtIndex:numViewControllers - 2] view];

  [UIView transitionFromView:self.navigationController.topViewController.view toView:nextView duration:0.5 options:UIViewAnimationOptionTransitionFlipFromLeft completion:^(BOOL finished) {
    [self.navigationController popViewControllerAnimated:NO];
  }];}

其他回答

我做了以下工作,它很好..并且简单易懂。

CATransition* transition = [CATransition animation];
transition.duration = 0.5;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.type = kCATransitionFade; //kCATransitionMoveIn; //, kCATransitionPush, kCATransitionReveal, kCATransitionFade
//transition.subtype = kCATransitionFromTop; //kCATransitionFromLeft, kCATransitionFromRight, kCATransitionFromTop, kCATransitionFromBottom
[self.navigationController.view.layer addAnimation:transition forKey:nil];
[[self navigationController] popViewControllerAnimated:NO];

push也是一样。


Swift 3.0版本:

let transition = CATransition()
transition.duration = 0.5
transition.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
transition.type = kCATransitionFade
self.navigationController?.view.layer.add(transition, forKey: nil)
_ = self.navigationController?.popToRootViewController(animated: false)

由于这是谷歌上的排名第一的结果,我想我要分享我认为是最理智的方式;也就是使用ios7 +过渡API。我用Swift 3在iOS 10上实现了这个功能。

如果你创建UINavigationController的子类并返回一个符合uiviewcontrolleranimatedtransioning协议的类的实例,那么将这个与UINavigationController如何在两个视图控制器之间动画结合起来是非常简单的。

例如,这是我的UINavigationController子类:

class NavigationController: UINavigationController {
    init() {
        super.init(nibName: nil, bundle: nil)

        delegate = self
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

extension NavigationController: UINavigationControllerDelegate {

    public func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return NavigationControllerAnimation(operation: operation)
    }

}

你可以看到,我设置了UINavigationControllerDelegate本身,在我的子类的扩展中,我实现了UINavigationControllerDelegate中的方法,它允许你返回一个自定义动画控制器(即,NavigationControllerAnimation)。这个自定义动画控制器将取代库存动画为您。

您可能想知道为什么我通过NavigationControllerAnimation实例的初始化器将操作传递给它。我这样做是为了在NavigationControllerAnimation的uiviewcontrolleranimatedtransiating协议的实现中,我知道操作是什么(即“push”或“pop”)。这有助于知道我应该做什么样的动画。大多数情况下,您希望根据操作执行不同的动画。

其余的都是标准的。在uiviewcontrolleranimatedtransiating协议中实现两个必需的函数,并按你喜欢的方式进行动画:

class NavigationControllerAnimation: NSObject, UIViewControllerAnimatedTransitioning {

    let operation: UINavigationControllerOperation

    init(operation: UINavigationControllerOperation) {
        self.operation = operation

        super.init()
    }

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

    public func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        guard let fromViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from),
            let toViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to) else { return }
        let containerView = transitionContext.containerView

        if operation == .push {
            // do your animation for push
        } else if operation == .pop {
            // do your animation for pop
        }
    }
}

重要的是要记住,对于每个不同类型的操作(例如,“push”或“pop”),to和from视图控制器将是不同的。当你在进行推送操作时,to视图控制器将是被推送的那个。当你在弹出操作中,to视图控制器将是被转换到的那个,而from视图控制器将是被弹出的那个。

另外,to视图控制器必须作为转换上下文中containerView的子视图添加。

当你的动画完成时,你必须调用transitionContext.completeTransition(true)。如果你正在做一个交互式转换,你将不得不动态返回一个Bool到completeTransition(didComplete: Bool),这取决于转换是否在动画结束时完成。

最后(可选阅读),您可能想看看我是如何完成我正在进行的转换的。这段代码有点粗糙,我写得很快,所以我不会说它是伟大的动画代码,但它仍然显示了如何做动画部分。

我的转变非常简单;我想模仿UINavigationController通常做的相同的动画,但不是“下一页上方”动画,我想在新视图控制器出现的同时实现旧视图控制器的1:1动画。这使得两个视图控制器看起来好像是固定在一起的。

对于push操作,首先需要在屏幕外的x轴上设置toViewController的视图原点,将其添加为containerView的子视图,通过设置该原点将其动画化到屏幕上。X到0。与此同时,我通过设置fromViewController的原点来使它动画化。X离开屏幕:

toViewController.view.frame = containerView.bounds.offsetBy(dx: containerView.frame.size.width, dy: 0.0)

containerView.addSubview(toViewController.view)

UIView.animate(withDuration: transitionDuration(using: transitionContext),
               delay: 0,
               options: [ UIViewAnimationOptions.curveEaseOut ],
               animations: {
                toViewController.view.frame = containerView.bounds
                fromViewController.view.frame = containerView.bounds.offsetBy(dx: -containerView.frame.size.width, dy: 0)
},
               completion: { (finished) in
                transitionContext.completeTransition(true)
})

弹出操作基本上是相反的。添加toViewController作为containerView的子视图,并将fromViewController动画移到右边,就像你从左边动画toViewController一样:

containerView.addSubview(toViewController.view)

UIView.animate(withDuration: transitionDuration(using: transitionContext),
               delay: 0,
               options: [ UIViewAnimationOptions.curveEaseOut ],
               animations: {
                fromViewController.view.frame = containerView.bounds.offsetBy(dx: containerView.frame.width, dy: 0)
                toViewController.view.frame = containerView.bounds
},
               completion: { (finished) in
                transitionContext.completeTransition(true)
})

以下是整个swift文件的要点:

https://gist.github.com/alanzeino/603293f9da5cd0b7f6b60dc20bc766be

我知道这个帖子很旧了,但我想我应该发表我的意见。你不需要制作一个自定义动画,有一个简单(可能是hack)的方法来做它。不使用push,而是创建一个新的导航控制器,使新的视图控制器成为那个nav控制器的根视图控制器,然后从原始的nav控制器中呈现出nav控制器。现在是很容易自定义的许多风格,不需要做一个自定义动画。

例如:

UIViewcontroller viewControllerYouWantToPush = UIViewController()
UINavigationController newNavController = UINavigationController(root: viewControllerYouWantToView)
newNavController.navBarHidden = YES;
self.navigationController.present(newNavController)

你可以随心所欲地改变演示风格。

这就是我总是设法完成这项任务的方法。

推动:

MainView *nextView=[[MainView alloc] init];
[UIView  beginAnimations:nil context:NULL];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationDuration:0.75];
[self.navigationController pushViewController:nextView animated:NO];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight forView:self.navigationController.view cache:NO];
[UIView commitAnimations];
[nextView release];

流行:

[UIView  beginAnimations:nil context:NULL];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationDuration:0.75];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:self.navigationController.view cache:NO];
[UIView commitAnimations];

[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDelay:0.375];
[self.navigationController popViewControllerAnimated:NO];
[UIView commitAnimations];

我还是从这里得到了很多反馈所以我将继续更新它,使用动画块这是苹果推荐的制作动画的方法。 推动:

MainView *nextView = [[MainView alloc] init];
[UIView animateWithDuration:0.75
                         animations:^{
                             [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
                             [self.navigationController pushViewController:nextView animated:NO];
                             [UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight forView:self.navigationController.view cache:NO];
                         }];

流行:

[UIView animateWithDuration:0.75
                         animations:^{
                             [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
                             [UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:self.navigationController.view cache:NO];
                         }];
[self.navigationController popViewControllerAnimated:NO];

使用私人通话是一个坏主意,因为苹果不再批准这样做的应用程序。 也许你可以试试这个:

//Init Animation
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration: 0.50];


[UIView setAnimationTransition:UIViewAnimationTransitionCurlUp forView:self.navigationController.view cache:YES];

//Create ViewController
MyViewController *myVC = [[MyViewController alloc] initWith...];

[self.navigationController pushViewController:myVC animated:NO];
[myVC release];

//Start Animation
[UIView commitAnimations];