【问题标题】:How to dismiss a stack of UIViewControllers to a certain Root ViewController?如何将 ViewController 堆栈解除到某个 Route ViewController?
【发布时间】:2019-09-18 12:25:02
【问题描述】:

一些RootViewController 提供ParentViewController,而不是提供ChildViewController

如何在不再次显示ParentViewController 的情况下将ChildViewController 动画 直接发送到RootViewController


详细说明

  • 假设 some 提供了 ParentViewController,它允许用户输入一些凭据以登录到某个用户帐户。
  • 建立连接后,ParentViewController 会显示ChildViewController,向用户显示连接/帐户详细信息
  • 当用户关闭ChildViewController 时,它应该被动画(向下滑动等)关闭。但与其返回ParentViewController,不如直接返回RootViewController

当然,ParentViewController 本身并不存在ChildViewController,但(不知何故)告诉RootViewController 这也是可能的。这样从ChildViewController直接返回到RootViewController就没有问题了。然而,这不是我正在寻找的,因为RootViewController 不应该知道ChildViewController 甚至不在乎ParentViewController 是否代表其他 VC。

我正在寻找一种解决方案,其中ParentViewController 控制是否在其呈现的 VC 或其父级(= 根 VC)被解散后显示自身。

代码:

typealias CompletionBlock = () -> Void

class RootViewController: UIViewController {
    @IBAction func showParentVC(_ sender: Any) {
        let parentVC = ParentViewController()
        parentVC.completion = {
            self.dismiss(animated: true, completion: nil)
        }

        present(parentVC, animated: true)
    }
}

class ParentViewController: UIViewController {
    var completion: CompletionBlock?

    @IBAction func showChild(_ sender: Any) {
        let childVC = ChildViewController()
        childVC.completion = {
            self.completion?()
        }

        present(childVC, animated: true)
    }
}

class ChildViewController: UIViewController {
    var completion: CompletionBlock?

    @IBAction func close(_ sender: Any) {
        completion?()
    }
}

使用此代码不会解决所描述的问题。如果在ChildViewController 上调用close,则RootViewController 调用self.dismiss(animated: true, completion: nil)。这样ChildViewController 动画消失,ParentViewController 变得可见。然后ParentViewController 动画消失,RootViewController 变得可见。

如何跳过ParentViewController,在动画掉ChildViewController后直接显示RootViewController

【问题讨论】:

  • 您的解释与文档冲突。上面写着“如果您连续呈现多个视图控制器,从而构建一组呈现的视图控制器,则在堆栈中较低的视图控制器上调用此方法会关闭其直接子视图控制器以及堆栈上该子视图上方的所有视图控制器。当发生这种情况时,只有最顶层的视图会以动画方式消失;任何中间视图控制器都会简单地从堆栈中移除。
  • @ShreeramBhat 是的,我从文档中知道这段话。但是,当我使用上面发布的代码时,这不是我观察到的......这是一个错误还是文档过时或不完整(这不会太不寻常......)

标签: ios swift uiviewcontroller


【解决方案1】:

我的建议是将您的 RootViewController 嵌入到 NavigationController 中(如果您还没有的话)并为父母双方提供一个孩子

navigationController?.present(viewController, animated: true, completion: nil)
//instead of viewController.present(...)

然后你可以在你的 childViewController 中使用这个方法

navigationController?.popToRootViewController(animated: true)

【讨论】:

  • 但是如果RootViewController 不应该在UINavigationControll / 不应该有导航栏等怎么办?
【解决方案2】:

一种方法是当您将另一个 VC 呈现到呈现的 VC 的“堆栈”上时,将视图的 alpha 设置为零。

因此,正常显示“根”VC 中的第一个模态 VC。对于呈现另一个 VC 的每个“孩子”,请使用:

present(vc, animated: true, completion: {
    self.view.alpha = 0.0
})

现在,当您回调“根”VC 以关闭所有 VC 时,您将看不到中间 VC / VC 的部分 / flash。

这是一个完整的测试示例。没有 @IBOutlets@IBActions ... 只需从黑色视图控制器开始并将其自定义类分配给 MultiPresentDismissViewController

import UIKit

class MultiPresentDismissViewController: UIViewController {

    let theLabel: UILabel = {
        let v = UILabel()
        v.textAlignment = .center
        v.font = UIFont.boldSystemFont(ofSize: 40)
        v.backgroundColor = .yellow
        v.text = "\"Root\" VC"
        return v
    }()

