简体   繁体   English

第一个TableView单元未正确出队

[英]First TableView cells are not dequeuing properly

I'm working on the app, which loads flags to a class using https://www.countryflags.io/ API. 我正在使用该应用程序,该应用程序使用https://www.countryflags.io/ API将标志加载到类中。 I am loading a flag when initializing the object using Alamofire get request. 使用Alamofire get请求初始化对象时,我正在加载标志。 The problem is that the first few TableView cells that are dequeued when starting the app are loaded without flags. 问题在于,启动应用程序时出队的前几个TableView单元格没有标记。

But when I scroll back after scrolling down, they load perfectly. 但是当我向下滚动后向后滚动时,它们会完美加载。

I thought that it is happening because the request is not processed quickly enough and the first flags are not ready to load before the start of dequeuing cells. 我以为是因为没有足够快地处理请求,并且在开始出队单元之前没有准备好加载第一个标志。 But I have no idea how to setup something inside the getFlag() method to help me reload TableView data upon completion or delay dequeuing to the point when all flags are loaded. 但是我不知道如何在getFlag()方法中设置一些东西来帮助我在完成时重新加载TableView数据,或者延迟出队到加载所有标志时的延迟。

Country class with getflag() method 使用getflag()方法的国家/地区类别

import UIKit
import Alamofire

final class Country {
    let name: String
    let code: String
    var flag: UIImage?
    var info: String?

    init(name: String, code: String, flag: UIImage? = nil, info: String? = nil) {
        self.name = name
        self.code = code
        if flag == nil {
            getFlag()
        } else {
            self.flag = flag
        }
        self.info = info
    }

    func getFlag() {
        let countryFlagsURL = "https://www.countryflags.io/\(code.lowercased())/shiny/64.png"

        Alamofire.request(countryFlagsURL).responseData { response in
            if response.result.isSuccess {
                if let data = response.data {
                    self.flag = UIImage(data: data)
                }
            }
        }
    }
}

cellForRowAt method cellForRowAt方法

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let country = countries[indexPath.row]
        let cell = tableView.dequeueReusableCell(withIdentifier: "Country", for: indexPath)

        cell.textLabel?.text = country.name
        if let flag = country.flag {
            cell.imageView?.image = flag
        } else {
            cell.imageView?.image = .none
        }

        return cell
    }

The init method of Country should not be initiating the asynchronous image retrieval. Countryinit方法不应启动异步图像检索。 Instead, you should have the cellForRowAt initiate the asynchronous image retrieval. 相反,您应该让cellForRowAt启动异步图像检索。

But you shouldn't just blithely update the cell asynchronously, either, (because the row may have been reused by the time your Alamofire request is done). 但是,您也不应该只是异步地更新cell (因为在Alamofire请求完成时该行可能已经被重用)。 And, a more subtle point, you'll want to avoid having image requests getting backlogged if you scroll quickly to the end of the tableview, so you want to cancel pending requests for rows that are no longer visible. 而且,更微妙的一点是,如果快速滚动到表视图的末尾,您将希望避免积压图像请求,因此您想取消对不再可见的行的待处理请求。 There are a number of ways of accomplishing all three of these goals (async image retrieval in cellForRowAt , don't update cell after it has been used for another row, and don't let it get backlogged if scrolling quickly). 有多种方法可以实现所有这三个目标(在cellForRowAt异步图像检索,不要在将单元格用于另一行之后更新单元格,并且如果快速滚动,不要让其积压)。

The easiest approach is to use AlamofireImage . 最简单的方法是使用AlamofireImage Then all of this complexity of handling asynchronous image requests, canceling requests for reused cells, etc., is reduced to something like: 然后,处理异步图像请求,取消对重用单元的请求等所有这些复杂性都将降低为:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: “Country", for: indexPath)
    cell.imageView.af_setImage(withURL: objects[indexPath.row].url, placeholderImage: placeholder)
    return cell
}

Note, I'd suggest if you're going to use the default table view cell, that you supply a placeholder image in this routine, like shown above. 注意,如果您要使用默认的表格视图单元格,则建议您在此例程中提供一个占位符图像,如上所示。 Just create a blank image (or whatever) that is the same size as your flags. 只需创建一个与您的标志大小相同的空白图像(或其他任何图像)即可。 This ensures that the cell will be properly laid out. 这样可以确保正确放置单元。


By the way, if you'd like to generate that placeholder image programmatically, you can do something like: 顺便说一句,如果您想以编程方式生成该占位符图像,则可以执行以下操作:

let imageSize = CGSize(width: 44, height: 44)

lazy var placeholder: UIImage = UIGraphicsImageRenderer(size: imageSize).image { _ in
    UIColor.blue.setFill()
    UIBezierPath(rect: CGRect(origin: .zero, size: imageSize)).fill()
}

Now, that creates a blue placeholder thumbnail that is 44×44, but you can tweak colors and size as suits your application. 现在,这将创建一个44×44的蓝色占位符缩略图,但是您可以根据自己的应用调整颜色和大小。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM