我有一个基于导航的应用程序,我想更改推送和弹出动画的动画。我该怎么做?
编辑2018
这个问题有很多答案,现在已经有一段时间了,我已经重新选择了我认为现在最相关的答案。如果有人不这么认为,请在评论中告诉我
答案 0 :(得分:261)
我做了以下工作并且工作正常..并且简单易懂...
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)
答案 1 :(得分:256)
这就是我一直设法完成这项任务的方式。
推送:
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];
对于Pop:
[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];
我仍然从中获得了很多反馈,因此我将继续更新它以使用动画块,这无论如何都是Apple推荐的动画方式。
推送:
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];
}];
对于Pop:
[UIView animateWithDuration:0.75
animations:^{
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:self.navigationController.view cache:NO];
}];
[self.navigationController popViewControllerAnimated:NO];
答案 2 :(得分:29)
推送
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];
for pop
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];
答案 3 :(得分:22)
如何在基于导航的应用中更改推送和弹出动画...
序言:
假设您是iOS开发的新手。令人困惑的是,Apple提供了两个可以轻松使用的过渡。这些是:“交叉淡化”和“翻转”。
但当然,“交叉淡化”和“翻转”都是无用的。他们从未使用过。没有人知道为什么Apple提供了这两个无用的转换!
所以:
假设您要执行普通,常见,过渡,例如“幻灯片”。在这种情况下,你必须做大量的工作!。
这项工作,在这篇文章中有解释。
重复:
令人惊讶的是:对于iOS,如果您想要最简单,最常见的日常转换(例如普通幻灯片),您必须执行所有工作 完全自定义转换。
这是怎么做的......
UIViewControllerAnimatedTransitioning
你需要像popStyle
一样拥有自己的布尔。 (是弹出,还是弹出?)
您必须包含transitionDuration
(琐碎)和主要电话animateTransition
事实上, 必须 为内部animateTransition
编写两个不同的例程。一个用于推送,一个用于流行音乐。可能会将其命名为animatePush
和animatePop
。在animateTransition
内,只需将popStyle
分支到两个例程
以下示例执行简单的移动/移动
在animatePush
和animatePop
例程中。您 必须 获取“从视图”和“查看”。 (如何做到这一点,在代码示例中显示。)
您 必须 addSubview
才能使用新的“收件人”视图。
并且 必须 在动画结束时致电completeTransition
所以......
class SimpleOver: NSObject, UIViewControllerAnimatedTransitioning {
var popStyle: Bool = false
func transitionDuration(
using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.20
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
if popStyle {
animatePop(using: transitionContext)
return
}
let fz = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from)!
let tz = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)!
let f = transitionContext.finalFrame(for: tz)
let fOff = f.offsetBy(dx: f.width, dy: 55)
tz.view.frame = fOff
transitionContext.containerView.insertSubview(tz.view, aboveSubview: fz.view)
UIView.animate(
withDuration: transitionDuration(using: transitionContext),
animations: {
tz.view.frame = f
}, completion: {_ in
transitionContext.completeTransition(true)
})
}
func animatePop(using transitionContext: UIViewControllerContextTransitioning) {
let fz = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from)!
let tz = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)!
let f = transitionContext.initialFrame(for: fz)
let fOffPop = f.offsetBy(dx: f.width, dy: 55)
transitionContext.containerView.insertSubview(tz.view, belowSubview: fz.view)
UIView.animate(
withDuration: transitionDuration(using: transitionContext),
animations: {
fz.view.frame = fOffPop
}, completion: {_ in
transitionContext.completeTransition(true)
})
}
}
然后......
注意:奇怪的是, 只需要在“第一个”视图控制器中执行此操作 。 (那个“在下面”的那个。)
使用顶部上显示的那个,执行无。容易。
所以你的班级......
class SomeScreen: UIViewController {
}
...变为
class FrontScreen: UIViewController,
UIViewControllerTransitioningDelegate, UINavigationControllerDelegate {
let simpleOver = SimpleOver()
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.delegate = self
}
func navigationController(
_ navigationController: UINavigationController,
animationControllerFor operation: UINavigationControllerOperation,
from fromVC: UIViewController,
to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
simpleOver.popStyle = (operation == .pop)
return simpleOver
}
}
就是这样。
正常推送和弹出,没有变化。推动......
let n = UIStoryboard(name: "nextScreenStoryboardName", bundle: nil)
.instantiateViewController(withIdentifier: "nextScreenStoryboardID")
as! NextScreen
navigationController?.pushViewController(n, animated: true)
然后弹出它,如果您愿意,可以在下一个屏幕上执行此操作:
class NextScreen: TotallyOrdinaryUIViewController {
@IBAction func userClickedBackOrDismissOrSomethingLikeThat() {
navigationController?.popViewController(animated: true)
}
}
呼。
滚动到@AlanZeino和@elias回答,了解如何在iOS应用中AnimatedTransitioning
如何进行更多讨论!
答案 4 :(得分:18)
@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
中实现此功能即可。 然而,这也将保留默认动画。要解决此问题,您必须转到故事板,单击Segue,然后取消选中“Animates'”框。但这会限制你的IOS 9.0及以上版本的应用程序(至少我在Xcode 7中做到了这一点)。
在segue中进行操作时,最后两行应替换为:
self.navigationController?.popViewControllerAnimated(false)
即使我设置了假,也有点忽略它。
答案 5 :(得分:15)
请记住,在 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)
}
}
答案 6 :(得分:10)
使用私人电话是一个坏主意,因为Apple不再批准那样做的应用程序。 也许你可以试试这个:
//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];
答案 7 :(得分:8)
由于这是Google的最高成绩,我认为我会分享我认为最健全的方式;这是使用iOS 7+转换API。我使用Swift 3为iOS 10实现了这个。
如果您创建UINavigationController
的子类并返回符合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
中允许您返回自定义动画控制器的方法(即{{1} }})。此自定义动画控制器将替换您的股票动画。
您可能想知道为什么我通过其初始化程序将操作传递给NavigationControllerAnimation
实例。我这样做是为了在NavigationControllerAnimation
NavigationControllerAnimation
协议的实现中我知道操作是什么(即'push'或'pop')。这有助于了解我应该做什么样的动画。大多数情况下,您希望根据操作执行不同的动画。
其余的很标准。在UIViewControllerAnimatedTransitioning
协议中实现两个必需的函数,然后按照您喜欢的方式设置动画:
UIViewControllerAnimatedTransitioning
重要的是要记住,对于每种不同类型的操作(即“推”或“弹出”),来往视图控制器将是不同的。当您处于推送操作时,视图控制器将被推送。当您处于弹出操作时,视图控制器将是正在转换到的控制器,并且视图控制器将是正在弹出的控制器。
此外,必须将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
}
}
}
视图控制器添加为转换上下文中to
的子视图。
动画完成后,您必须致电containerView
。如果您正在进行交互式转换,则必须动态地将transitionContext.completeTransition(true)
返回到Bool
,具体取决于动画结束时转换是否完成。
最后(可选阅读),您可能想看看我是如何完成我正在进行的转换。这段代码有点笨拙,我写的很快,所以我不会说它是伟大的动画代码,但它仍然显示了如何做动画部分。
我的过渡非常简单;我想模仿UINavigationController通常所做的相同动画,但是我想要在新视图的同时实现旧视图控制器的1:1动画,而不是它的“顶部下一页”动画。控制器出现。这样可以使两个视图控制器看起来像彼此固定一样。
对于推送操作,需要首先在x轴关闭屏幕上设置completeTransition(didComplete: Bool)
的视图原点,将其添加为toViewController
的子视图,通过设置该动画将其设置为屏幕上的动画containerView
为零。同时,我通过在屏幕上设置origin.x
来为fromViewController
的视图添加动画效果:
origin.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
以下是整个swift文件的要点:
https://gist.github.com/alanzeino/603293f9da5cd0b7f6b60dc20bc766be
答案 8 :(得分:6)
在那里有UINavigationControllerDelegate和UIViewControllerAnimatedTransition,您可以根据需要更改动画。
例如,这是VC的垂直弹出动画:
@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/
答案 9 :(得分:5)
基于更新为swift 4的jordanperry
answer
推送UIViewController
let yourVC = self.storyboard?.instantiateViewController(withIdentifier: "yourViewController") as! yourViewController
UIView.animate(withDuration: 0.75, animations: {() -> Void in
UIView.setAnimationCurve(.easeInOut)
self.navigationController?.pushViewController(terms, animated: true)
UIView.setAnimationTransition(.flipFromRight, for: (self.navigationController?.view)!, cache: false)
})
对于Pop
UIView.animate(withDuration: 0.75, animations: {() -> Void in
UIView.setAnimationCurve(.easeInOut)
UIView.setAnimationTransition(.flipFromLeft, for: (self.navigationController?.view)!, cache: false)
})
navigationController?.popViewController(animated: false)
答案 10 :(得分:5)
以下是我在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)
})
对于Pop:
我实际上对上面的一些响应做了一点点不同 - 但由于我是Swift开发的新手,它可能不对。我已覆盖viewWillDisappear:animated:
并在其中添加了pop代码:
UIView.animateWithDuration(0.75, animations: { () -> Void in
UIView.setAnimationCurve(UIViewAnimationCurve.EaseInOut)
UIView.setAnimationTransition(UIViewAnimationTransition.FlipFromLeft, forView: self.navigationController!.view, cache: false)
})
super.viewWillDisappear(animated)
答案 11 :(得分:4)
我最近试图做类似的事情。我决定不喜欢UINavigationController的滑动动画,但我也不想做UIView给你的动画像curl或类似的东西。当我推或弹时,我想在视图之间进行交叉淡入淡出。
那里的问题涉及这样的事实:视图实际上是删除视图或在当前视图的顶部弹出一个视图,因此淡入淡出不起作用。我参与的解决方案包括获取我的新视图并将其作为子视图添加到UIViewController堆栈上的当前顶视图。我用alpha为0添加它,然后进行交叉渐变。当动画序列完成时,我将视图推送到堆栈而不动画。然后我回到旧的topView并清理我已经改变的东西。
它比这更复杂,因为你必须调整navigationItems以使转换看起来正确。此外,如果进行任何旋转,则必须在将视图添加为子视图时调整帧大小,以便它们在屏幕上正确显示。这是我使用的一些代码。我将UINavigationController子类化,并覆盖push和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];
}
正如我在代码中提到的,我总是有一个左栏按钮项,所以在将它作为动画委托的上下文传递给数组之前,我不检查它是否为nil。如果您这样做,您可能想要进行检查。
我发现的问题是,如果你在委托方法中崩溃,它不会崩溃程序。它只是阻止代表完成但你没有收到任何警告 所以,因为我在该委托例程中进行了清理,所以它导致了一些奇怪的视觉行为,因为它没有完成清理。
我创建的后退按钮调用“goBack”方法,该方法只调用pop例程。
-(void)goBack
{
[self popViewControllerAnimated:YES];
}
此外,这是我的流行例程。
-(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];
}
}
}
这就是它。如果要实现旋转,则需要一些额外的代码。您需要在显示之前设置您添加为子视图的视图的帧大小,否则您将遇到方向为横向的问题,但上次您看到上一个视图时它是纵向的。所以,然后你将它添加为子视图并淡入它但它显示为肖像,然后当我们弹出没有动画时,相同的视图,但堆栈中的那个,现在是风景。整件事看起来有点时髦。每个人的轮换实现都有点不同,所以我没有在这里包含我的代码。
希望它可以帮助一些人。我一直在寻找这样的东西,却找不到任何东西。我认为这不是一个完美的答案,但在这一点上它对我来说真的很有效。
答案 12 :(得分:3)
使用iJordan的答案作为灵感,为什么不简单地在UINavigationController上创建一个分类,以便在整个应用中使用,而不是在整个地方复制/粘贴这个动画代码?
的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];
答案 13 :(得分:3)
您现在可以使用UIView.transition
。请注意animated:false
。这适用于任何转换选项,弹出,推送或堆栈替换。
if let nav = self.navigationController
{
UIView.transition(with:nav.view, duration:0.3, options:.transitionCrossDissolve, animations: {
_ = nav.popViewController(animated:false)
}, completion:nil)
}
答案 14 :(得分:2)
看看ADTransitionController,我们在Applidium创建的自定义转换动画(它的API与UINavigationController的API匹配)是UINavigationController的替代品。
您可以为推送和流行操作使用不同的预定义动画,例如刷卡,淡化, 多维数据集,卡鲁塞尔,缩放等。
答案 15 :(得分:2)
虽然这里的所有答案都很棒,而且大部分工作都很顺利,但是有一种稍微简单的方法可以达到同样的效果......
推送:
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];
}];
对于Pop:
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];
}];}
答案 16 :(得分:2)
非常简单
self.navigationController?.view.semanticContentAttribute = .forceRightToLeft
答案 17 :(得分:1)
请参阅我的回答to this question,了解在更少的代码行中执行此操作的方法。此方法允许您以任何方式为新视图控制器的伪“推”设置动画,并且在动画完成后,它将设置导航控制器,就像您使用标准Push方法一样。我的示例允许您从左侧或右侧为幻灯片设置动画。 为方便起见,此处重复了代码:
-(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];
}];
}
答案 18 :(得分:1)
@Luca Davanzo在 Swift 4.2
中的回答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: CATransitionType = .fade, duration: CFTimeInterval = 0.3) {
self.addTransition(transitionType: type, duration: duration)
self.popViewController(animated: 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: CATransitionType = .fade, duration: CFTimeInterval = 0.3) {
self.addTransition(transitionType: type, duration: duration)
self.pushViewController(vc, animated: false)
}
private func addTransition(transitionType type: CATransitionType = .fade, duration: CFTimeInterval = 0.3) {
let transition = CATransition()
transition.duration = duration
transition.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
transition.type = type
self.view.layer.add(transition, forKey: nil)
}
}
答案 19 :(得分:0)
我不知道您可以公开更改过渡动画的任何方式。
如果不需要“后退”按钮,应使用modal view controllers进行“从底部推”/“翻转”/“淡入淡出”/(≥3.2)“页面卷曲“过渡。
在私有方面,方法-pushViewController:animated:
会调用未记录的方法-pushViewController:transition:forceImmediate:
,例如如果您想要从左到右翻转,可以使用
[navCtrler pushViewController:ctrler transition:10 forceImmediate:NO];
但是,你不能以这种方式改变“弹出”过渡。
答案 20 :(得分:0)
意识到这是一个老问题。我仍然想发布这个答案,因为我有一些问题突然出现了几个viewControllers
的答案。我的解决方案是子类UINavigationController
并覆盖所有pop和push方法。
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
答案 21 :(得分:0)
只需使用:
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];
答案 22 :(得分:0)
从示例应用中,查看此变体。 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;
}
答案 23 :(得分:0)
我知道这个线程很旧,但是我以为我要花两美分。您不需要制作自定义动画,这是一种简单的方法(也许很笨拙)。代替使用推,创建一个新的导航控制器,使新的视图控制器成为该导航控制器的根视图控制器,然后从原始导航控制器中显示该导航控制器。 Present可以很容易地自定义为多种样式,而无需制作自定义动画。
例如:
Sub Macro15()
'
' Macro15 Macro
'
' Keyboard Shortcut: Ctrl+Shift+Q
'
Sheets("MACRO (insert data)").Select
Range("G4:Q4").Select
Selection.Copy
Sheets("Jun-2019").Select
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
Sheets("MACRO (insert data)").Select
Range("W4:AG5").Select
Selection.Copy
Sheets("Jun-2019").Select
Range("C42").Select
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
Range("O10:Y10").Select
Selection.Copy
Startcell.Offset(0, 11).Select
Selection.PasteSpecial Paste:=xlPasteFormulas, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
End Sub
您可以根据需要更改演示样式。
答案 24 :(得分:-1)
我找到了一种温和的递归方式来实现这个目的。我有一个实例变量BOOL,用于阻止正常的弹出动画并替换我自己的非动画弹出消息。该变量最初设置为NO。当点击后退按钮时,委托方法将其设置为YES并向导航栏发送新的非动画弹出消息,从而再次调用相同的委托方法,这次将变量设置为YES。将变量设置为YES时,委托方法将其设置为NO并返回YES以允许发生非动画弹出。第二个委托调用返回后,我们最终返回第一个,返回NO,阻止原始动画弹出!它实际上没有听起来那么混乱。我的shouldPopItem方法如下所示:
- (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;
}
}
适合我。