web-dev-qa-db-fra.com

Chaque UIAlertController disparaît automatiquement avant que l'utilisateur ne réponde - depuis iOS 13

Puisque j'utilise iOS 13, chacun de mes UIAlertController apparaît pendant environ une demi-seconde et disparaît instantanément avant toute action de l'utilisateur. Une idée ?

Comme j'utilise UIAlertController à partir de différentes parties de mon application, j'utilise une extension qui me permet d'afficher à la fois des vues classiques et collectionView (cellule, en-tête, etc.)

public extension UIAlertController {
    func show() {
        let win = UIWindow(frame: UIScreen.main.bounds)
        let vc = UIViewController()
        vc.view.backgroundColor = .clear
        vc.view.tintColor = Theme.mainAccentColor
        win.rootViewController = vc
        win.windowLevel = UIWindow.Level.alert + 1
        win.makeKeyAndVisible()
        vc.present(self, animated: true, completion: nil)
    }
}

Et voici un exemple d'utilisation de cette extension:

fileprivate func showMissingAlert() {
        let alert = UIAlertController(title: "blablabla", message: "blablablablabla blabla", preferredStyle: UIAlertController.Style.alert)
        alert.show()
        alert.view.tintColor = Theme.mainAccentColor
        let cancelAction = UIAlertAction(title: "OK, blabla", style: .default, handler: {(alert: UIAlertAction!) in print("ok, leave")})
        alert.addAction(cancelAction)
    }

Et plus loin dans mon code:

showMissingAlert()

Avant iOS 13, chaque UIAlert fonctionnait bien ... Depuis que je suis passé à iOS 13, et même à iOS 13.1, c'est devenu un gros gâchis ... :(

  • Une idée de ce qui pourrait provoquer cela?

  • Et comment éviter d'utiliser UIAlert comme message subliminal :)?

9
Creanomy

J'ai exactement le même problème et je l'ai résolu en maintenant la fenêtre dans laquelle l'alerte est présentée dans une variable forte.

Vous pouvez tenir une fenêtre pour présenter des alertes dans votre AppDelegate, par exemple, et l'utiliser dans votre extension UIAlertController.

//In app delegate
let alertWindow: UIWindow = {
    let win = UIWindow(frame: UIScreen.main.bounds)
    win.windowLevel = UIWindow.Level.alert + 1
    return win
}()

Ensuite, dans votre extension:

public extension UIAlertController {
    func show() {
        let appDelegate = UIApplication.shared.delegate as! AppDelegate
        let vc = UIViewController()
        vc.view.backgroundColor = .clear
        vc.view.tintColor = Theme.mainAccentColor
        appDelegate.alertWindow.rootViewController = vc
        appDelegate.alertWindow.makeKeyAndVisible()
        vc.present(self, animated: true, completion: nil)
    }
}

Vous devrez également vous assurer que votre fenêtre d'alerte est supprimée de la vue lorsque votre alerte est fermée, sinon votre application ne répondra plus, car tous les appuis seront gérés par la fenêtre d'alerte (invisible), qui est toujours au-dessus de tout. Je fais cela en ajoutant ce code aux gestionnaires de toutes les actions de l'alerte:

(UIApplication.shared.delegate as! AppDelegate).alertWindow.isHidden = true
9
pepsy

Basé sur pepsyréponse . Si vous ne voulez pas vous soucier de alertWindow.isHidden = true stuff, vous pouvez faire quelque chose comme ceci:

class AlertHandler {
    private static let alertWindow: UIWindow = {
        let window = UIWindow(frame: UIScreen.main.bounds)
        window.windowLevel = UIWindow.Level.alert + 1
        return window
    }()

    private var alertController: UIAlertController

    init(title: String?,
         message: String?) {
        alertController = UIAlertController(title: title,
                                            message: message,
                                            preferredStyle: .alert)
    }

    func addAction(title: String?,
                   style: UIAlertAction.Style,
                   handler: ((UIAlertAction) -> Void)? = nil) {
        let action = UIAlertAction(title: title,
                                   style: style) { action in
                                    handler?(action)
                                    AlertHandler.alertWindow.isHidden = true
        }
        alertController.addAction(action)
    }

    func present() {
        AlertHandler.alertWindow.rootViewController = UIViewController()
        AlertHandler.alertWindow.makeKeyAndVisible()
        AlertHandler.alertWindow.rootViewController?.present(alertController,
                                                             animated: true,
                                                             completion: nil)
    }
}
0
Timofey Kuzmin

Vous pouvez également essayer cette solution. Ça marche de moi.

écrivez la méthode ci-dessous dans votre classe.

func presentViewController(alertController: UIAlertController, completion: (() -> Void)? = nil) {
        if var topController = UIApplication.shared.keyWindow?.rootViewController {
            while let presentedViewController = topController.presentedViewController {
                topController = presentedViewController
            }

            DispatchQueue.main.async {
                topController.present(alertController, animated: true, completion: completion)
            }
        }
    }

Ensuite, appelez-le à partir de votre code comme ci-dessous

let alertController = UIAlertController(title: "Discard Photo?",
                                                message: "Your photo will not be attached",
                                                preferredStyle: .alert)
        alertController.addAction(UIAlertAction(title: "Keep Photo", style: .default, handler: nil))
        alertController.addAction(UIAlertAction(title: "Discard", style: .default) { (_) -> Void in
            self.PhotoStack.deletePhoto(at: index)
            self.cameraBtn.isEnabled = true
        })

        self.presentViewController(alertController: alertController)
0