如何在基于导航的应用程序中更改推送和stream行animation

我有一个基于导航的应用程序,我想改变推动和stream行animation的animation。 我该怎么做?

我做了以下,它工作正常..和简单,易于理解..

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]; 

而推动同样的事情..


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) 

这就是我一直设法完成这个任务的方式。

推送:

 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]; 

对于stream行:

 [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]; 

我仍然从中得到很多反馈,因此我将继续进行更新,以便使用animation块,这是苹果推荐的animation方式。

推送:

 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]; }]; 

对于stream行:

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

 CATransition *transition = [CATransition animation]; transition.duration = 0.3; transition.type = kCATransitionFade; //transition.subtype = kCATransitionFromTop; [self.navigationController.view.layer addAnimation:transition forKey:kCATransition]; [self.navigationController pushViewController:ViewControllerYouWantToPush animated:NO]; 

为stream行

 CATransition *transition = [CATransition animation]; transition.duration = 0.3; transition.type = kCATransitionFade; //transition.subtype = kCATransitionFromTop; [self.navigationController.view.layer addAnimation:transition forKey:kCATransition]; [self.navigationController popViewControllerAnimated:NO]; 

@Magnus回答,只有Swift(2.0)

  let transition = CATransition() transition.duration = 0.5 transition.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut) transition.type = kCATransitionPush transition.subtype = kCATransitionFromTop self.navigationController!.view.layer.addAnimation(transition, forKey: nil) let writeView : WriteViewController = self.storyboard?.instantiateViewControllerWithIdentifier("WriteView") as! WriteViewController self.navigationController?.pushViewController(writeView, animated: false) 

一些附注:

Segue也可以做到这一点,只是在prepareForSegue执行这个或者shouldPerformSegueWithIdentifier执行shouldPerformSegueWithIdentifier不过 ,这也会保留默认的animation。 要解决这个问题,你必须去故事板,点击塞格,并取消选中“animation”框。 但是这会限制你的应用程序的IOS 9.0以上(至less当我在Xcode 7中)。

在赛季中,最后两行应该replace为:

 self.navigationController?.popViewControllerAnimated(false) 

即使我设置了错误,它也会忽略它。

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

 //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]; 

请记住,在Swift中扩展名肯定是你的朋友!

 public extension UINavigationController { /** Pop current view controller to previous view controller. - parameter type: transition animation type. - parameter duration: transition animation duration. */ func pop(transitionType type: String = kCATransitionFade, duration: CFTimeInterval = 0.3) { self.addTransition(transitionType: type, duration: duration) self.popViewControllerAnimated(false) } /** Push a new view controller on the view controllers's stack. - parameter vc: view controller to push. - parameter type: transition animation type. - parameter duration: transition animation duration. */ func push(viewController vc: UIViewController, transitionType type: String = kCATransitionFade, duration: CFTimeInterval = 0.3) { self.addTransition(transitionType: type, duration: duration) self.pushViewController(vc, animated: false) } private func addTransition(transitionType type: String = kCATransitionFade, duration: CFTimeInterval = 0.3) { let transition = CATransition() transition.duration = duration transition.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut) transition.type = type self.view.layer.addAnimation(transition, forKey: nil) } } 

有UINavigationControllerDelegate和UIViewControllerAnimatedTransitioning那里你可以改变任何你想要的animation。

例如,这是VC的垂直popup式animation:

 @objc class PopAnimator: NSObject, UIViewControllerAnimatedTransitioning { func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval { return 0.5 } func animateTransition(transitionContext: UIViewControllerContextTransitioning) { let fromViewController = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)! let toViewController = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)! let containerView = transitionContext.containerView() let bounds = UIScreen.mainScreen().bounds containerView!.insertSubview(toViewController.view, belowSubview: fromViewController.view) toViewController.view.alpha = 0.5 let finalFrameForVC = fromViewController.view.frame UIView.animateWithDuration(transitionDuration(transitionContext), animations: { fromViewController.view.frame = CGRectOffset(finalFrameForVC, 0, bounds.height) toViewController.view.alpha = 1.0 }, completion: { finished in transitionContext.completeTransition(!transitionContext.transitionWasCancelled()) }) } 

}

