PushViewController Twice When I double click too quickly

拜拜、爱过 提交于 2021-02-10 06:32:12

问题


I have the follow code when I call to push the ViewController to detailed chat controller ( 1-to-1 chat). However, if I click too quickly, the view controller will be pushed twice. I see the animation twice. Could anyone point me where the mistake is? The code is from a Youtube lesson (Firebase Chat) from 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)
}

回答1:


What you could do to avoid this issue is to disable the table view user interaction and reenable it after pushing to second view controller.

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
    })
}

By default, pushViewController(_:animated:) does not has a completion handler, so as a workaround we could add the follwoing extention to achieve it:

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() }
    }
}

Cited from: https://stackoverflow.com/a/33767837/5501940




回答2:


The problem is that you allow user to tap multiple times and this causes that view controller is pushed multiple times. You have to prevent it.

So, one option is creating one global variable isObserving which doesn't allow observing multiple times.

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 to better UX. If observing takes some time, you should let user know that there is something which needs time. So you can for example start and stop loading UIActivityIndicatorView. Also you can forbid user to select cell multiple times by working with table view's isUserInteractionEnabled.




回答3:


There's no mistake in your code, double tap on a cell its just something that Swift don't check.

You can try something like this to avoid this behavior:


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)
}

Hope it helps you!




回答4:


The problem is that you're only pushing the ViewController after you get a server response, and the button can be tapped again before the response.

So you can either push the view controller immediately and then request data on the pushed view controller, or, prevent the "request" with a variable like @Robert Dresler did.



来源:https://stackoverflow.com/questions/54201067/pushviewcontroller-twice-when-i-double-click-too-quickly

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!