如何在基于导航的应用程序中更改Push和Pop动画

Jab*_*Jab 213 animation uinavigationcontroller ios

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

编辑2018

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

小智 264

我做了以下,它工作正常..而且简单易懂.

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];
Run Code Online (Sandbox Code Playgroud)

推送同样的事情..


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)
Run Code Online (Sandbox Code Playgroud)

  • +1,这确实是最理智的解决方案.对未来的访客来说只是一个小小的注意事项:"动画:不"部分至关重要.如果通过"是",则动画会混合并产生有趣的效果. (34认同)
  • 到目前为止最好的解决方案..对于初学者,不要忘记包含QuartCore(#import <QuartzCore/QuartzCore.h>) (12认同)
  • 我对此代码的问题是每个视图在滑入或滑出时都会闪烁为灰色或白色. (9认同)
  • 这个解决方案唯一的问题是推送的viewcontroller的viewDidAppear在我没有动画的情况下立即被调用.有办法解决吗? (4认同)
  • 老实说,这个七年前的答案并不是一个解决方案。这是 SO 上的案例之一,其中确实行不通的“快速修复”获得了很多支持!不幸的是,自那时以来,iOS 发生了巨大的变化,它确实不可行。 (3认同)
  • 再次强调,对于任何在这里谷歌搜索的人来说,这个 8 年前的答案现在**不起作用**。我每一两个月都会更新下面的答案(参见“2018”),以便它具有最新的 Swift 语法。现在有更多使用正确的现代方法的答案。这是一个很好的例子,说明了一些答案如何变得非常非常过时。 (3认同)

jor*_*rry 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];
Run Code Online (Sandbox Code Playgroud)

对于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];
Run Code Online (Sandbox Code Playgroud)


我还是从这个,所以我要继续前进,并更新它使用动画块这是苹果推荐的方法做动画反正得到很多反馈.

推送:

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];
                         }];
Run Code Online (Sandbox Code Playgroud)

对于Pop:

[UIView animateWithDuration:0.75
                         animations:^{
                             [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
                             [UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:self.navigationController.view cache:NO];
                         }];
[self.navigationController popViewControllerAnimated:NO];
Run Code Online (Sandbox Code Playgroud)

  • 谢谢你.但是弹出是由UINavigationController自动完成的.如何覆盖该行为,以便您可以调用自定义弹出逻辑? (3认同)
  • 为什么给UIViewController子类一个不带“ ViewController”部分的名称。此名称更适合UIView。 (2认同)

Ted*_*Ted 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];
Run Code Online (Sandbox Code Playgroud)

对于流行音乐

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];
Run Code Online (Sandbox Code Playgroud)


Fat*_*tie 26

如何在基于导航的应用程序中更改Push和Pop动画...

对于2018年,错误的2019年,"最终答案!"

序言 假设您是iOS开发的新手,可能来自Android.令人困惑的是,Apple提供了两个(也就是两个)转换,可以轻松使用.这些是:"交叉淡化"和"翻转".现在,两个最无用的过渡是"交叉淡入淡出"和"翻转"!没有人知道为什么Apple提供这两个无用的转换作为内置转换!所以:令人惊讶的是,如果你想做最普通的过渡,比如"滑落",你必须做大量的工作.这项工作,在这篇文章中解释.

  • 注1:要使用Apple提供的两个中的一个(交叉淡入淡出,翻转),请在此页面上查看@PeterDeWeese的答案.

  • 注2:几年前有一个快速而又肮脏的CATransition准转换.这是行不通的.

重复一遍:

令人惊讶的是:对于iOS,如果您只想进行最简单的转换,例如普通的滑动/滑动转换,则必须实现完整的自定义转换.

这是怎么做的......

你需要一个习惯 UIViewControllerAnimatedTransitioning

  1. 你需要一个属于你自己的布尔popStyle.(是弹出,还是弹出?)

  2. 你必须包括transitionDuration(琐碎)和主要的电话,animateTransition

  3. 实际上你必须为内部编写两个不同的例程animateTransition.一个用于推送,一个用于流行音乐.可能会命名他们animatePushanimatePop.在里面animateTransition,只是分支popStyle到两个例程

  4. 下面的例子做了一个简单的移动/移动

  5. 在你animatePushanimatePop日常生活中.你必须得到"从视图"和"查看".(如何做到这一点,在代码示例中显示.)

  6. 必须 addSubview为新的"到"视图.

  7. 必须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)
            })
        }
    }
Run Code Online (Sandbox Code Playgroud)

然后 ...

2.在视图控制器中使用它.

注意:奇怪的是,您只需要在"第一个"视图控制器中执行此操作.(那个"在下面".)

