在Swift中设置UITableViewCell中的图片

3

我有一份Reddit帖子列表,如果存在缩略图,我想要显示它。我已经实现了这个功能,但是它非常不稳定。主要存在两个问题:

  • 图片在点击时会被重新调整大小
  • 图片在滚动时会乱跳

这是代码:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("Post", forIndexPath: indexPath) as UITableViewCell
    let post = swarm.posts[indexPath.row]
    cell.textLabel!.text = post.title

    if(post.thumb? != nil && post.thumb! != "self") {
        cell.imageView!.image = UIImage(named: "first.imageset")
        var image = self.imageCache[post.thumb!]

        if(image == nil) {
            FetchAsync(url: post.thumb!) { data in // code is at bottom, this just drys things up
                if(data? != nil) {
                    image = UIImage(data: data!)
                    self.imageCache[post.thumb!] = image
                    dispatch_async(dispatch_get_main_queue(), {
                        if let originalCell = tableView.cellForRowAtIndexPath(indexPath) {
                            originalCell.imageView?.image = image
                            originalCell.imageView?.frame = CGRectMake(5,5,35,35)
                        }
                    })
                }
            }
        } else {
            dispatch_async(dispatch_get_main_queue(), {
                if let originalCell = tableView.cellForRowAtIndexPath(indexPath) {
                    originalCell.imageView?.image = image
                    originalCell.imageView?.frame = CGRectMake(5,5,35,35)
                }
            })
        }
    }

    return cell
}

这是应用程序加载时的样子,看起来一切都正常:

loaded up, looks like everything is working

然后,如果我点击一张图片(即使在滚动时),它会重新调整大小:

pictures resize on tap

如果你上下滚动,图片就会变得很混乱(看看中间的帖子-泛型有趣):

pictures shuffle on scroll

我做错了什么?
**图片和标题来自reddit,不是由我生成的**
承诺的FetchAsync类编辑:
class FetchAsync {
    var url: String
    var callback: (NSData?) -> ()

    init(url: String, callback: (NSData?) -> ()) {
        self.url = url
        self.callback = callback
        self.fetch()
    }

    func fetch() {
        var imageRequest: NSURLRequest = NSURLRequest(URL: NSURL(string: self.url)!)
        NSURLConnection.sendAsynchronousRequest(imageRequest,
            queue: NSOperationQueue.mainQueue(),
            completionHandler: { response, data, error in
                if(error == nil) {
                    self.callback(data)
                } else {
                    self.callback(nil)
                }
        })
        callback(nil)
    }
}

我认为你的originalCell常量是单元格的旧值,因为你从tableView.cellForRowAtIndexPath(indexPath)获取它,并且你正在“刷新”它的函数中。所以尝试在cell上进行更改。 - Pintouch
这是因为它被放置在后台队列中,所以当它下载图像时,表格单元可能已经不存在了。将 originalCell 切换为 cell 并没有解决任何问题,如果有的话,似乎会使错误更加严重,但这可能只是我的想象。 - cadlac
你尝试使用调试器运行它了吗?你可以尝试调用 tableView.reloadData。(我现在无法测试,所以这只是一个建议) - Pintouch
显然还不够:)。我今晚会再做一些工作。我并不认为每次显示新的单元格时重新加载整个tableView是解决办法...它可能能起作用,但我觉得它无法解决潜在问题。 - cadlac
听起来你的数据模型不匹配。 - CaptainCOOLGUY
3个回答

5
很遗憾,“基础”表格视图单元似乎有此限制。我最终创建了一个自定义的TableViewCell。我参考了Ray Wenderlich的教程,链接在这里:http://www.raywenderlich.com/68112/video-tutorial-table-views-custom-cells。虽然代码很简单,但这也意味着它是一个“简单”的解决方案。这是我的最终代码:PostCell.swift(所有脚手架代码)。
import UIKit

class PostCell: UITableViewCell {

    @IBOutlet weak var thumb: UIImageView!
    @IBOutlet weak var title: UILabel!

    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
    }

    override func setSelected(selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)

        // Configure the view for the selected state
    }

}

PostsController.swift

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("PostCell", forIndexPath: indexPath) as PostCell
    let post = swarm.posts[indexPath.row]
    cell.title!.text = post.title

    if(post.thumb? != nil && post.thumb! != "self") {
        cell.thumb!.image = UIImage(named: "first.imageset")
        cell.thumb!.contentMode = .ScaleAspectFit
        var image = self.imageCache[post.thumb!]

        if(image == nil) {
            FetchAsync(url: post.thumb!) { data in
                if(data? != nil) {
                    image = UIImage(data: data!)
                    self.imageCache[post.thumb!] = image
                    dispatch_async(dispatch_get_main_queue(), {
                        if let postCell = tableView.cellForRowAtIndexPath(indexPath) as? PostCell {
                            postCell.thumb!.image = image
                        }
                    })
                }
            }
        } else {
            dispatch_async(dispatch_get_main_queue(), {
                if let postCell = tableView.cellForRowAtIndexPath(indexPath) as? PostCell {
                    postCell.thumb!.image = image
                }
            })
        }
    }

    return cell
}

我的简陋故事板: 在此输入图片描述

2
我不确定最佳方法,但以下是几种解决方案:
  1. 像其他人一样使用AFNetworking。它有占位图的想法,替换图像的异步下载和智能缓存。使用CocoaPods安装,创建一个桥接文件并添加 #import "UIImageView+AFNetworking.h"

  2. 创建两种不同类型的单元格。在你的cellForRowAtIndexPath中获取可重用的cell之前,检查它是否已展开。如果已展开,返回并填充一个展开的cell,否则返回并填充一个未展开的cell。如果该单元格是“选定”单元格,则通常会展开该单元格。

结果因人而异。

1
在UITableViewDataSource的tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell实现中调用tableView.cellForRowAtIndexPath是一个巨大的错误。相反,当缩略图的异步获取完成后,更新您的模型以显示该图像,然后请求tableView重新加载特定单元格的indexPath。让您的数据源确定正确的indexPath。如果图片下载完成时单元格不在屏幕上,则不会影响性能。当然,在主线程上重新加载行。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接