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) }
4 Réponses :
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 .
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.
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 }) }
Merci. J'aime cette méthode. Cela peut éviter de nombreux problèmes similaires comme celui que j'ai rencontré.
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!