【问题标题】:Reloading table causes flickering重新加载表导致闪烁
【发布时间】:2016-02-16 16:17:57
【问题描述】:

我在它下面有一个搜索栏和一个表格视图。当我搜索某些东西时,会进行网络调用并将 10 个项目添加到数组中以填充表。当我滚动到表格底部时,又对另外 10 个项目进行了网络调用,所以现在数组中有 20 个项目...这可以继续,因为它是类似于 Facebook 的新闻提要的无限滚动。

每次进行网络调用时,我也会在主线程上调用self.tableView.reloadData()由于每个单元格都有一个图像,您可以看到闪烁 - 单元格图像闪烁白色

我尝试实现this solution,但我不知道将它放在我的代码中的哪个位置或如何放置。我的代码是 Swift,那就是 Objective-C。

有什么想法吗?

问题 1 更新

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier(R.reuseIdentifier.searchCell.identifier, forIndexPath: indexPath) as! CustomTableViewCell

    let book = booksArrayFromNetworkCall[indexPath.row]

    // Set dynamic text
    cell.titleLabel.font = UIFont.preferredFontForTextStyle(UIFontTextStyleHeadline)
    cell.authorsLabel.font = UIFont.preferredFontForTextStyle(UIFontTextStyleFootnote)

    // Update title
    cell.titleLabel.text = book.title

    // Update authors
    cell.authorsLabel.text = book.authors

    /*
    - Getting the CoverImage is done asynchronously to stop choppiness of tableview.
    - I also added the Title and Author inside of this call, even though it is not
    necessary because there was a problem if it was outside: the first time a user
    presses Search, the call for the CoverImage was too slow and only the Title
    and Author were displaying.
    */
    Book.convertURLToImagesAsynchronouslyAndUpdateCells(book, cell: cell, task: task)

    return cell
}

cellForRowAtIndexPath 在里面使用这个方法:

    class func convertURLToImagesAsynchronouslyAndUpdateCells(bookObject: Book, cell: CustomTableViewCell, var task: NSURLSessionDataTask?) {

    guard let coverImageURLString = bookObject.coverImageURLString, url = NSURL(string: coverImageURLString) else {
        return
    }

    // Asynchronous work being done here.
    task = NSURLSession.sharedSession().dataTaskWithURL(url, completionHandler: { (data, response, error) -> Void in

        dispatch_async(dispatch_get_main_queue(), {

            // Update cover image with data

            guard let data = data else {
                return
            }

            // Create an image object from our data
            let coverImage = UIImage(data: data)
            cell.coverImageView.image = coverImage
        })
    })

    task?.resume()
}

当我滚动到表格底部时,我会使用willDisplayCell 检测是否到达底部。如果是底部,那么我再次进行相同的网络调用。

    override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {

    if indexPath.row+1 == booksArrayFromNetworkCall.count {

        // Make network calls when we scroll to the bottom of the table.
        refreshItems(currentIndexCount)
    }

}

这是网络调用代码。当我在搜索栏上按 Enter 时第一次调用它,然后每次到达单元格底部时都会调用它,如您在 willDisplayCell 中看到的那样。

    func refreshItems(index: Int) {

    // Make to network call to Google Books
    GoogleBooksClient.getBooksFromGoogleBooks(self.searchBar.text!, startIndex: index) { (books, error) -> Void in

        guard let books = books else {
            return
        }

        self.footerView.hidden = false

        self.currentIndexCount += 10

        self.booksArrayFromNetworkCall += books

        dispatch_async(dispatch_get_main_queue()) {
            self.tableView.reloadData()
        }
    }
}

【问题讨论】:

  • 能否在创建单元格时显示代码(cellForRowAtIndexPath)?我以前做过这种搜索,我没有这个问题。如果只有图像闪烁白色,而旁边的文本没有闪烁,则可能当您调用 reloadData() 时,图像会再次从源下载,这会导致闪烁。在这种情况下,您可能需要将图像保存在缓存中。
  • @OscarJ.Irun 请查看更新后的问题,我添加了您要求的代码。再次查看应用程序后,只有图像闪烁白色,而不是文字。你会如何解决这个问题?
  • 是的,我猜它每次调用reloadData() 时都会从源下载图像。我建议使用SDWebImage 来缓存图像并异步下载。它非常简单,我在大多数项目中都使用它。要确认是这种情况,只需将您的资产中的静态图像添加到单元格中,而不是调用convertURLToImagesAsynchronouslyAndUpdateCells,您将看到它不会再次闪烁。
  • @OscarJ.Irun 你说得对,当我添加静态图像时,它不再闪烁。我以前从未做过图像缓存,听起来很复杂。你有什么推荐的 Swift 教程吗?另外,这是解决此问题的唯一方法吗?谢谢!
  • 该库完成了所有艰苦的工作。我没有在 Swift 中编程,但我认为它就像 cell.imageView.sd_setImageWithURL(myImageURL) 一样简单。它已经完成了!检查此链接SDWebImage in Swift