接着

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

有用的教程https://www.objc.io/issues/5-ios7/view-controller-transitions/

我最近试图做类似的事情。 我决定我不喜欢UINavigationController的滑动animation,但我也不想做UIView给你的animation,比如curl或类似的东西。 当我推动或popup时,我想在视图之间淡入淡出。

那里的问题涉及的事实是,视图是从字面上删除视图或popup一个在当前的顶部,所以淡出不起作用。 我来到的解决scheme涉及到我的新视图,并将其作为子视图添加到UIViewController的堆栈上的当前顶视图。 我加上一个0的alpha,然后做一个交叉淡入淡出。 当animation序列结束时,我将视图推到堆栈上,而不用animation。 然后,我回到旧的topView,并清理我改变了的东西。

它比这更复杂一点,因为你必须调整navigationItems才能使转换看起来正确。 此外,如果您进行任何旋转,则必须在将视图添加为子视图时调整帧大小,以使其在屏幕上正确显示。 这是我使用的一些代码。 我subclassed UINavigationController和overrode推和pop方法。

 -(void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated { UIViewController *currentViewController = [self.viewControllers lastObject]; //if we don't have a current controller, we just do a normal push if(currentViewController == nil) { [super pushViewController:viewController animated:animated]; return; } //if no animation was requested, we can skip the cross fade if(!animation) { [super pushViewController:viewController animated:NO]; return; } //start the cross fade. This is a tricky thing. We basically add the new view //as a subview of the current view, and do a cross fade through alpha values. //then we push the new view on the stack without animating it, so it seemlessly is there. //Finally we remove the new view that was added as a subview to the current view. viewController.view.alpha = 0.0; //we need to hold onto this value, we'll be releasing it later NSString *title = [currentViewController.title retain]; //add the view as a subview of the current view [currentViewController.view addSubview:viewController.view]; [currentViewController.view bringSubviewToFront:viewController.view]; UIBarButtonItem *rButtonItem = currentViewController.navigationItem.rightBarButtonItem; UIBarButtonItem *lButtonItem = currentViewController.navigationItem.leftBarButtonItem; NSArray *array = nil; //if we have a right bar button, we need to add it to the array, if not, we will crash when we try and assign it //so leave it out of the array we are creating to pass as the context. I always have a left bar button, so I'm not checking to see if it is nil. Its a little sloppy, but you may want to be checking for the left BarButtonItem as well. if(rButtonItem != nil) array = [[NSArray alloc] initWithObjects:currentViewController,viewController,title,lButtonItem,rButtonItem,nil]; else { array = [[NSArray alloc] initWithObjects:currentViewController,viewController,title,lButtonItem,nil]; } //remove the right bar button for our transition [currentViewController.navigationItem setRightBarButtonItem:nil animated:YES]; //remove the left bar button and create a backbarbutton looking item //[currentViewController.navigationItem setLeftBarButtonItem:nil animated:NO]; //set the back button UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithTitle:title style:kButtonStyle target:self action:@selector(goBack)]; [currentViewController.navigationItem setLeftBarButtonItem:backButton animated:YES]; [viewController.navigationItem setLeftBarButtonItem:backButton animated:NO]; [backButton release]; [currentViewController setTitle:viewController.title]; [UIView beginAnimations:@"push view" context:array]; [UIView setAnimationDidStopSelector:@selector(animationForCrossFadePushDidStop:finished:context:)]; [UIView setAnimationDelegate:self]; [UIView setAnimationDuration:0.80]; [viewController.view setAlpha: 1.0]; [UIView commitAnimations]; } -(void)animationForCrossFadePushDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context { UIViewController *c = [(NSArray*)context objectAtIndex:0]; UIViewController *n = [(NSArray*)context objectAtIndex:1]; NSString *title = [(NSArray *)context objectAtIndex:2]; UIBarButtonItem *l = [(NSArray *)context objectAtIndex:3]; UIBarButtonItem *r = nil; //not all views have a right bar button, if we look for it and it isn't in the context, //we'll crash out and not complete the method, but the program won't crash. //So, we need to check if it is there and skip it if it isn't. if([(NSArray *)context count] == 5) r = [(NSArray *)context objectAtIndex:4]; //Take the new view away from being a subview of the current view so when we go back to it //it won't be there anymore. [[[c.view subviews] lastObject] removeFromSuperview]; [c setTitle:title]; [title release]; //set the search button [c.navigationItem setLeftBarButtonItem:l animated:NO]; //set the next button if(r != nil) [c.navigationItem setRightBarButtonItem:r animated:NO]; [super pushViewController:n animated:NO]; } 

正如我在代码中提到的,我总是有一个左栏button项,所以我不检查它是否为零,然后将其放入作为animation委托的上下文传递的数组中。 如果你这样做,你可能想要做这个检查。

我发现的问题是,如果你在代理方法崩溃,它不会崩溃的程序。 它只是阻止代表完成,但你没有得到任何警告。
所以,因为我在那个代理例程中进行了清理工作,所以导致了一些奇怪的视觉行为,因为它没有完成清理工作。

我创build的后退button调用了“goBack”方法,该方法只是调用pop例程。

 -(void)goBack { [self popViewControllerAnimated:YES]; } 

另外,这是我的stream行例程。

 -(UIViewController *)popViewControllerAnimated:(BOOL)animated { //get the count for the number of viewControllers on the stack int viewCount = [[self viewControllers] count]; //get the top view controller on the stack UIViewController *topViewController = [self.viewControllers objectAtIndex:viewCount - 1]; //get the next viewController after the top one (this will be the new top one) UIViewController *newTopViewController = [self.viewControllers objectAtIndex:viewCount - 2]; //if no animation was requested, we can skip the cross fade if(!animated) { [super popViewControllerAnimated:NO]; return topViewController; } //start of the cross fade pop. A bit tricky. We need to add the new top controller //as a subview of the curent view controler with an alpha of 0. We then do a cross fade. //After that we pop the view controller off the stack without animating it. //Then the cleanup happens: if the view that was popped is not released, then we //need to remove the subview we added and change some titles back. newTopViewController.view.alpha = 0.0; [topViewController.view addSubview:newTopViewController.view]; [topViewController.view bringSubviewToFront:newTopViewController.view]; NSString *title = [topViewController.title retain]; UIBarButtonItem *lButtonItem = topViewController.navigationItem.leftBarButtonItem; UIBarButtonItem *rButtonItem = topViewController.navigationItem.rightBarButtonItem; //set the new buttons on top of the current controller from the new top controller if(newTopViewController.navigationItem.leftBarButtonItem != nil) { [topViewController.navigationItem setLeftBarButtonItem:newTopViewController.navigationItem.leftBarButtonItem animated:YES]; } if(newTopViewController.navigationItem.rightBarButtonItem != nil) { [topViewController.navigationItem setRightBarButtonItem:newTopViewController.navigationItem.rightBarButtonItem animated:YES]; } [topViewController setTitle:newTopViewController.title]; //[topViewController.navigationItem.leftBarButtonItem setTitle:newTopViewController.navigationItem.leftBarButtonItem.title]; NSArray *array = nil; if(rButtonItem != nil) array = [[NSArray alloc] initWithObjects:topViewController,title,lButtonItem,rButtonItem,nil]; else { array = [[NSArray alloc] initWithObjects:topViewController,title,lButtonItem,nil]; } [UIView beginAnimations:@"pop view" context:array]; [UIView setAnimationDidStopSelector:@selector(animationForCrossFadePopDidStop:finished:context:)]; [UIView setAnimationDelegate:self]; [UIView setAnimationDuration:0.80]; [newTopViewController.view setAlpha: 1.0]; [UIView commitAnimations]; return topViewController; } -(void)animationForCrossFadePopDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context { UIViewController *c = [(NSArray *)context objectAtIndex:0]; //UIViewController *n = [(NSArray *)context objectAtIndex:1]; NSString *title = [(NSArray *)context objectAtIndex:1]; UIBarButtonItem *l = [(NSArray *)context objectAtIndex:2]; UIBarButtonItem *r = nil; //Not all views have a right bar button. If we look for one that isn't there // we'll crash out and not complete this method, but the program will continue. //So we need to check if it is therea nd skip it if it isn't. if([(NSArray *)context count] == 4) r = [(NSArray *)context objectAtIndex:3]; //pop the current view from the stack without animation [super popViewControllerAnimated:NO]; //if what was the current veiw controller is not nil, then lets correct the changes //we made to it. if(c != nil) { //remove the subview we added for the transition [[c.view.subviews lastObject] removeFromSuperview]; //reset the title we changed c.title = title; [title release]; //replace the left bar button that we changed [c.navigationItem setLeftBarButtonItem:l animated:NO]; //if we were passed a right bar button item, replace that one as well if(r != nil) [c.navigationItem setRightBarButtonItem:r animated:NO]; else { [c.navigationItem setRightBarButtonItem:nil animated:NO]; } } } 

这是非常多的。 如果你想实现旋转,你将需要一些额外的代码。 您需要设置您添加为子视图的视图的帧大小,然后再显示它们,否则会遇到问题的方向是横向,但上一次您看到前一个视图是肖像。 所以,然后你把它作为一个子视图添加,并淡入,但它显示为肖像,然后当我们popup没有animation,相同的观点,但在堆栈中,现在是风景。 整个事情看起来有点时髦。 每个人的旋转实现都有点不同,所以我没有在这里包含我的代码。

希望它有助于一些人。 我已经找遍了这样的东西,找不到任何东西。 我不认为这是完美的答案,但在这一点上对我来说真的很好。

既然这是谷歌最重要的结果,我认为我会分享我认为是最正常的方式; 即使用iOS 7 +过渡API。 我用Swift 3实现了这个iOS 10。

UINavigationControllerUINavigationController如何在两个视图控制器之间进行animation相结合非常简单,只要创build一个UINavigationController的子类并返回一个符合UIViewControllerAnimatedTransitioning协议的类的实例即可。

例如这里是我的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中的方法,允许你返回一个自定义animation控制器(即NavigationControllerAnimation )。 这个自定义animation控制器将replace你的股票animation。

您可能想知道为什么我通过其初始化程序将操作传递给NavigationControllerAnimation实例。 我这样做,以便在NavigationControllerAnimationUIViewControllerAnimatedTransitioning协议的实现我知道什么是操作(即“推”或“stream行”)。 这有助于知道我应该做什么样的animation。 大多数情况下,您希望根据操作执行不同的animation。

其余的很标准。 在UIViewControllerAnimatedTransitioning协议中实现两个必需的函数,然后使用animation:

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

重要的是要记住,对于每种不同types的操作(即,“推”或“stream行”),来往视图控制器将是不同的。 当您正在进行推送操作时,要查看的控制器将被推送。 当你在popup操作时,视图控制器将被转换到,并从视图控制器将被popup。

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

当animation完成时,您必须调用transitionContext.completeTransition(true) 。 如果您正在进行交互式转换,则必须根据animation结束时是否完成转换,将Booldynamic返回到completeTransition(didComplete: Bool)

最后( 可选的阅读 ),你可能想看看我是如何做我正在做的转换。 这个代码有点怪异,我写得很快,所以我不会说这是很棒的animation代码,但它仍然显示如何做animation部分。

矿是一个非常简单的过渡。 我想模仿UINavigationController通常所做的相同的animation,而不是“顶层”animation的“下一页”,我想在新视图的同时实现旧视图控制器的1:1animation控制器出现。 这使得两个视图控制器看起来好像彼此固定在一起。

对于push操作来说,需要先在x轴的off屏幕上设置toViewController的视图原点,然后将其作为containerView的子视图,将origin.x设置为零, origin.x将其animation到屏幕上。 与此同时,我通过在屏幕上设置origin.x来animationfromViewController的视图:

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

stream行操作基本上是相反的。 将toViewController添加为toViewController的子视图,并在fromViewController进行animation处理时,将fromViewControllerfromViewController到右侧:

 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

下面是我在Swift中做了同样的事情:

推送:

  UIView.animateWithDuration(0.75, animations: { () -> Void in UIView.setAnimationCurve(UIViewAnimationCurve.EaseInOut) self.navigationController!.pushViewController(nextView, animated: false) UIView.setAnimationTransition(UIViewAnimationTransition.FlipFromRight, forView: self.navigationController!.view!, cache: false) }) 

对于stream行:

实际上,我对上面的一些回应做了一些改动 – 但是因为我对Swift开发很陌生,所以可能是不对的。 我已经覆盖viewWillDisappear:animated:并在其中添加了stream行代码:

  UIView.animateWithDuration(0.75, animations: { () -> Void in UIView.setAnimationCurve(UIViewAnimationCurve.EaseInOut) UIView.setAnimationTransition(UIViewAnimationTransition.FlipFromLeft, forView: self.navigationController!.view, cache: false) }) super.viewWillDisappear(animated) 

使用iJordan的答案作为灵感,为什么不简单地在UINavigationController上创build一个类别来在整个应用程序中使用,而不是在整个地方复制/粘贴这个animation代码?

UINavigationController的+ Animation.h

 @interface UINavigationController (Animation) - (void) pushViewControllerWithFlip:(UIViewController*) controller; - (void) popViewControllerWithFlip; @end 

UINavigationController的+ Animation.m

 @implementation UINavigationController (Animation) - (void) pushViewControllerWithFlip:(UIViewController *) controller { [UIView animateWithDuration:0.50 animations:^{ [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut]; [self pushViewController:controller animated:NO]; [UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight forView:self.view cache:NO]; }]; } - (void) popViewControllerWithFlip { [UIView animateWithDuration:0.5 animations:^{ [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut]; [UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight forView:self.view cache:NO]; }]; [self popViewControllerAnimated:NO]; } @end 

然后,只需导入UINavigationController + Animation.h文件并正常调用它:

 [self.navigationController pushViewControllerWithFlip:[[NewViewController alloc] init]]; [self.navigationController popViewControllerWithFlip]; 

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

推送:

  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]; }]; 

