J'ai un childViewController qui est poussé de parentViewController. Dans childViewController, je souhaite bloquer l'action pop dans une condition particulière.

J'ai écrit ce code dans viewWillDisappear: Mais je suppose que je dois le faire ailleurs.

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)

    if changesMade {
        let alertController = UIAlertController(title: "Alert", message: "Changes made are not saved. Do you wish to save changes made?", preferredStyle: .alert)

        let cancelOption = UIAlertAction(title: "Cancel", style: .cancel)
        let saveOption = UIAlertAction(title: "Save", style: .default, handler: { (action) in
            self.saveSession()
        })

        alertController.addAction(saveOption)
        alertController.addAction(cancelOption)
        present(alertController, animated: true)
    }
}
1
kbokdia 15 nov. 2017 à 07:34

4 réponses

Meilleure réponse

Il y a deux cas ici:

  1. L'utilisateur peut pop en utilisant le bouton de retour.
  2. L'utilisateur peut pop en utilisant le geste pop interactif du contrôleur de navigation.

Je pense que vous devriez utiliser un bouton de retour personnalisé et le nommer Terminé et écrire votre logique d'affichage de l'alerte en appuyant sur ce bouton. L'utilisation du bouton de retour personnalisé désactiverait le geste pop interactif par défaut et vous serez épargné de jouer la danse de l'activation / désactivation de interactivePopGesture sur le contrôleur de navigation.

1
Puneet Sharma 15 nov. 2017 à 05:36

J'ai trouvé une solution beaucoup plus élégante (à mon avis).

Cela fonctionne quelle que soit la façon dont l'utilisateur déclenche le pop (échappement d'accessibilité, geste de balayage vers l'arrière, tapotement vers l'arrière) car il remplace les méthodes pop intégrées utilisées par le système.

Swift 5

public class DiscardSafeNavigationController:UINavigationController {
    /// Should the pop be prevented? Set this to `true` when you have changes which need to be protected
    public var hasUnsavedChanges:Bool = false

    /// Show a prompt on the top most screen asking the user if they wish to proceed with the pop
    /// - Parameter discardCallback: The callback to use if the user opts to discard
    private func confirmDiscardChanges(discardCallback:@escaping (()->())) {
        let alertController = UIAlertController.init(title: "Discard changes", message: "Are you sure you want to discard any unsaved changes?", preferredStyle: UIAlertController.Style.alert)
        alertController.addAction(UIAlertAction.init(title: "Discard", style: UIAlertAction.Style.destructive, handler: { (_) in
            discardCallback()
            //User elected to discard and so, at this point, they no longer have changes to save
            self.hasUnsavedChanges = false
        }))
        alertController.addAction(UIAlertAction.init(title: "Cancel", style: UIAlertAction.Style.cancel, handler: nil))
        self.topViewController?.present(alertController, animated: true, completion: nil)
    }

    override func popViewController(animated: Bool) -> UIViewController? {
        //If there aren't unsaved changes, popping is safe
        if !hasUnsavedChanges {
            return super.popViewController(animated: animated)
        }else {
            //Changes have been made. Block the pop and first check with the user before continuing
            confirmDiscardChanges {
                super.popViewController(animated: animated)
            }
            return nil
        }
    }


}

Et lorsque vous souhaitez activer la protection contre la suppression de tout contrôleur de vue enfant, utilisez simplement:

(self.navigationController as? DiscardSafeNavigationController)?.hasUnsavedChanges = true

Puis chaque fois que le contrôleur de navigation est invité à le faire apparaître, le contrôleur de navigation le demandera d'abord à l'utilisateur.

-1
Sirens 26 juin 2019 à 01:20

Bloquer l'action pop jusqu'à ce que vos modifications ne soient pas enregistrées comme ceci

if changesMade {
        let alertController = UIAlertController(title: "Alert", message: "Changes made are not saved. Do you wish to save changes made?", preferredStyle: .alert)

        let cancelOption = UIAlertAction(title: "Cancel", style: .cancel)
        let saveOption = UIAlertAction(title: "Save", style: .default, handler: { (action) in
            self.saveSession()
            self.navigationController?.popViewController(animated: true)
        })

        alertController.addAction(saveOption)
        alertController.addAction(cancelOption)
        present(alertController, animated: true)
    }

Mise à jour - Ajoutez ce bouton personnalisé ci-dessous et son action dans le contrôleur de vue enfant qui est poussé depuis le contrôleur de vue parent afin que, sans satisfaire votre condition, l'utilisateur ne puisse pas passer à nouveau d'enfant à parent

Pour personnaliser l'action de navigation backButton, vous devez ajouter manuellement un bouton de retour en utilisant la ligne ci-dessous, vous pouvez personnaliser barButton ajouté ici dans DidLoad

 self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Back", style: UIBarButtonItemStyle.plain, target: self, action: #selector(self.backToInitial(sender:)))

Il exécutera l'action requise

 @objc func backToInitial(sender: AnyObject) {
        if changesMade {
            let alertController = UIAlertController(title: "Alert", message: "Changes made are not saved. Do you wish to save changes made?", preferredStyle: .alert)

            let cancelOption = UIAlertAction(title: "Cancel", style: .cancel)
            let saveOption = UIAlertAction(title: "Save", style: .default, handler: { (action) in
                self.saveSession()
                self.navigationController?.popViewController(animated: true)
            })

            alertController.addAction(saveOption)
            alertController.addAction(cancelOption)
            present(alertController, animated: true)
        }
    }

Et je ne pense pas que vous puissiez arrêter la tâche d'action du bouton de retour par défaut du contrôleur de navigation car il est conçu pour l'exécuter

Mais oui, vous pouvez le gérer dans ViewWillDisappear comme:

override func viewWillDisappear(_ animated: Bool) {
        if self.isMovingFromParentViewController || self.isBeingDismissed {
            self.navigationController?.popViewController(animated: false) //here task but will not result as Expected output 
        }
    }

-----------------Remettre à jour ---------------

Dans swift, j'ai utilisé une classe objective-C pour obtenir la sortie comme prévu maintenant, l'action pop childViewController est en train d'être contrôleur à partir d'une alerte en utilisant le bouton de retour par défaut que nous obtenons du contrôleur de navigation

Vous pouvez personnaliser votre action pop à exécuter ou non jusqu'à ce que votre condition ne soit pas satisfaite Lien Github - https://github.com/RockinGarg/NavigationBackButton.git

0
iOS Geek 15 nov. 2017 à 06:48

En fait, il existe une solution très simple: navigationItem.hidesBackButton = true - cela masquera le bouton "RETOUR" et désactivera la fonction de balayage vers l'arrière. 🤓

2
Serzhas 31 mai 2019 à 09:12
47299417