标签: ios swift uitableview networking reloaddata


【解决方案1】:

如果只有图像闪烁白色,而旁边的文本没有,可能当您调用reloadData() 时,图像再次从源下载,导致闪烁。在这种情况下,您可能需要将图像保存在缓存中。

我建议使用SDWebImage 来缓存图像并异步下载。它非常简单,我在大多数项目中都使用它。要确认是这种情况,只需将您的资产中的静态图像添加到单元格中,而不是调用convertURLToImagesAsynchronouslyAndUpdateCells,您将看到它不会再次闪烁。

我不会在 Swift 中编程,但我认为它就像 cell.imageView.sd_setImageWithURL(myImageURL) 一样简单。它已经完成了!

【讨论】:

  • 完美运行!我尝试在没有库的情况下首先根据上面的答案使用self.tableView.insertRowsAtIndexPaths 并且闪烁停止,但后来我遇到了一个问题,即当表格触到底部并插入新行时表格会跳动。这个库解决了所有问题,而且非常简单,感谢您也发送了有关如何使用该库的链接。
【解决方案2】:

这是一个使用insertRowsAtIndexPaths(_:withRowAnimation:)的无限滚动示例

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {

    @IBOutlet weak var tableView: UITableView!

    var dataSource = [String]()
    var currentStartIndex = 0

    // We use this to only fire one fetch request (not multiple) when we scroll to the bottom.
    var isLoading = false

    override func viewDidLoad() {
        super.viewDidLoad()

        // Load the first batch of items.
        loadNextItems()
    }

    // Loads the next 20 items using the current start index to know from where to start the next fetch.
    func loadNextItems() {

        MyFakeDataSource().fetchItems(currentStartIndex, callback: { fetchedItems in

            self.dataSource += fetchedItems // Append the fetched items to the existing items.

            self.tableView.beginUpdates()

            var indexPathsToInsert = [NSIndexPath]()
            for i in self.currentStartIndex..<self.currentStartIndex + 20 {
                indexPathsToInsert.append(NSIndexPath(forRow: i, inSection: 0))
            }
            self.tableView.insertRowsAtIndexPaths(indexPathsToInsert, withRowAnimation: .Bottom)
            self.tableView.endUpdates()

            self.isLoading = false

            // The currentStartIndex must point to next index.
            self.currentStartIndex = self.dataSource.count
        })
    }

    // #MARK: - Table View Data Source Methods

    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return dataSource.count
    }

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = UITableViewCell()
        cell.textLabel!.text = dataSource[indexPath.row]
        return cell
    }

    // #MARK: - Table View Delegate Methods

    func scrollViewDidScroll(scrollView: UIScrollView) {
        if isLoading == false && scrollView.contentOffset.y + scrollView.bounds.size.height > scrollView.contentSize.height {
            isLoading = true
            loadNextItems()
        }
    }
}

MyFakeDataSource 无关紧要,它可能是您的GoogleBooksClient.getBooksFromGoogleBooks,或您正在使用的任何数据源。

【讨论】:

  • 这解决了闪烁的问题,这很好,但它引入了另一个问题。现在,每当我到达表格底部并进行新的网络调用时,它就会开始加载,然后单元格开始动画并且表格“跳起来”。所以它到达底部,然后像屏幕的 20% 一样向上弹起,新的单元格被加载。滚动条向上移动。从那里开始一切正常,但在我的解决方案之前并没有发生这种情况。任何想法为什么?
  • 如果我这样做self.tableView.insertRowsAtIndexPaths(indexPathsToInsert, withRowAnimation: .None),None 仍然不会删除我认为的动画。也许这就是问题所在,不确定。另外,我用的是tableView.estimatedRowHeight = 100tableView.rowHeight = UITableViewAutomaticDimension,不知道是不是这个问题?
【解决方案3】:

尝试在调用[tableView reloadData]方法前后改变table alpha值..赞

dispatch_async(dispatch_get_main_queue()) {
            self.aTable.alpha = 0.4f;
            self.tableView.reloadData()
            [self.aTable.alpha = 1.0f;

        }

我在 UIWebView 重新加载中使用了相同的方法。它对我有用。

【讨论】:

  • 我更新了我上面的问题,你会把这个放在哪里?
猜你喜欢
  • 2013-10-08
  • 2013-02-18
  • 2013-01-20
  • 1970-01-01
  • 1970-01-01
  • 2012-06-19
  • 1970-01-01
  • 2010-09-15
  • 2017-07-19
相关资源
最近更新 更多