首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >这是使用heightForRowAt的正确方式吗?

这是使用heightForRowAt的正确方式吗?
EN

Stack Overflow用户
提问于 2018-01-15 01:43:28
回答 1查看 414关注 0票数 1

我正在尝试实现基于下载图像大小的动态大小的行高。我遇到的问题是在运行函数heightForRowAt时没有下载图像。实现这段代码的正确方式是什么?图像是UIImage的数组,rowHeights是CGFloat类型的数组,imageURLS是imageURLS的字符串数组。

代码语言:javascript
复制
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "Reuse", for: indexPath) as! TableViewCell

    // Configure the cell...

    ///////////////////////
 if(cell.cellImageView.image == nil){   
        let downloadURL = URL(string: self.imageURLS[indexPath.row])

      URLSession.shared.dataTask(with: downloadURL!) { (data, _, _) in
        if let data = data {
            let image = UIImage(data: data)
            DispatchQueue.main.async {
                cell.cellImageView.image = image
                cell.cellImageView.contentMode = .scaleAspectFit
                self.images.insert(image!, at: 0)
                let aspectRatio = Float((cell.cellImageView?.image?.size.width)!/(cell.cellImageView?.image?.size.height)!)
                print("aspectRatio: \(aspectRatio)")
                tableView.rowHeight = CGFloat(Float(UIScreen.main.bounds.width)/aspectRatio)
              print("tableView.rowHeight: \(tableView.rowHeight)")
                self.rowHeights.insert(CGFloat(Float(UIScreen.main.bounds.width)/aspectRatio), at: 0)
             tableView.reloadRows(at: [indexPath], with: .top)
            }
        }
        }.resume()
 }


    ///////////////////////
    return cell
}

//What is the proper way to implement this function
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    print("Im in height for row")
    return CGFloat(0.0)
}
EN

回答 1

Stack Overflow用户

发布于 2018-01-15 03:17:30

如果异步请求可能会更改单元格的高度,则不应直接更新单元格,而应完全重新加载单元格。

因此,在检索图像之前,将为每个可见单元格调用一次heightForRowAtcellForRowAt。由于尚未检索到图像,因此heightForRowAt必须返回一些适合于没有图像的单元格的固定值。cellForRowAt应该检测到尚未检索到图像,并启动该过程。但是当图像检索完成时,cellForRowAt应该调用reloadRows(at:with:),而不是直接更新单元格。这将再次启动此行的进程,包括触发heightForRowAt再次被调用。但这一次,图像应该在那里,所以heightForRowAt现在可以返回适当的高度,cellForRowAt现在可以只更新图像视图,而不需要进一步的网络请求。

例如:

代码语言:javascript
复制
class ViewController: UITableViewController {

    private var objects: [CustomObject]!

    override func viewDidLoad() {
        super.viewDidLoad()

        objects = [
            CustomObject(imageURL: URL(string: "https://upload.wikimedia.org/wikipedia/commons/e/e8/Second_Life_Landscape_01.jpg")!),
            CustomObject(imageURL: URL(string: "https://upload.wikimedia.org/wikipedia/commons/7/78/Brorfelde_landscape_2.jpg")!)
        ]
    }

    let imageCache = ImageCache()

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! CustomCell
        let imageURL = objects[indexPath.row].imageURL
        if let image = imageCache[imageURL] {
            // if we got here, we found image in our cache, so we can just 
            // update image view and we're done

            cell.customImageView.image = image
        } else {
            // if we got here, we have not yet downloaded the image, so let's
            // request the image and then reload the cell

            cell.customImageView.image = nil  // make sure to reset the image view

            URLSession.shared.dataTask(with: imageURL) { data, _, error in
                guard let data = data, error == nil else {
                    print(error ?? "Unknown error")
                    return
                }
                if let image = UIImage(data: data) {
                    self.imageCache[imageURL] = image
                    DispatchQueue.main.async {
                        // NB: This assumes that rows cannot be inserted while this asynchronous
                        // request is underway. If that is not a valid assumption, you will need to
                        // go back to your model and determine what `IndexPath` now represents
                        // this row in the table.

                        tableView.reloadRows(at: [indexPath], with: .middle)
                    }
                }
            }.resume()
        }
        return cell
    }

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

    override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        let imageURL = objects[indexPath.row].imageURL
        if let image = imageCache[imageURL] {
            let size = image.size
            return view.bounds.size.width * size.height / size.width
        } else {
            return 0
        }
    }
}

其中一个简单的图像缓存(与您的问题无关,但为了完整起见,我将其包括在内)如下所示:

代码语言:javascript
复制
class ImageCache {
    private let cache = NSCache<NSURL, UIImage>()

    private var observer: NSObjectProtocol!

    init () {
        observer = NotificationCenter.default.addObserver(forName: .UIApplicationDidReceiveMemoryWarning, object: nil, queue: nil) { [weak self] _ in
            self?.cache.removeAllObjects()
        }
    }

    deinit {
        NotificationCenter.default.removeObserver(observer)
    }

    subscript(key: URL) -> UIImage? {
        get {
            return cache.object(forKey: key as NSURL)
        }
        set (newValue) {
            if let image = newValue {
                cache.setObject(image, forKey: key as NSURL)
            } else {
                cache.removeObject(forKey: key as NSURL)
            }
        }
    }
}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/48252362

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档