    let showAnotherButton: UIButton = {
        let v = UIButton()
        v.backgroundColor = UIColor(white: 0.9, alpha: 1.0)
        v.setTitle("Present a VC", for: .normal)
        v.setTitleColor(.blue, for: .normal)
        v.setTitleColor(.cyan, for: .highlighted)
        return v
    }()

    let theStackView: UIStackView = {
        let v = UIStackView()
        v.axis = .vertical
        v.alignment = .fill
        v.distribution = .fill
        v.spacing = 32
        return v
    }()

    override func viewDidLoad() {
        super.viewDidLoad()

        view.backgroundColor = .white

        [theLabel, showAnotherButton].forEach {
            theStackView.addArrangedSubview($0)
            $0.layer.borderWidth = 1.0
        }

        theStackView.translatesAutoresizingMaskIntoConstraints = false

        view.addSubview(theStackView)

        NSLayoutConstraint.activate([
            theStackView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            theStackView.topAnchor.constraint(equalTo: view.topAnchor, constant: 100.0),
            theStackView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.8),
            ])

        showAnotherButton.addTarget(self, action: #selector(presentAnotherVC), for: .touchUpInside)

    }

    @objc func presentAnotherVC() -> Void {
        let vc = AnotherViewController()
        vc.myID = 1
        present(vc, animated: true, completion: nil)
    }

}

class AnotherViewController: UIViewController {

    let theLabel: UILabel = {
        let v = UILabel()
        v.textAlignment = .center
        v.font = UIFont.boldSystemFont(ofSize: 100)
        v.backgroundColor = .yellow
        return v
    }()

    let showAnotherButton: UIButton = {
        let v = UIButton()
        v.backgroundColor = UIColor(white: 0.9, alpha: 1.0)
        v.setTitle("Present Another VC", for: .normal)
        v.setTitleColor(.blue, for: .normal)
        v.setTitleColor(.cyan, for: .highlighted)
        return v
    }()

    let defaultDismissButton: UIButton = {
        let v = UIButton()
        v.backgroundColor = UIColor(white: 0.9, alpha: 1.0)
        v.setTitle("Default Dismiss All", for: .normal)
        v.setTitleColor(.blue, for: .normal)
        v.setTitleColor(.cyan, for: .highlighted)
        return v
    }()

    let theStackView: UIStackView = {
        let v = UIStackView()
        v.axis = .vertical
        v.alignment = .fill
        v.distribution = .fill
        v.spacing = 20
        return v
    }()

    var myID: Int = 0

    override func viewDidLoad() {
        super.viewDidLoad()

        view.backgroundColor = .random()

        [theLabel, showAnotherButton, defaultDismissButton].forEach {
            theStackView.addArrangedSubview($0)
            $0.layer.borderWidth = 1.0
        }

        theStackView.translatesAutoresizingMaskIntoConstraints = false

        view.addSubview(theStackView)

        NSLayoutConstraint.activate([
            theStackView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            theStackView.topAnchor.constraint(equalTo: view.topAnchor, constant: 100.0),
            theStackView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.8),
            ])

        theLabel.text = "\(myID)"

        showAnotherButton.addTarget(self, action: #selector(presentAnotherVC), for: .touchUpInside)
        defaultDismissButton.addTarget(self, action: #selector(defaultDismissAll), for: .touchUpInside)

    }

    @objc func presentAnotherVC() -> Void {
        let vc = AnotherViewController()
        vc.myID = myID + 1
        present(vc, animated: true, completion: {
            self.view.alpha = 0.0
        })
    }

    @objc func defaultDismissAll() -> Void {
        // walk up the "presenting" hierarchy to find the "root" VC
        var vc = self.presentingViewController
        while vc?.presentingViewController != nil {
            vc = vc?.presentingViewController
        }
        vc?.dismiss(animated: true, completion: nil)
    }

}

extension CGFloat {
    static func random() -> CGFloat {
        return CGFloat(arc4random()) / CGFloat(UInt32.max)
    }
}

extension UIColor {
    static func random() -> UIColor {
        return UIColor(red:   .random(),
                       green: .random(),
                       blue:  .random(),
                       alpha: 1.0)
    }
}

【讨论】:

  • 听起来很有希望,但行不通:仍然可以看到中间 VC 的闪光。不是 VC 的视图/内容,而是黑屏。
  • @AndreiHerford - 嗯......我编辑了我的答案以包含一个完整的例子。试试看?
猜你喜欢
  • 2012-12-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多