对于stream行:

  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]; }];} 

我不知道有什么方法可以公开更改过渡animation。

如果“后退”button不是必要的,你应该使用模态视图控制器 “从底部推”/“翻转”/“淡入淡出”/(≥3.2)“页面curl”转换。


私有方面,方法-pushViewController:animated:调用未logging的方法-pushViewController:transition:forceImmediate:例如,如果你想从左到右翻转,你可以使用

 [navCtrler pushViewController:ctrler transition:10 forceImmediate:NO]; 

但是,您不能通过这种方式更改“stream行”过渡。

看到我对这个问题的答案, 以less得多的代码行。 This method allows you to animate a pseudo-"Push" of a new view controller any way you like, and when the animation is done it sets up the Navigation Controller just as if you had used the standard Push method. My example lets you animate either a slide-in from the left or from the right. Code repeated here for convenience:

 -(void) showVC:(UIViewController *) nextVC rightToLeft:(BOOL) rightToLeft { [self addChildViewController:neighbor]; CGRect offscreenFrame = self.view.frame; if(rightToLeft) { offscreenFrame.origin.x = offscreenFrame.size.width * -1.0; } else if(direction == MyClimbDirectionRight) { offscreenFrame.origin.x = offscreenFrame.size.width; } [[neighbor view] setFrame:offscreenFrame]; [self.view addSubview:[neighbor view]]; [neighbor didMoveToParentViewController:self]; [UIView animateWithDuration:0.5 animations:^{ [[neighbor view] setFrame:self.view.frame]; } completion:^(BOOL finished){ [neighbor willMoveToParentViewController:nil]; [neighbor.view removeFromSuperview]; [neighbor removeFromParentViewController]; [[self navigationController] pushViewController:neighbor animated:NO]; NSMutableArray *newStack = [[[self navigationController] viewControllers] mutableCopy]; [newStack removeObjectAtIndex:1]; //self, just below top [[self navigationController] setViewControllers:newStack]; }]; } 

Have a look at ADTransitionController , a drop in replacement for UINavigationController with custom transition animations (its API matches the API of UINavigationController) that we created at Applidium.

You can use different pre-defined animations for push and pop actions such as Swipe , Fade , Cube , Carrousel , Zoom and so on.

See my more detailed answer, using only public methods, here:

Prevent the animation when clicking "Back" button in a navigation bar?

…it's imperfect (it means re-implementing some of UINavigationController) – but it doesn't use any private methods, and it works.

From the sample app, check out this variation. https://github.com/mpospese/MPFoldTransition/

 #pragma mark - UINavigationController(MPFoldTransition) @implementation UINavigationController(MPFoldTransition) //- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated - (void)pushViewController:(UIViewController *)viewController foldStyle:(MPFoldStyle)style { [MPFoldTransition transitionFromViewController:[self visibleViewController] toViewController:viewController duration:[MPFoldTransition defaultDuration] style:style completion:^(BOOL finished) { [self pushViewController:viewController animated:NO]; } ]; } - (UIViewController *)popViewControllerWithFoldStyle:(MPFoldStyle)style { UIViewController *toController = [[self viewControllers] objectAtIndex:[[self viewControllers] count] - 2]; [MPFoldTransition transitionFromViewController:[self visibleViewController] toViewController:toController duration:[MPFoldTransition defaultDuration] style:style completion:^(BOOL finished) { [self popViewControllerAnimated:NO]; } ]; return toController; } 

只要使用:

 ViewController *viewController = [[ViewController alloc] init]; UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:viewController]; navController.navigationBarHidden = YES; [self presentViewController:navController animated:YES completion: nil]; [viewController release]; [navController release]; 

Realising this is an old question. I still would like to post this answer, as I had some problems popping several viewControllers with the proposed answers. My solution is to subclass UINavigationController and override all the pop and push methods.

FlippingNavigationController.h

 @interface FlippingNavigationController : UINavigationController @end 

FlippingNavigationController.m:

 #import "FlippingNavigationController.h" #define FLIP_DURATION 0.5 @implementation FlippingNavigationController - (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated { [UIView transitionWithView:self.view duration:animated?FLIP_DURATION:0 options:UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionTransitionFlipFromRight animations:^{ [super pushViewController:viewController animated:NO]; } completion:nil]; } - (UIViewController *)popViewControllerAnimated:(BOOL)animated { return [[self popToViewController:[self.viewControllers[self.viewControllers.count - 2]] animated:animated] lastObject]; } - (NSArray *)popToRootViewControllerAnimated:(BOOL)animated { return [self popToViewController:[self.viewControllers firstObject] animated:animated]; } - (NSArray *)popToViewController:(UIViewController *)viewController animated:(BOOL)animated { __block NSArray* viewControllers = nil; [UIView transitionWithView:self.view duration:animated?FLIP_DURATION:0 options:UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionTransitionFlipFromLeft animations:^{ viewControllers = [super popToViewController:viewController animated:NO]; } completion:nil]; return viewControllers; } @end 

I found a mildly recursive way to do this that works for my purposes. I have an instance variable BOOL that I use to block the normal popping animation and substitute my own non-animated pop message. The variable is initially set to NO. When the back button is tapped, the delegate method sets it to YES and sends a new non-animated pop message to the nav bar, thereby calling the same delegate method again, this time with the variable set to YES. With the variable is set to YES, the delegate method sets it to NO and returns YES to allow the non-animated pop occur. After the second delegate call returns, we end up back in the first one, where NO is returned, blocking the original animated pop! It's actually not as messy as it sounds. My shouldPopItem method looks like this:

 - (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item { if ([[navigationBar items] indexOfObject:item] == 1) { [expandedStack restack]; } if (!progPop) { progPop = YES; [navBar popNavigationItemAnimated:NO]; return NO; } else { progPop = NO; return YES; } } 

为我工作。

You can now use UIView.transition . Note that animated:false . This works with any transition option, pop, push, or stack replace.

 if let nav = self.navigationController { UIView.transition(with:nav.view, duration:0.3, options:.transitionCrossDissolve, animations: { _ = nav.popViewController(animated:false) }, completion:nil) }