如何使用 Multipeer Connectivity (Swift 3) 在会话中通过 didReceiveData() 调用 table.reloadData

Posted

技术标签:

【中文标题】如何使用 Multipeer Connectivity (Swift 3) 在会话中通过 didReceiveData() 调用 table.reloadData【英文标题】:How to call table.reloadData by didReceiveData() in session using Multipeer Connectivity (Swift 3) 【发布时间】:2017-12-07 04:53:45 【问题描述】:

我正在努力解决一个问题,即当我使用 Multipeer Connectivity 框架从会话接收数据时,我想更新数据并立即重新加载视图控制器的表。

这是我的 MPCHandler 的 didReceiveData 函数的代码:

func session(_ session: MCSession, didReceive data: Data, fromPeer peerID: MCPeerID) 
    NSLog("%@", "didReceive: \(data)")
    if var dict: [NSDictionary] = NSKeyedUnarchiver.unarchiveObject(with: data) as? [NSDictionary]
        let d = dict.removeFirst()
        let appDelegate = UIApplication.shared.delegate as! AppDelegate
        appDelegate.winnerDataController.syncWinnerFromDictionary(d: dict)

    

WinnerDataController 中的同步功能

func syncWinnerFromDictionary(d: NSDictionary)
    let appDelegate = UIApplication.shared.delegate as! AppDelegate
    let context = appDelegate.persistentContainer.viewContext
    let entityDescription = NSEntityDescription.entity(forEntityName: "Winner", in: context)!

    let winner: Winner = NSManagedObject(entity: entityDescription, insertInto: context) as! Winner
    winner.setValue(d.value(forKey: "index"), forKey: "index")
    winner.setValue(d.value(forKey: "dept"), forKey: "dept")
    winner.setValue(d.value(forKey: "name"), forKey: "name")
    save()
    appDelegate.resultViewController.tableView.reloadData()

我想在收到数据后更新并重新加载 resultViewContoller 表。但是,当它转到appDelegate.resultViewController.tableView.reloadData() 时,控制台会给出fatal error: unexpectedly found nil while unwrapping an Optional value。我花了一整天的时间来弄清楚为什么 tableView 是 nil,但仍然没有任何想法。

有人可以帮忙吗?谢谢

我尝试了@Hitesh Sultaniya 提供的以下代码:结果视图控制器中的NotificationCenter.default.addObserver(self, selector: #selector(testing), name: NSNotification.Name(rawValue: "syncWinner"), object: nil)func testing(notification: Notification) print("testing") self.tableView.reloadData() 。此外,WinnerDataController 中的NotificationCenter.default.post(name: NSNotification.Name(rawValue: "syncWinner"), object: nil)

在控制台中,打印“测试”。然而,几秒钟后, “在从主线程访问引擎后,此应用程序正在从后台线程修改自动布局引擎。这可能导致引擎损坏和奇怪的崩溃” 显示。

然后,我尝试了这个:

func testing(notification: Notification)
DispatchQueue.global(qos: .userInitiated).async
   self.tableView.reloadData()

现在,没有显示错误。但是表还没有重新加载...

【问题讨论】:

【参考方案1】:

在 resultViewController 中你可以设置通知观察者重新加载表格视图

你可以这样设置

// 在结果视图控制器中注册并添加观察者

 NotificationCenter.default.addObserver(self, selector: #selector(self.reloadTableView(_:)), name: NSNotification.Name(rawValue: "<Your Notfication Name>"), object: nil)

 // handle notification
 func reloadTableView(_ notification: NSNotification) 

  //relaod your table view 
 

当您完成接收数据后,您可以像这样发布通知

NSNotificationCenter.defaultCenter().postNotificationName(notificationName, object: nil)

所以这将发布您在结果视图控制器中设置的通知,然后您的任务将被执行

注意:这只是伪代码,因此可能包含错误

如果您在执行此任务时更改 UI 的应用程序更改了您更改 UI 的位置 而且我认为您还需要在重新加载表格视图之前再次从数据库中获取数据,因为根据您提供的相关代码,您可以直接重新加载表格。 您可以像这样检查当前线程,

 if (Thread.isMainThread == true)
 
                    //Perform UI Task 
 
 else
 
      //Dispatch for main queue with synchronous not asynchronous and then update the UI
 

希望对你有帮助

【讨论】:

【参考方案2】:

在@Hitesh Sultaniya 的帮助下,我解决了这个问题。

这是我的有效代码:

WinnerDataController 中的同步功能:

func syncWinnerFromDictionary(d: NSDictionary)
    let appDelegate = UIApplication.shared.delegate as! AppDelegate
    let context = appDelegate.persistentContainer.viewContext
    let entityDescription = NSEntityDescription.entity(forEntityName: "Winner", in: context)!
    let winner: Winner = NSManagedObject(entity: entityDescription, insertInto: context) as! Winner
    winner.setValue(d.value(forKey: "index"), forKey: "index")
    winner.setValue(d.value(forKey: "dept"), forKey: "dept")
    winner.setValue(d.value(forKey: "name"), forKey: "name")
    save()
    NotificationCenter.default.post(name: NSNotification.Name(rawValue: "syncWinner"), object: nil)

对于 ResultViewController:

override func viewDidLoad() 
    super.viewDidLoad()
    NotificationCenter.default.addObserver(self, selector: #selector(testing), name: NSNotification.Name(rawValue: "syncWinner"), object: nil)
    appDelegate = UIApplication.shared.delegate as! AppDelegate
    

func testing(notification: Notification)
    if Thread.isMainThread == true
        self.tableView.reloadData()
        print("main thread")
    
    else
        DispatchQueue.main.sync 
            self.updateList()
            print(winnerList.count)
            self.tableView.reloadData()
            print("not main thread")

        
    

最初,我在 ResultViewController 中尝试过,但不起作用

override func viewDidLoad() 
    super.viewDidLoad()
    NotificationCenter.default.addObserver(self, selector: #selector(testing), name: NSNotification.Name(rawValue: "syncWinner"), object: nil)
    appDelegate = UIApplication.shared.delegate as! AppDelegate
    

func testing(notification: Notification)
    if Thread.isMainThread == true
        self.tableView.reloadData()
        print("main thread")
    
    else
        DispatchQueue.main.sync 
            self.tableView.reloadData()
            print("not main thread")

        
    

这是因为在运行DispatchQueue.main.sync的block时,它不是主线程,还没有拿到最新的winnerList。事实上,该表已被重新加载,但获胜者列表已过时。因此,似乎表尚未重新加载。

然后,我添加了self.updateList(),让这个线程获取最新的核心数据。最后,它起作用了。

希望这对那些与我遇到同样问题的人有所帮助。

【讨论】:

以上是关于如何使用 Multipeer Connectivity (Swift 3) 在会话中通过 didReceiveData() 调用 table.reloadData的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Multipeer Connectivity 安全地停止发送图像

MultiPeer 框架避免使用 MCBrowserView?

如何使用 Multipeer Connectivity 框架邀请用户加入我的会话?

如何检测 Multipeer Connectivity 邀请的拒绝?

如何使用 Multipeer Connectivity (Swift 3) 在会话中通过 didReceiveData() 调用 table.reloadData

仅使用蓝牙时,iOS Multipeer 连接将对等点数量限制为 6 个,无论如何要绕过它?