【问题标题】:iOS ViewController modalPresentationStyle, opaque background and viewWillAppear from presenting ViewControlleriOS ViewController modalPresentationStyle, opaque background 和 viewWillAppear from presenting ViewController
【发布时间】:2021-12-15 22:34:43
【问题描述】:

我被这个问题困扰了一段时间。 我阅读了很多关于不同 modalPresentationStyle、何时使用每种以及每种如何影响视图层次结构的信息。 例如,要在另一个 ViewController (VC1) 上显示某个 ViewController (VC2) 并具有透明背景,应使用:

    modalPresentationStyle = .overCurrentContext/.overFullScreen

默认情况下具有不透明背景并且将 VC2 的背景颜色分配为清除将是不透明的。

问题是我丢失了 ViewContoller 层次结构调用。例如 viewWillAppear 将不会在呈现的 ViewController (VC1) 上被调用,我需要使用某种 hacky 解决方案来通知 VC1 上述控制器已被解除。

但是当我使用允许使用 ViewController 层次结构调用的选项时:

    modalPresentationStyle = .fullScreen

我失去了不透明和不透明的能力......

我知道我可以使用委托并基本上通知他们,但我使用协调器模式将导航和演示从 ViewController 中抽象出来,并再次要求我以某种方式(通知/称为特定方法)通知 VC1,我想知道是否可能避免。

推送和使用 NavigaitonController 也无济于事...

我也知道我可以使用UIAdaptivePresentationControllerDelegate,但同样需要在协调员之间共享特定知识,如果可能的话,我不想共享这些知识。另外因为我从代码中解散了控制器并且它不会被调用

我缺少任何建议或 API?

我找到的最好的解释在这里 - explain

我一直在阅读的参考资料:

link-1link-2link-3link-4link-5link-6link-7link-8link-9link-10

