2
votes

PushViewController deux fois lorsque je double-clique trop rapidement

J'ai le code suivant lorsque j'appelle pour pousser le ViewController vers le contrôleur de chat détaillé (chat 1-à-1). Cependant, si je clique trop rapidement, le contrôleur de vue sera poussé deux fois. Je vois l'animation deux fois. Quelqu'un pourrait-il m'indiquer où est l'erreur? Le code provient d'une leçon Youtube (Firebase Chat) de LBTA.

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    let message = messages[indexPath.row]
    guard let chatPartnerId = message.chatPartnerId() else {return}

    let ref = Database.database().reference().child("users").child(chatPartnerId)
    ref.observeSingleEvent(of: .value, with: { (snapshot) in
        guard let dictionary = snapshot.value as? [String: AnyObject] else {
            return
        }
        let user = ChatUser(dictionary: dictionary)
        user.id = chatPartnerId
        self.showChatControllerForUser(user)

    }, withCancel: nil)

}

func showChatControllerForUser(_ user: ChatUser) {
    let chatLogController = ChatLogController(collectionViewLayout: UICollectionViewFlowLayout())
    chatLogController.chatUser = user
    navigationController?.pushViewController(chatLogController, animated: true)
}


0 commentaires

4 Réponses :


3
votes

Le problème est que vous autorisez l'utilisateur à appuyer plusieurs fois, ce qui fait que le contrôleur de vue est poussé plusieurs fois. Vous devez l'empêcher.

Donc, une option consiste à créer une variable globale isObserving qui ne permet pas d'observer plusieurs fois.

var isObserving: Bool = false

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

    if !isObserving {
        isObserving = true
        ...
        ref.observeSingleEvent(of: .value, with: { snapshot in
            ...
            self.isObserving = false
            self.showChatControllerForUser(user)
        })
    }
}

Suggestions pour une meilleure UX. Si l'observation prend un certain temps, vous devez informer l'utilisateur qu'il y a quelque chose qui a besoin de temps. Ainsi, vous pouvez par exemple démarrer et arrêter le chargement de UIActivityIndicatorView . Vous pouvez également interdire à l'utilisateur de sélectionner une cellule plusieurs fois en travaillant avec isUserInteractionEnabled de la vue tableau .


0 commentaires

0
votes

Le problème est que vous n'appuyez sur ViewController qu'après avoir reçu une réponse du serveur, et le bouton peut être à nouveau appuyé avant la réponse.

Vous pouvez donc soit pousser le contrôleur de vue immédiatement, puis demander des données sur le contrôleur de vue poussé, soit empêcher la "demande" avec une variable comme @Robert Dresler l'a fait.


0 commentaires

5
votes

Ce que vous pouvez faire pour éviter ce problème est de désactiver l'interaction utilisateur de la vue tableau et de la réactiver après avoir poussé vers le deuxième contrôleur de vue.

extension UINavigationController {
    public func pushViewController(
        _ viewController: UIViewController,
        animated: Bool,
        completion: @escaping () -> Void)
    {
        pushViewController(viewController, animated: animated)

        guard animated, let coordinator = transitionCoordinator else {
            DispatchQueue.main.async { completion() }
            return
        }

        coordinator.animate(alongsideTransition: nil) { _ in completion() }
    }
}

Par défaut, pushViewController (_: animated :) n'a pas de gestionnaire d'achèvement, donc comme solution de contournement, nous pourrions ajouter l'extension suivante pour y parvenir:

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    // add this:
    tableView.isUserInteractionEnabled = false
    let message = messages[indexPath.row]
    guard let chatPartnerId = message.chatPartnerId() else {return}

    let ref = Database.database().reference().child("users").child(chatPartnerId)
    ref.observeSingleEvent(of: .value, with: { (snapshot) in
        guard let dictionary = snapshot.value as? [String: AnyObject] else {
            return
        }
        let user = ChatUser(dictionary: dictionary)
        user.id = chatPartnerId
        self.showChatControllerForUser(user)

    }, withCancel: nil)

}

func showChatControllerForUser(_ user: ChatUser) {
    let chatLogController = ChatLogController(collectionViewLayout: UICollectionViewFlowLayout())
    chatLogController.chatUser = user
    // edit this:
    navigationController?.pushViewController(chatLogController, animated: true)

    navigationController?.pushViewController(chatLogController, animated: true, completion: {
        self.tableView.isUserInteractionEnabled = true
    })
}

Cité de: https://stackoverflow.com/a/33767837/5501940


1 commentaires

Merci. J'aime cette méthode. Cela peut éviter de nombreux problèmes similaires comme celui que j'ai rencontré.



2
votes

Il n'y a pas d'erreur dans votre code, appuyez deux fois sur une cellule, c'est juste quelque chose que Swift ne vérifie pas.

Vous pouvez essayer quelque chose comme ceci pour éviter ce comportement:

override func viewWillAppear(){
    super.viewWillAppear()

    self.view.isUserInteractionEnabled = true // you need to enable user interaction if user comes back
}

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

    self.view.isUserInteractionEnabled = false // this will prevent further taps

    let message = messages[indexPath.row]
    guard let chatPartnerId = message.chatPartnerId() else {return}

    let ref = Database.database().reference().child("users").child(chatPartnerId)
    ref.observeSingleEvent(of: .value, with: { (snapshot) in
        guard let dictionary = snapshot.value as? [String: AnyObject] else {
            return
        }
        let user = ChatUser(dictionary: dictionary)
        user.id = chatPartnerId
        self.showChatControllerForUser(user)

    }, withCancel: nil)

}

func showChatControllerForUser(_ user: ChatUser) {
    let chatLogController = ChatLogController(collectionViewLayout: UICollectionViewFlowLayout())
    chatLogController.chatUser = user
    navigationController?.pushViewController(chatLogController, animated: true)
}

J'espère que cela vous aidera!


0 commentaires