reloadData 不会将完成处理程序的 dispatch_async 中的表视图重新加载到主线程

Posted

技术标签:

【中文标题】reloadData 不会将完成处理程序的 dispatch_async 中的表视图重新加载到主线程【英文标题】:reloadData doesn't reload the table view in completion handler's dispatch_async to the main thread 【发布时间】:2015-07-31 04:17:05 【问题描述】:

我正在使用 UISearchBar 在 ios 8(模拟器)中使用 Swift 1.2 执行 MKLocalSearch。

我在完成处理程序中返回了结果(通过 printlns 在控制台中查看),但是当我尝试执行异步调用主线程以重新加载表视图时,没有任何反应。

如果我在搜索栏中单击取消,它将在表格视图中显示结果。看来我在做异步重载数据调用错误。

这是我的 UISearchBarDelegate searchBarSearchButtonClicked 代码:

func searchBarSearchButtonClicked(searchBar: UISearchBar) 
    searchBar.resignFirstResponder()

    let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate

    if let currentLocation = appDelegate.currentLocation 
        searchResults.removeAll()

        let request = MKLocalSearchRequest()
        request.naturalLanguageQuery = searchBar.text
        request.region = MKCoordinateRegionMakeWithDistance(currentLocation.coordinate, 200, 200)

        let search = MKLocalSearch(request: request)

        search.startWithCompletionHandler( (response: MKLocalSearchResponse!, error: NSError!) -> Void in
            if error != nil 
                println("Error occured in search: \(error.localizedDescription)")
             else if response.mapItems.count == 0 
                println("No matches found")
             else 
                println("Matches found")

                for item in response.mapItems as! [MKMapItem] 
                    println("Name = \(item.name)")

                    self.searchResults.append(item)
                

                println("Matching items = \(self.searchResults.count)")
            

            dispatch_async(dispatch_get_main_queue(),  () -> Void in
                self.searchTableView.reloadData() // doesn't do anything
            )
        )
    


这是我在控制台中看到的示例,但表格视图仍然为空:

numberOfRowsInSection called
numberOfRowsInSection called
numberOfRowsInSection called
numberOfRowsInSection called
numberOfRowsInSection called
numberOfRowsInSection called
numberOfRowsInSection called
numberOfRowsInSection called
numberOfRowsInSection called
numberOfRowsInSection called
numberOfRowsInSection called
numberOfRowsInSection called
numberOfRowsInSection called
searchBarSearchButtonClicked called
startWithCompletionHandler called
Matches found
Name = Artichoke Basille's Pizza
Name = Scott's Pizza Tours
Name = Waldy's Wood Fired Pizza & Penne
Name = Trattoria Zero Otto Nove
Name = Vezzo Thin Crust Pizza
Name = Grimaldi's
Name = Tappo Thin Crust Pizza
Name = Mozzarelli's
Name = NY Pizza Suprema
Name = Otto Enoteca Pizzeria
Matching items = 10
numberOfRowsInSection called
cellForRowAtIndexPath called, name = Artichoke Basille's Pizza
cellForRowAtIndexPath called, name = Scott's Pizza Tours
cellForRowAtIndexPath called, name = Waldy's Wood Fired Pizza & Penne
cellForRowAtIndexPath called, name = Trattoria Zero Otto Nove
cellForRowAtIndexPath called, name = Vezzo Thin Crust Pizza
cellForRowAtIndexPath called, name = Grimaldi's
cellForRowAtIndexPath called, name = Tappo Thin Crust Pizza
cellForRowAtIndexPath called, name = Mozzarelli's
cellForRowAtIndexPath called, name = NY Pizza Suprema
cellForRowAtIndexPath called, name = Otto Enoteca Pizzeria

我在这里做错了什么?

谢谢!

编辑:添加了更多 println 以显示正在调用的不同表委托方法并更新了上面的控制台输出。

【问题讨论】:

searchTableView 是否定义了 delegate 是的,searchTableView和searchBar的delegate就是table view controller。 你的表视图数据源方法被调用了吗? IE。 numberOfRowsInSectioncellForRowAtIndexPath? 尝试将self.searchTableView.reloadData() 紧跟在for 循环之后(println 上方)。 @FrequencyMatched,不幸的是这没有帮助。它只显示对 numberOfRowsInSection 和 cellForRowAtIndexPath 的调用加倍,但表格显示没有变化。 【参考方案1】:

好的,因此您的日志将显示cellForRowAtIndexPath 被正确调用并且它正在成功检索数据。所以,现在的问题是为什么你没有在表格中看到它。

cellForRowAtIndexPath 没有填充正确的控件,或者这些控件的frame(或alpha 或类似的东西)设置为您看不到数据。通过点击 Xcode 中的“暂停”按钮暂停执行:

并在新的视图调试器中查看视图的布局:

或在(lldb) 提示符下键入po [[UIWindow keyWindow] recursiveDescription]。确认您使用记录的数据填充的文本标签的 frame

【讨论】:

感谢您提供故障排除提示!我不小心将搜索栏控制器(已弃用?)与另一个控制器一起添加到场景中。一旦我删除它不再有漂亮的“取消”按钮(只是一个“x”),但它正确地显示了带有 MapKit 列表的表格视图。再次感谢!【参考方案2】:

问题是不能从主线程调用UITableView.reloadData()。取而代之的是,您需要安排自己的线程并异步调用它。

因此,首先您需要安排自己创建的线程并从后台调用它。您可以使用以下代码重新加载您的 UITableView 数据。

let concurrentQueue: dispatch_queue_t = dispatch_queue_create("MyQueue", nil)

dispatch_async(concurrentQueue) 
                            // update some UI
    dispatch_async(dispatch_get_main_queue()) 

     // update some UI
     self.tableView.reloadData()
         
   

好吧,这肯定会重新加载您的 UITableView 数据。据我所知,我正在使用这种方法。如果您发现任何其他更方便的方法,请告诉我。

【讨论】:

他没有从后台队列呼叫reloadData。他已经将它分派回主队列。 @Rob 哦!这是我的错!当您调用 dispatch_get_main_queue() 时,您实际上会返回主线程。更新答案,这就是我创建自己的线程来重新加载数据的原因! 您可能误解了我的观点:我同意您的观点,您必须从主线程调用reloadData(因此,如果您在后台队列中执行某些操作,请将其分派回主队列)。我的意思只是 OP 已经在这样做了,所以这显然不是这里的问题。问题出在其他地方。

以上是关于reloadData 不会将完成处理程序的 dispatch_async 中的表视图重新加载到主线程的主要内容,如果未能解决你的问题,请参考以下文章

在 deleteRows 之后 TableView reloadData

ReloadData 不会触发 cellForRowAtIndexPath

循环中的 tableView reloadData 不会在每次通过时绘制单元格

UICollectionView reloadData() 不会更新集合视图中的单元格

reloadData() 不会更新父 TableView

iOS:在 reloadData 时保持 UITableView 在同一位置