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

编辑2018

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


当前回答

记住,在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)
    }

}

其他回答

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

推动:

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

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

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

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

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

从示例应用程序中,可以看到这种变化。 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;
}