这样,您就弹出一个顶部,做什么.简单.

所以你的班级......

class SomeScreen: UIViewController {
}
Run Code Online (Sandbox Code Playgroud)

成为...

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
    }
}
Run Code Online (Sandbox Code Playgroud)

而已.

完全正常推送和弹出,没有变化.要推......

let n = UIStoryboard(name: "nextScreenStoryboardName", bundle: nil)
          .instantiateViewController(withIdentifier: "nextScreenStoryboardID")
          as! NextScreen
navigationController?.pushViewController(n, animated: true)
Run Code Online (Sandbox Code Playgroud)

如果您愿意,可以在下一个屏幕上执行此操作:

class NextScreen: TotallyOrdinaryUIViewController {

    @IBAction func userClickedBackOrDismissOrSomethingLikeThat() {

        navigationController?.popViewController(animated: true)
    }
}
Run Code Online (Sandbox Code Playgroud)

"就这么简单."


Cul*_*tes 19

@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)
Run Code Online (Sandbox Code Playgroud)

一些旁注:

您也可以使用Segue执行此操作,只需在prepareForSegue或中执行此操作即可shouldPerformSegueWithIdentifier.但是,这也将保留默认动画.要解决此问题,您必须转到故事板,单击Segue,然后取消选中"Animates"框.但这会限制你的IOS 9.0及以上版本的应用程序(至少我在Xcode 7中做过).

在segue中进行操作时,最后两行应替换为:

self.navigationController?.popViewControllerAnimated(false)
Run Code Online (Sandbox Code Playgroud)

即使我设置了假,它也忽略了它.


Luc*_*nzo 16

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

}
Run Code Online (Sandbox Code Playgroud)


nic*_*mro 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];
Run Code Online (Sandbox Code Playgroud)


Ala*_*ino 9

由于这是谷歌的最高成绩,我认为我会分享我认为最理智的方式; 这是使用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)
    }

}
Run Code Online (Sandbox Code Playgroud)

你可以看到我设置了UINavigationControllerDelegate自己,并在我的子类的扩展中,我实现了方法UINavigationControllerDelegate,允许你返回一个自定义动画控制器(即NavigationControllerAnimation).此自定义动画控制器将替换您的股票动画.

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

其余的很标准.在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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

重要的是要记住,对于每种不同类型的操作(即"推"或"弹出"),来往视图控制器将是不同的.当您处于推送操作时,视图控制器将被推送.当您处于弹出操作时,视图控制器将是正在转换到的控制器,并且视图控制器将是正在弹出的控制器.

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

动画完成后,您必须致电transitionContext.completeTransition(true).如果你正在做一个互动的转变,你必须动态地返回BoolcompleteTransition(didComplete: Bool),这取决于如果过渡是在动画的最后完成.

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

我的过渡非常简单; 我想模仿UINavigationController通常所做的相同动画,但是我想要在新视图的同时实现旧视图控制器的1:1动画,而不是它的"顶部下一页"动画.控制器出现.这具有使两个视图控制器看起来好像彼此固定的效果.

对于推动操作,需要首先toViewController在x轴关闭屏幕上设置视图原点,将其添加为子视图,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)
})
Run Code Online (Sandbox Code Playgroud)

流行操作基本上是相反的.添加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)
})
Run Code Online (Sandbox Code Playgroud)

这是整个swift文件的要点:

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


小智 8

这很简单

self.navigationController?.view.semanticContentAttribute = .forceRightToLeft
Run Code Online (Sandbox Code Playgroud)


eil*_*las 7

有UINavigationControllerDelegate和UIViewControllerAnimatedTransitioning,您可以根据需要更改动画.

例如,这是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())
    })
}
Run Code Online (Sandbox Code Playgroud)

}

然后

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

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


vp2*_*698 6

根据 swift 4更新的答案jordanperry

推动 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)
})
Run Code Online (Sandbox Code Playgroud)

对于流行音乐

UIView.animate(withDuration: 0.75, animations: {() -> Void in
    UIView.setAnimationCurve(.easeInOut)
    UIView.setAnimationTransition(.flipFromLeft, for: (self.navigationController?.view)!, cache: false)
})
navigationController?.popViewController(animated: false)
Run Code Online (Sandbox Code Playgroud)


djb*_*jbp 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)
    })
Run Code Online (Sandbox Code Playgroud)

对于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)
Run Code Online (Sandbox Code Playgroud)


Pet*_*ese 5

您现在可以使用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)
}
Run Code Online (Sandbox Code Playgroud)


Ser*_*ens 5

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

}
Run Code Online (Sandbox Code Playgroud)