在模态视图控制器的解除动画时访问presentingViewController

Posted

技术标签:

【中文标题】在模态视图控制器的解除动画时访问presentingViewController【英文标题】:Accessing presentingViewController while dismissal animation of a Modal View Controller 【发布时间】:2017-01-25 20:28:10 【问题描述】:

我在主视图控制器中有一个按钮,它以模态方式呈现另一个视图控制器。当模态vc向上滑动时,我制作了一个主视图控制器变暗的自定义动画。但是,当模态vc向下滑动(使用dismiss)时,我怎样才能让它倒退? 使用this site 上的教程,我创建了访问呈现 vc 的协议,但动画没有发生,它只是立即改变了不透明度。

另外,如果我将动画代码放入正在呈现的 vc 的 viewWillAppear 中,它只会在模态 vc 一直向下滑动(消失)时才开始动画。我没有使用常规的 show segue,因为我认为这不是摆脱模态 vc 的最佳方法。如果我错了,请纠正我

这是演示vc中的prepareForSegue

 override func prepare(for segue: UIStoryboardSegue, sender: Any?) 
    if let identifier = segue.identifier
        switch identifier 
        case "showSettings":
            let tempFrame = CGRect(x: 0, y: 0, width: self.view.bounds.width, height: self.view.bounds.height)
            dimView = UIView(frame: tempFrame)
            dimView.backgroundColor = .black
            dimView.layer.opacity = 0
            self.view.addSubview(dimView)
            UIView.animate(withDuration: 0.5, animations: 
                self.dimView.layer.opacity = 0.4
            )
        default:break
        
    
    if let vc = segue.destination as? Dismissable
    
        vc.dismissalDelegate = self
    

这是一个 IBaction,它消除了自身内部的模态 vc。

    @IBAction func dismiss() 
    dismissalDelegate?.finishedShowing(viewController: self)
    UIView.animate(withDuration: 1.0, animations:
        (self.dismissalDelegate as! ViewController).dimView.layer.opacity = 0.3
    )

【问题讨论】:

也许你应该通过 unwindsegue 来做到这一点。谷歌一下,你会找到一些相关的教程。 感谢您的帮助,我设法使用 unwindSegue 回到演示 VC(对我来说是新技术,非常方便,谢谢 :D),但动画仍然没有发生,它不断变化呈现的 vc 视图在出现之前的不透明度。 诀窍是生成呈现 VC 的屏幕截图,并将其用作呈现 VC 的背景。然后你用那个图像做任何你想要的动画。然后,当你的动画完成后,你的展开看起来就完成了,所以你可以直接弹出没有动画的 VC。 哇,有没有办法以编程方式生成屏幕截图?我真的很怀疑,这里有一个问题,我应该如何为所有设备优化它?我现在拥有的解决方案看起来并不像预期的那样,但很接近,而且我不确定是否值得使用这个技巧。我通过委托访问呈现 vc,将其不透明度更改为 0.3,并在其 viewWillAppear 中在 0.5 秒内将其更改为 0。这几乎是好的 :D 我想我最好专注于我笨拙的应用程序的更重要的功能 为您添加一些示例代码的答案。不知道是 swift 2 还是 3,但它应该给你一些指示 【参考方案1】:

这只是如何完成的一个示例。很确定它远非完美,但应该作为一个起点。那里有很多特定于我的旧项目的代码,但可以删除。

final class ModalSeguePushStyle : UIStoryboardSegue

    override func perform() 
        let sourceVC = self.sourceViewController
        let destinationVC = self.destinationViewController
        var endPosition = destinationVC.view.frame
        var startPostition = destinationVC.view.frame

        startPostition.origin.x = startPostition.width

        if sourceVC.navigationController != nil
        
            startPostition.origin.y = -44
            endPosition.origin.y = -44
        

        destinationVC.view.frame = startPostition

        sourceVC.view.addSubview(destinationVC.view)

        UIView.animateWithDuration(0.3, animations:  () -> Void in
            destinationVC.view.frame = endPosition
            )  (Bool) -> Void in
                self.sourceViewController.presentViewController(self.destinationViewController, animated: false, completion: nil)
        
    


final class ModalSeguePushStyleUnwind : UIStoryboardSegue

    func createMockView(view: UIView) -> UIImageView 
        UIGraphicsBeginImageContextWithOptions(view.frame.size, true, UIScreen.mainScreen().scale)

        view.drawViewHierarchyInRect(view.bounds, afterScreenUpdates: true)
        let image = UIGraphicsGetImageFromCurrentImageContext()

        UIGraphicsEndImageContext()
        return UIImageView(image: image)
    

    override func perform() 
        let sourceVC = self.sourceViewController
        let destinationVC = self.destinationViewController
        var endPosition = sourceVC.view.frame

        endPosition.origin.x += endPosition.width

        var bar:UIImageView?
        let mock = createMockView(destinationVC.view)

        if destinationVC.navigationController != nil
        
            mock.frame.origin.y = 64
        

        sourceVC.view.superview!.insertSubview(mock, atIndex: 0)

        if let b = destinationVC.tabBarController?.tabBar
        
            bar = createMockView(b)
            bar?.frame = b.frame
            sourceVC.view.superview!.insertSubview(bar!, aboveSubview: mock)
        

        UIView.animateWithDuration(0.3, animations:  () -> Void in
            sourceVC.view.frame = endPosition
            )  (Bool) -> Void in
                sourceVC.dismissViewControllerAnimated(false, completion:  () -> Void in

                )
        
    

【讨论】:

以上是关于在模态视图控制器的解除动画时访问presentingViewController的主要内容,如果未能解决你的问题,请参考以下文章

当模态视图控制器被解除时如何调用函数

视图控制器解除动画参数

为啥在模态视图控制器被解除后 CAGradientLayer 从视图中删除

在模态视图被解除后,父视图控制器中是不是有一个委托被调用?

UIViewController 在解除先前呈现的模态视图控制器后被释放

如何解除模态转场,然后将转场推入新的视图控制器