swift 4 - segue 动画完成后如何在目标视图控制器中运行代码?

Posted

技术标签:

【中文标题】swift 4 - segue 动画完成后如何在目标视图控制器中运行代码?【英文标题】:swift 4 - how do I run code in the destination view controller AFTER the segue animation is finished? 【发布时间】:2018-04-22 16:14:08 【问题描述】:

所以我有 2 个视图控制器,我想通过自定义动画从 view controller 1view controller 2。这是我的自定义动画的代码:

let transition = CATransition()
transition.duration = 0.5
transition.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionDefault)
transition.type = kCATransitionPush
transition.subtype = kCATransitionFromRight
self.view.window?.layer.add(transition, forKey: nil)

我在调用 performSegue() 之前运行它,它可以工作。但我想做的是在 view controller 2 的代码中,我想在 segue 动画完成后运行一些东西(所以在 0.5 秒后)。我的视图控制器不是导航控制器的一部分,所以this post 没有帮助。我还希望我的代码位于 目标视图控制器 中,但 this post 将它放在 源视图控制器 中,所以这也无济于事。

我尝试过测试viewDidLoad()viewDidAppear(),但它们都在滑动动画完成之前运行。请帮忙,谢谢!

【问题讨论】:

CATransaction.setCompletionBlock(completion) 顺便说一句,如果你想要实现的只是一个推弹式导航,但没有导航栏,你显然可以考虑只使用导航控制器,但指定它的导航栏应该是隐藏... 【参考方案1】:

当你正确地为你的过渡设置动画时,viewDidAppear 将在动画完成时被调用。请参阅 ios 视图控制器编程指南中的Customizing Transition Animations 了解有关自定义两个视图控制器之间转换的正确方法的说明。

正如该指南所说,当您想要自定义模态转换时,您应该指定 .custommodalPresentationStyle,然后指定将提供的 transitioningDelegate

演示控制器; 用于呈现模态的动画控制器;和 用于关闭模式的动画控制器

例如,目标视图控制器会指定它将执行自定义转换:

class ViewController: UIViewController 

    // if storyboards, override `init(coder:)`; if NIBs or programmatically 
    // created view controllers, override the appropriate `init` method.

    required init?(coder aDecoder: NSCoder) 
        super.init(coder: aDecoder)

        transitioningDelegate = self
        modalPresentationStyle = .custom
    

    ...


并且,在其UIViewControllerTransitioningDelegate 中,它出售了演示控制器和动画控制器:

extension ViewController: UIViewControllerTransitioningDelegate 
    func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? 
        return TransitionAnimator(operation: .present)
    

    func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? 
        return TransitionAnimator(operation: .dismiss)
    

    func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? 
        return PresentationController(presentedViewController: presented, presenting: presenting)
    

演示控制器所做的只是指定演示者的视图应该在转换完成时从视图层次结构中删除(这是经验法则,除非演示视图是半透明的或不覆盖整个屏幕):

class PresentationController: UIPresentationController 
    override var shouldRemovePresentersView: Bool  return true 

动画师指定动画的持续时间和特定细节:

class TransitionAnimator: NSObject 

    enum TransitionOperation 
        case present
        case dismiss
    

    private let operation: TransitionOperation

    init(operation: TransitionOperation) 
        self.operation = operation
        super.init()
    


extension TransitionAnimator: UIViewControllerAnimatedTransitioning 
    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval 
        return 2.0
    

    func animateTransition(using context: UIViewControllerContextTransitioning) 
        let toVC = context.viewController(forKey: .to)!
        let fromVC = context.viewController(forKey: .from)!
        let container = context.containerView

        let frame = fromVC.view.frame
        let rightFrame = CGRect(origin: CGPoint(x: frame.origin.x + frame.width, y: frame.origin.y), size: frame.size)
        let leftFrame = CGRect(origin: CGPoint(x: frame.origin.x - frame.width, y: frame.origin.y), size: frame.size)

        switch operation 
        case .present:
            toVC.view.frame = rightFrame
            container.addSubview(toVC.view)
            UIView.animate(withDuration: transitionDuration(using: context), animations: 
                toVC.view.frame = frame
                fromVC.view.frame = leftFrame
            , completion:  finished in
                fromVC.view.frame = frame
                context.completeTransition(!context.transitionWasCancelled)
            )

        case .dismiss:
            toVC.view.frame = leftFrame
            container.addSubview(toVC.view)
            UIView.animate(withDuration: transitionDuration(using: context), animations: 
                toVC.view.frame = frame
                fromVC.view.frame = rightFrame
            , completion:  finished in
                fromVC.view.frame = frame
                context.completeTransition(!context.transitionWasCancelled)
            )
        
    

显然,你可以做任何你想做的动画,但希望你能得到基本的想法。底线,目标视图控制器应该指定它的transitioningDelegate,然后你可以做一个标准的模态演示(通过presentshow或只是一个segue),你的过渡动画将被定制,你的目的地的@动画完成后会调用 987654339@。

【讨论】:

这适用于 segues 吗? Apple Documentation 暗示这应该在 UIStoryboardSegue 的子类中完成。

以上是关于swift 4 - segue 动画完成后如何在目标视图控制器中运行代码?的主要内容,如果未能解决你的问题,请参考以下文章

如何在swift3中完成5个文件下载请求后执行segue

没有故事板的过渡动画 segue swift

如何在swift中打开动画完成的另一个视图?

如何在 Swift 上从最后一个 VC 更改为中间 VC

使用segue按下按钮后禁用PageViewController - Swift 4

动画后执行 Segue,但动画项目回到之前的位置