PushViewController 两次当我双击太快时

问题描述 投票:0回答:5

当我调用将 ViewController 推送到详细聊天控制器(一对一聊天)时,我有以下代码。但是,如果我单击得太快,视图控制器将被推送两次。动画我看了两遍。谁能指出我错误在哪里?该代码来自 LBTA 的 Youtube 课程(Firebase Chat)。

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)
}
ios swift uitableview uinavigationcontroller
5个回答
6
投票

避免此问题的方法是禁用表视图用户交互,并在推送到第二个视图控制器后重新启用它。

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

默认情况下,

pushViewController(_:animated:)
没有完成处理程序,因此作为解决方法,我们可以添加以下扩展来实现它:

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

引用自:https://stackoverflow.com/a/33767837/5501940


3
投票

问题是您允许用户多次点击,这会导致视图控制器被多次推送。你必须阻止它。

因此,一种选择是创建一个全局变量

isObserving
,它不允许多次观察。

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

更好的用户体验的建议。如果观察需要一些时间,你应该让用户知道有些事情需要时间。例如,您可以开始和停止加载

UIActivityIndicatorView
。您还可以通过使用表格视图的
isUserInteractionEnabled
来禁止用户多次选择单元格。


2
投票

您的代码中没有错误,双击单元格只是 Swift 不检查的事情。

您可以尝试这样的方法来避免这种行为:


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

希望对您有帮助!


0
投票

问题在于,您仅在收到服务器响应后才推送 ViewController,并且可以在响应之前再次点击该按钮。

因此,您可以立即推送视图控制器,然后在推送的视图控制器上请求数据,或者像 @Robert Dresler 那样使用变量阻止“请求”。


0
投票

检查 ViewController 是否已经在导航堆栈中

import UIKit

extension UINavigationController {
    func contains(_ controller: UIViewController.Type) -> Bool {
        for viewController in viewControllers {
            if viewController.isKind(of: controller) {
                return true
            }
        }
        return false
    }
}

然后

guard !navigationController.contains(YourViewController.self) else { return }
navigationController.pushViewController(controller, animated: true)
© www.soinside.com 2019 - 2024. All rights reserved.