如何处理出现在我视图后面的内置 AlertController / 电子邮件提示

How to handle a built in AlertController / Email Prompt from appearing behind my view

我的应用程序包含一个可以从任何地方呈现的模态 UIView。这是如何工作的 present 方法将视图作为子视图附加到键 window:

func present(_ completion: ((Bool) -> ())? = { _ in }) {

    guard !isPresented else {
        return
    }

    if !isBackgroundReady {
        initializeBackground()
    }

    UIApplication.shared.keyWindow?.addSubview(backgroundView)
    UIApplication.shared.keyWindow?.addSubview(self)

    UIView.animate(withDuration: 0.3, animations: {
        self.backgroundView.alpha = 0.35
        self.alpha = 1.0
    }, completion: { _ in
        self.isPresented = true
        completion?(true)
    })
}

private func initializeBackground() {
    backgroundView.backgroundColor = UIColor.black
    backgroundView.alpha = 0.0
    backgroundView.frame = CGRect(x: UIScreen.main.bounds.midX, y: UIScreen.main.bounds.midY, width: UIScreen.main.bounds.width * 1.2, height: UIScreen.main.bounds.height * 1.2)
    backgroundView.center = CGPoint(x: UIScreen.main.bounds.midX, y: UIScreen.main.bounds.midY)
}

此模式包含一封电子邮件 link,用户可以单击该电子邮件以打开电子邮件提示(如果他们长按它,则可以打开电子邮件操作 sheet)。此 link 是通过使用 NSAttributedString 添加的,它是 UITextView 上的 .link 属性:

let supportString = NSMutableAttributedString(
    string: "general.supportEmail".localized(),
    attributes: [
        .link: "mailto:\("general.supportEmail".localized())",
    ]
)
supportTextView.attributedText = supportString

但是,当出现电子邮件提示或操作sheet时,它显示在模态视图后面:

是否有可能让 prompt/action sheet 以我当前呈现模态的方式出现在模态视图上方,或者我是否需要在某处添加某种识别器来检测何时这些视图之一出现并暂时关闭模式,直到我的应用程序视图重新聚焦?如果是后者,我将如何实现?

不是在 window 中添加,而是在 navicontroller -> topviewcontroller 中添加模式。

Link: https://developer.apple.com/documentation/uikit/uinavigationcontroller/1621849-topviewcontroller.

这可能对您有所帮助。

关于 为什么 发生这种情况的快速回答是,您在 Window 之上展示您的自定义模式视图,这将在所有内容之上,您的 UIAlertController 将显示在显示它的 UIViewController 上(在您的自定义视图下方)。

一个快速的解决方案是始终将您的自定义视图添加为当前 "top" UIViewController 的子视图。您可以使用 UIViewController 扩展名来做到这一点 - 像这样:

extension UIViewController {

    static func topViewController(_ viewController: UIViewController? = nil) -> UIViewController? {
        let viewController = viewController ?? UIApplication.shared.keyWindow?.rootViewController
        if let navigationController = viewController as? UINavigationController, !navigationController.viewControllers.isEmpty {
            return self.topViewController(navigationController.viewControllers.last)
        } else if let tabBarController = viewController as? UITabBarController,
            let selectedController = tabBarController.selectedViewController
        {
            return self.topViewController(selectedController)
        } else if let presentedController = viewController?.presentedViewController {
            return self.topViewController(presentedController)
        }
        return viewController
    }

}

此扩展程序将处理 "on top" 中的任何 UIViewController,无论它是在 UINavigationControllerUITabBarController 中还是仅以模态形式呈现等。应该涵盖所有案例。

之后您可以调整您的 present 方法以考虑到这一点:

func present(_ completion: ((Bool) -> ())? = { _ in }) {
    guard !isPresented else {
        return
    }
    if !isBackgroundReady {
        initializeBackground()
    }
    guard let topViewController = UIViewController.topViewController() else { return }
    topViewController.view.addSubview(backgroundView)
    topViewController.view.addSubview(self)
    UIView.animate(withDuration: 0.3, animations: {
        self.backgroundView.alpha = 0.35
        self.alpha = 1.0
    }, completion: { _ in
        self.isPresented = true
        completion?(true)
    })
}