【问题讨论】:

    标签: ios swift uiviewcontroller uikit


    【解决方案1】:

    作为一般规则...viewWillAppear()与“视图将再次可见”相同。

    viewWillAppear() 是视图控制器生命周期的一部分。最有可能的是,在那里执行不同的(或额外的)代码,而不是在关闭呈现的控制器时。

    您可以尝试的一件事是让您的呈现控制器符合UIViewControllerTransitioningDelegate,并实现:

    func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? 
    

    这是一些示例代码(请注意:它是仅示例代码,而不是“生产就绪”):

    class PresentTestViewController: UIViewController, UIViewControllerTransitioningDelegate {
        
        let infoLabel: UILabel = {
            let v = UILabel()
            v.backgroundColor = .yellow
            v.numberOfLines = 0
            return v
        }()
        let presentButton: UIButton = {
            let v = UIButton()
            v.setTitle("Test Present", for: [])
            v.setTitleColor(.white, for: .normal)
            v.setTitleColor(.lightGray, for: .highlighted)
            v.backgroundColor = .systemRed
            return v
        }()
        
        var presentCount: Int = 0
        var dismissCount: Int = 0
        var dismissReason: String = ""
        
        override func viewDidLoad() {
            super.viewDidLoad()
            
            [infoLabel, presentButton].forEach { v in
                v.translatesAutoresizingMaskIntoConstraints = false
                view.addSubview(v)
            }
            
            let g = view.safeAreaLayoutGuide
            NSLayoutConstraint.activate([
            
                // put the button at the top
                presentButton.topAnchor.constraint(equalTo: g.topAnchor, constant: 8.0),
                presentButton.centerXAnchor.constraint(equalTo: g.centerXAnchor),
                presentButton.widthAnchor.constraint(equalTo: g.widthAnchor, multiplier: 0.7),
                
                // put the info label below the present button
                infoLabel.topAnchor.constraint(equalTo: presentButton.bottomAnchor, constant: 20.0),
                infoLabel.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
                infoLabel.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
    
            ])
            
            presentButton.addTarget(self, action: #selector(doPresent(_:)), for: .touchUpInside)
            
            view.backgroundColor = .white
            
        }
        
        override func viewWillAppear(_ animated: Bool) {
            super.viewWillAppear(animated)
            
            // we're probably doing some view setup tasks here
            //  that we don't want to ALSO do when a
            //  presented VC is dismissed
            
            // call UI update func
            myViewWillAppear()
        }
        
        func myViewWillAppear(fromDismiss: Bool = false) -> Void {
            if !fromDismiss {
                infoLabel.text = "Info Label"
            } else {
                var str = infoLabel.text ?? ""
                str += "\n" + "Dismiss Count: \(dismissCount) Reason: \(dismissReason)"
                infoLabel.text = str
            }
        }
        
        func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
            dismissCount += 1
            if let vc = dismissed as? PresentMeViewController {
                self.dismissReason = vc.dismissReason
            }
            myViewWillAppear(fromDismiss: true)
            return nil
        }
        
        @IBAction func doPresent(_ sender: Any) {
    
            presentCount += 1
            var str = infoLabel.text ?? ""
            str += "\n" + "Present Count: \(presentCount)"
            infoLabel.text = str
            
            let vc = PresentMeViewController()
    
            vc.modalPresentationStyle = .automatic
            
            // set transitioningDelegate
            vc.transitioningDelegate = self
            present(vc, animated: true, completion: nil)
    
        }
        
    }
    
    class PresentMeViewController: UIViewController {
        
        private let containerView: UIView = {
            let v = UIView()
            v.backgroundColor = .white
            v.layer.borderColor = UIColor.blue.cgColor
            v.layer.borderWidth = 2
            v.layer.cornerRadius = 16
            return v
        }()
        private let stackView: UIStackView = {
            let v = UIStackView()
            v.axis = .vertical
            v.spacing = 80
            return v
        }()
        private let testLabel: UILabel = {
            let v = UILabel()
            v.backgroundColor = .green
            v.textAlignment = .center
            v.numberOfLines = 0
            v.text = "This is a label in a stack view in the view controller that will be presented."
            return v
        }()
        private let dismissButton: UIButton = {
            let v = UIButton()
            v.setTitle("Dismiss Me", for: [])
            v.setTitleColor(.white, for: .normal)
            v.setTitleColor(.lightGray, for: .highlighted)
            v.backgroundColor = .systemBlue
            return v
        }()
        
        private var timer: Timer!
        
        public var dismissReason: String = ""
        
        override func viewDidLoad() {
            super.viewDidLoad()
            
            [stackView, containerView].forEach { v in
                v.translatesAutoresizingMaskIntoConstraints = false
            }
            
            stackView.addArrangedSubview(testLabel)
            stackView.addArrangedSubview(dismissButton)
            
            containerView.addSubview(stackView)
            
            view.addSubview(containerView)
            
            let g = view.safeAreaLayoutGuide
            NSLayoutConstraint.activate([
                
                containerView.centerXAnchor.constraint(equalTo: g.centerXAnchor),
                containerView.centerYAnchor.constraint(equalTo: g.centerYAnchor),
                containerView.widthAnchor.constraint(equalTo: g.widthAnchor, multiplier: 0.7),
                
                stackView.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 20.0),
                stackView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 20.0),
                stackView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -20.0),
                stackView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: -20.0),
    
            ])
            
            // dismiss if no action after 5 seconds
            timer = Timer.scheduledTimer(timeInterval: 5.0, target: self, selector: #selector(gotTimeout), userInfo: nil, repeats: false)
    
            // dismiss on button tap
            dismissButton.addTarget(self, action: #selector(doDismiss(_:)), for: .touchUpInside)
            
            // transparent / translucent background
            if self.presentingViewController != nil {
                view.backgroundColor = UIColor.gray.withAlphaComponent(0.25)
            } else {
                view.backgroundColor = .systemYellow
            }
            
            // this will change if Timer times-out or
            //  Dismiss button is tapped
            dismissReason = "Dragged"
        }
        
        @objc func gotTimeout() {
            dismissReason = "Timeout"
            dismiss(animated: true, completion: nil)
        }
        
        @objc func doDismiss(_ sender: Any) {
            dismissReason = "Button Tap"
            dismiss(animated: true, completion: nil)
        }
        
        // if the timer is still valid (i.e. has not "timed out")
        //  cancel the timer
        override func viewWillDisappear(_ animated: Bool) {
            if timer != nil {
                timer.invalidate()
            }
            super.viewWillDisappear(animated)
        }
        
    }
    

    PresentTestViewController 开头是这样的:

    每次我们点击“Test Present”按钮时,我们的presentCount 将递增,“信息标签”将更新,我们将创建PresentMeViewController 的一个实例,设置它的.transitioningDelegate,然后展示它:

    如果我们“向下拖动”、点击按钮或 5 秒计时器超时,我们将更新 dismissReason 变量并关闭 VC。

    回到PresentTestViewController,我们对animationController(forDismissed dismissed: UIViewController)的实现将增加dismissCount,获取解雇的原因,并通过调用myViewWillAppear(fromDismiss: true)更新UI:

    【讨论】:

    • 感谢@DonMag,这是我尝试过的一种方法,确实没有帮助。问题是,一旦你设置了modalPresentationStyle = .fullScreen,它就不支持 ViewControler 生命周期,我必须将信息从 PresentMeVC 传递给其他人......
    猜你喜欢
    • 1970-01-01
    • 2016-06-26
    • 1970-01-01
    • 1970-01-01
    • 2012-05-10
    • 1970-01-01
    • 1970-01-01
    • 2020-01-04
    • 2011-10-15
    相关资源
    最近更新 更多