简体   繁体   English

iOS ViewController modalPresentationStyle、不透明背景和 viewWillAppear 来自呈现的 ViewController

[英]iOS ViewController modalPresentationStyle, opaque background and viewWillAppear from presenting ViewController

I'm strangling with this questions for some time.我被这个问题扼杀了一段时间。 I read a lot about the different modalPresentationStyle, when to use each and how each one affect the view hierarchy.我阅读了很多关于不同 modalPresentationStyle、何时使用每个以及每个如何影响视图层次结构的内容。 For example, to show some ViewController (VC2) over another ViewController (VC1) and to have a transparent background, one shall use:例如,要在另一个 ViewController (VC1) 上显示某个 ViewController (VC2) 并具有透明背景,应使用:

    modalPresentationStyle = .overCurrentContext/.overFullScreen

Which have an opaque background by default and with assigning the background color of VC2 to clear will be opaque.默认情况下具有不透明背景并且将 VC2 的背景颜色指定为 clear 将是不透明的。

The issue is that then I lose the ViewContoller hierarchy calls.问题是我丢失了 ViewContoller 层次结构调用。 For example viewWillAppear will not be called on the presenting ViewController (VC1), and I need to use some kind of a hacky solution to notify VC1 that the above controller was dismissed.例如 viewWillAppear 不会在呈现的 ViewController (VC1) 上被调用,我需要使用某种hacky 解决方案来通知 VC1 上述控制器已被解除。

But when I use the option that allows to utilize the ViewController hierarchy calls:但是当我使用允许使用 ViewController 层次结构调用的选项时:

    modalPresentationStyle = .fullScreen

I loose the opaque and opacity abilities...我失去了不透明和不透明的能力......

I know I can use delegates and basically notify them but I use Coordinators pattern which abstract the navigation and presentation away from the ViewControllers and again requires me to notify VC1 in some way (notification/called specific method) which I wonder if possible to avoid.我知道我可以使用委托并基本上通知他们,但我使用协调器模式,它将导航和演示从 ViewControllers 中抽象出来,并再次要求我以某种方式(通知/调用的特定方法)通知 VC1,我想知道是否可以避免这种情况。

Pushing and using NavigaitonController does not help as well...推送和使用 NavigaitonController 也无济于事......

I'm also aware to the fact that I can use UIAdaptivePresentationControllerDelegate but again, it will require specific knowledge to be shared between coordinators that I wish not to share if possible.我也知道我可以使用UIAdaptivePresentationControllerDelegate但同样,它需要在协调员之间共享特定的知识,如果可能的话,我不希望共享这些知识。 In addition for the fact that I dismiss the controller from the code and it will not be called此外,我从代码中关闭了控制器并且不会调用它

Any suggestions or API that I'm missing?我缺少任何建议或 API?

The best explanation I found is here - explain我找到的最好的解释在这里 - 解释

References I have been reading through:我一直在阅读的参考资料:

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

As a general rule... viewWillAppear() is not the same things as "view will become visible again."作为一般规则... viewWillAppear()一样的东西“的观点将再次变得可见。”

viewWillAppear() is part of the view controller lifecycle. viewWillAppear()是视图控制器生命周期的一部分。 Most likely, one would be executing different (or additional) code there, as opposed to when a presented controller is dismissed.最有可能的是,一个人会在那里执行不同的(或额外的)代码,而不是在呈现的控制器被解除时。

One thing you could try is to have your presenting controller conform to UIViewControllerTransitioningDelegate , and implement:您可以尝试的一件事是让您的呈现控制器符合UIViewControllerTransitioningDelegate ,并实现:

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

Here's some example code (please note: it's Example Code Only , not "Production Ready"):这是一些示例代码(请注意:它是仅示例代码,而不是“生产就绪”):

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

The PresentTestViewController starts like this: PresentTestViewController像这样开始:

在此处输入图片说明

Each time we tap the "Test Present" button, our presentCount will be incremented, the "Info Label" will be updated, we'll create an instance of our PresentMeViewController , set its .transitioningDelegate , and present it:每次我们挖掘“测试演示”按钮,我们presentCount将递增,“信息标签”将被更新,我们创建了一个实例PresentMeViewController ,设置其.transitioningDelegate ,并将其显示:

在此处输入图片说明

If we "drag down", or tap the button, or the 5-second Timer times-out, we'll update the dismissReason var and dismiss the VC.如果我们“向下拖动”,或者点击按钮,或者 5 秒计时器超时,我们将更新dismissReason并关闭VC。

Back in PresentTestViewController , our implementation of animationController(forDismissed dismissed: UIViewController) will increment dismissCount , get the reason for the dismissal, and update the UI by calling myViewWillAppear(fromDismiss: true) :回到PresentTestViewController ,我们的animationController(forDismissed dismissed: UIViewController)将增加dismissCount ,获取dismissCount的原因,并通过调用myViewWillAppear(fromDismiss: true)更新UI:

在此处输入图片说明

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 从UInavigationcontroller提供viewcontroller后,未调用ios 10加载view和viewwillappear - ios 10 load view and viewwillappear is not called after presenting a viewcontroller from UInavigationcontroller 从viewWillAppear推送viewController - Pushing viewController from viewWillAppear 在关闭当前ViewController之后快速显示,然后Presenting ViewController不调用viewWillAppear() - Swift after dismiss Current ViewController then the Presenting ViewController not calling viewWillAppear() 从AppDelegate呈现ViewController - Presenting a ViewController From AppDelegate 从appDelegate呈现ViewController - Presenting ViewController from appDelegate Swift iOS -NSMutableAttributedString不呈现ViewController吗? - Swift iOS -NSMutableAttributedString Not Presenting ViewController? 从自定义 PresentationController ViewController 呈现 ViewController - Presenting a ViewController from a custom PresentationController ViewController 从UIWebView委托呈现ViewController - Presenting ViewController from UIWebView delegate 从模态ViewController呈现UIImagePickerController - Presenting UIImagePickerController from modal ViewController 从UIApplication子类呈现ViewController - Presenting a ViewController from UIApplication subclass
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM