我有一个UITableView
来加载来自服务的图片。它会抓取网址并使用NSURLSession.sharedSession().dataTaskWithURL
下载图片:
NSURLSession.sharedSession().dataTaskWithURL(url, completionHandler: { (data, _, error) -> Void in
guard
let data = data where error == nil,
let image = UIImage(data: data)
else {
print(error?.localizedDescription)
return
}
dispatch_async(dispatch_get_main_queue()) {
[unowned self] in
self.pictureView.image = image
self.pictureView.layoutIfNeeded()
}
}).resume()
UIImageView
是故事板场景的出路。 UIImageView
内容模式是Aspect Fit。
可见单元格的UIImageView
最初显示为空(无图像),即使我已确认已设置图像。直到单元格在屏幕上滚动,单元格才会重绘(?)并显示图像。
所以这似乎是某种时间问题,但我无法弄明白。我已经尝试在设置图像后在dispatch_async
块中调用以下所有内容:
self.pictureView.setNeedsDisplay()
self.pictureView.setNeedsUpdateConstraints()
self.pictureView.setNeedsLayout()
self.pictureView.reloadInputViews()
这些都没有解决问题。是什么导致此图像仅在单元格滚出/进入视图后显示?
编辑:
值得注意的是,这是在笔尖和相关类中发生的。笔尖被加载,然后我开始下载。我想知道笔尖是否自行放置,当我的图像被下载并设置在UIImageView
上时,布局传递已经完成。然后通过在屏幕上移动单元格(包含笔尖),单元格再次执行布局,并且所有内容都正确显示。只是一个猜测,我仍然坚持这一点。
编辑2: 进一步澄清该过程:
1)我的UIViewController
包含UITableView
。
2)此UITableView
从名为PostCell的自定义UITableViewCell
类中加载单元格。
3)PostCell的全部内容是从名为PostView(带有PostView类)的笔尖加载的单个视图。该负载发生在PostCell中的init:style:reuseIdentifier
期间:
postView = NSBundle.mainBundle().loadNibNamed("PostView", owner: self, options: nil).first as? PostView
4)然后我在postView
上设置了一个Post对象:
postView.post = Post
请注意,这会在PostView上调用awakeFromNib()
后发生。
5)PostView上的post
属性有一个didSet
观察者,它根据post
对象中的URL(上面的代码)启动图像下载。该图片已下载并设置在名为UIImageView
的{{1}}上。
编辑3:
以下是pictureView
的代码:
tableView(_:cellForRowAtIndexPath:)
编辑4:
我发现如果我通过在笔尖中预先设置占位符图像来“填充”func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier(postCellIdentifier) as? PostCell
if cell == nil {
cell = PostCell()
}
if let postCell = cell,
let postArray = postArray,
let post = postArray[indexPath.row] {
postCell.post = post
return postCell
}
// This section is hit during a refresh.
let emptyCell = UITableViewCell()
emptyCell.contentView.backgroundColor = .whiteColor()
return emptyCell
}
,然后将我的图像加载到UIImageView
,图像将显示在高度为占位符。当我将该单元格从屏幕上移开然后再次打开时,UIImageView
将使用笔尖中的约束设置将图像绘制为正确的大小。
因此,似乎UIImageView
的约束和大小已经在图像加载时设置,并且在屏幕上拉/屏幕会导致重新绘制某种类型,从而正确调整图像大小。如果我能以编程方式触发重绘它会有所帮助,但请参阅上面我已经尝试过的内容。
编辑6:
这是在行动。请注意,我在笔尖中设置了占位符图像。这似乎导致图像在最初加载时显示,但在占位符的维度上显示。在屏幕外滚动它会导致调整大小,然后正确显示。
编辑7:
我在我的应用程序中的其他位置使用PostView nib没有问题。加载时,图像大小正确。问题似乎只出现在nib嵌入UIImageView
。
编辑8:
我找到了这个问题的根本原因,在UITableViewCell
的viewDidLoad中调用:
UIViewController
使用自动尺寸会导致初始错误的行高。出于某种原因,将单元格滚动到视图中会正确地重新计算行高,并且图像显示在正确的高度。删除此代码将显示图像,但行高不会从自动高度计算中受益。自动计算行高的最佳方法是什么,仍然可以避免这个问题?
答案 0 :(得分:4)
你可以试试这个扩展名:
extension UIImageView {
func downloadedFrom(link link:String, contentMode mode: UIViewContentMode) {
guard
let url = NSURL(string: link)
else {return}
contentMode = mode
NSURLSession.sharedSession().dataTaskWithURL(url, completionHandler: { (data, response, error) -> Void in
guard
let httpURLResponse = response as? NSHTTPURLResponse where httpURLResponse.statusCode == 200,
let mimeType = response?.MIMEType where mimeType.hasPrefix("image"),
let data = data where error == nil,
let image = UIImage(data: data)
else { return }
dispatch_async(dispatch_get_main_queue()) { () -> Void in
self.image = image
}
}).resume()
}
}
然后你可以把这条线放在任何你想要的地方
cell.cellPhoto.downloadedFrom(link: "customImageUrl", contentMode: .ScaleAspectFit) //Try changing contentMode
答案 1 :(得分:2)
将表格视图的单元格出列后,单元格和表格视图可能有不同的宽度。此时您的单元格尚未正确调整大小。 因此,在从表视图中出列后更改这段代码:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
...
if let postCell = cell,
let postArray = postArray,
let post = postArray[indexPath.row] {
// set the post with image after the cell is sized correctly
dispatch_async(dispatch_get_main_queue()) {
postCell.post = post
}
return postCell
}
确保在单元格nib文件中正确设置了约束。
我建议在控制器viewDidLoad方法中使用registerNib方法:
tableView?.registerNib(UINib(nibName: "PostCellName", bundle: NSBundle.mainBundle()), forCellReuseIdentifier: postCellIdentifier)
答案 2 :(得分:2)
你见过WWDC 2014 Session 226吗?该视频可能会对您的问题有所了解。
在WWDC会话中,他们使用您已经完成的self.tableView.estimatedRowHeight = 100.0;
,然后他们说您不需要表格视图控制器中的任何虚拟原型单元格。
问题是当故事板加载表视图时,它会将rowHeight
属性设置为xib / interface builder中的值。要使用自动调整大小单元格,您需要设置estimatedRowHeight
并将rowHeight
保留为默认值 - UITableViewAutomaticDimension
。 (你已经完成了)✅
self.tableView.rowHeight = UITableViewAutomaticDimension
⚠️新问题是在你有一个真正的行高之前,第一个单元格会加载。诀窍可能是在视图显示时强制重新加载表吗?
[super viewDidAppear:animated]
[self.tableView reloadData]
如果这不起作用,那么您可能会无意中在某些导致其无法正确调整大小的内容上设置显式 contentWidth / Height
答案 3 :(得分:1)
下载完成后,您需要重新加载tableview数据。尝试在self.tableview.reloadData()
块
dispatch_async
如果您想异步下载网址中的图片,我建议您使用Kingfisher
答案 4 :(得分:0)
在查看编辑过程(当前8个)和响应者的更新后,似乎您在导致errors in init mentioned here的init方法中遇到了相同的错误。
应该给予Prcela注册nib的正确轨道,但下面提供了一个更优雅的解决方案:
在nib文件中创建自定义表格视图单元格的最简单方法(自iOS 5.0起可用)是在表格视图控制器中使用registerNib:forCellReuseIdentifier:最大的优点是dequeueReusableCellWithIdentifier:然后在必要时自动从nib文件中实例化一个单元格。你不再需要if(cell == nil)... part。
在viewDidLoad
[self.tableView registerNib:[UINib nibWithNibName:@"CueTableCell" bundle:nil] forCellReuseIdentifier:@"CueTableCell"];
在cellForRowAtIndexPath
添加
CueTableCell *cell = [tableView dequeueReusableCellWithIdentifier:@"CueTableCell"];
// setup cell
return cell;
使用initWithCoder实例化从nib文件加载的单元格 如有必要,可以覆盖子类中的那个。对于修改 UI元素,你应该覆盖awakeFromNib(别忘了 叫“超级”)。
我希望我能为这样一个优雅的解决方案而受到赞誉,但这个功劳属于来自同一帖子的Martin R。