Imr*_*deh 10 uitableview ios swift haneke
我有一个tableview用代码创建的(没有storyboard):
class MSContentVerticalList: MSContent,UITableViewDelegate,UITableViewDataSource {
var tblView:UITableView!
var dataSource:[MSC_VCItem]=[]
init(Frame: CGRect,DataSource:[MSC_VCItem]) {
super.init(frame: Frame)
self.dataSource = DataSource
tblView = UITableView(frame: Frame, style: .Plain)
tblView.delegate = self
tblView.dataSource = self
self.addSubview(tblView)
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataSource.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .Subtitle, reuseIdentifier: nil)
let record = dataSource[indexPath.row]
cell.textLabel!.text = record.Title
cell.imageView!.downloadFrom(link: record.Icon, contentMode: UIViewContentMode.ScaleAspectFit)
cell.imageView!.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
print(cell.imageView!.frame)
cell.detailTextLabel!.text = record.SubTitle
return cell
}
}
Run Code Online (Sandbox Code Playgroud)
在其他类我有一个下载图像的扩展方法Async:
extension UIImageView
{
func downloadFrom(link link:String?, contentMode mode: UIViewContentMode)
{
contentMode = mode
if link == nil
{
self.image = UIImage(named: "default")
return
}
if let url = NSURL(string: link!)
{
print("\nstart download: \(url.lastPathComponent!)")
NSURLSession.sharedSession().dataTaskWithURL(url, completionHandler: { (data, _, error) -> Void in
guard let data = data where error == nil else {
print("\nerror on download \(error)")
return
}
dispatch_async(dispatch_get_main_queue()) { () -> Void in
print("\ndownload completed \(url.lastPathComponent!)")
self.image = UIImage(data: data)
}
}).resume()
}
else
{
self.image = UIImage(named: "default")
}
}
}
Run Code Online (Sandbox Code Playgroud)
我在其他地方使用此功能并正常工作,根据我的日志,我了解下载的图像没有问题(当单元格被渲染时)和下载图像后,单元格UI未更新.
我也尝试使用像Haneke这样的缓存库,但问题是存在而不是改变.
请帮我理解错误
谢谢
问题是,.Subtitle的演绎UITableViewCell将尽快布局对小区的cellForRowAtIndexPath收益(覆盖您尝试将frame图像视图).因此,如果您异步检索图像,则单元格将被重新布局,就像没有要显示的图像一样(因为您没有将图像视图的image属性初始化为任何内容),并且当您imageView稍后异步更新时,将以某种方式布置单元格,使您无法看到下载的图像.
这里有几个解决方案:
您不仅可以在没有URL时downloadFrom更新映像,default还可以在有URL时更新映像(因此您首先将其设置为默认映像,然后将映像更新为从网络下载的映像):
extension UIImageView {
func downloadFrom(link link:String?, contentMode mode: UIViewContentMode) {
contentMode = mode
image = UIImage(named: "default")
if link != nil, let url = NSURL(string: link!) {
NSURLSession.sharedSession().dataTaskWithURL(url) { data, response, error in
guard let data = data where error == nil else {
print("\nerror on download \(error)")
return
}
if let httpResponse = response as? NSHTTPURLResponse where httpResponse.statusCode != 200 {
print("statusCode != 200; \(httpResponse.statusCode)")
return
}
dispatch_async(dispatch_get_main_queue()) {
print("\ndownload completed \(url.lastPathComponent!)")
self.image = UIImage(data: data)
}
}.resume()
} else {
self.image = UIImage(named: "default")
}
}
}
Run Code Online (Sandbox Code Playgroud)
这确保了单元格将被布置为存在图像,无论如何,因此图像视图的异步更新将起作用(类似于:见下文).
您可以创建自己的单元格原型,而不是使用动态布局的.Subtitle再现UITableViewCell,您可以使用固定大小的图像视图进行适当的布局.这样,如果没有立即可用的图像,它将不会重新格式化单元格,就像没有可用的图像一样.这使您可以使用autolayout完全控制单元格的格式.
您还可以定义downloadFrom方法以获取另外的第三个参数,即下载完成后您将调用的闭包.然后你可以做一个reloadRowsAtIndexPaths封闭内部.但是,假设您修复此代码以缓存下载的图像(NSCache例如),以便您可以在再次下载之前检查是否有缓存的图像.
话虽如此,正如我上面提到的,这个基本模式存在一些问题:
如果向下滚动然后向上滚动,则将从网络重新检索图像.您确实希望在再次检索之前缓存先前下载的图像.
理想情况下,您的服务器响应标头配置正确,以便内置的NSURLCache将为您处理此问题,但您必须测试它.或者,您可以自己将图像缓存NSCache.
如果你快速向下滚动到第100行,你真的不希望可见的单元格在前面不再可见的99行的图像请求后面积压.你真的想取消滚动屏幕的单元格的请求.(或者dequeueCellForRowAtIndexPath,在重新使用单元格的地方使用,然后您可以编写代码来取消之前的请求.)
如上所述,您确实希望这样做,dequeueCellForRowAtIndexPath以便您不必不必要地实例化UITableViewCell对象.你应该重用它们.
就个人而言,我可能会建议您(a)使用dequeueCellForRowAtIndexPath,然后(b)将其与一个完善的UIImageViewCell类别(如AlamofireImage,SDWebImage,DFImageManager或Kingfisher)结合使用.对先前请求进行必要的缓存和取消是一项非常重要的练习,使用其中一个UIImageView扩展将简化您的生活.如果您决定自己这样做,您可能还想查看这些扩展的一些代码,以便您可以了解如何正确执行此操作.
-
例如,使用AlamofireImage,您可以:
定义自定义表视图单元子类:
class CustomCell : UITableViewCell {
@IBOutlet weak var customImageView: UIImageView!
@IBOutlet weak var customTitleLabel: UILabel!
@IBOutlet weak var customSubtitleLabel: UILabel!
}
Run Code Online (Sandbox Code Playgroud)将单元格原型添加到表视图故事板中,指定(a)基类CustomCell; (b)故事板编号CustomCell; (c)将图像视图和两个标签添加到您的单元格原型中,将其连接@IBOutlets到您的CustomCell子类; (d)添加定义图像视图的位置/大小和两个标签所需的任何约束.
您可以使用自动布局约束来定义图像视图的尺寸
你cellForRowAtIndexPath可以做以下事情:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("CustomCell", forIndexPath: indexPath) as! CustomCell
let record = dataSource[indexPath.row]
cell.customTitleLabel.text = record.Title
cell.customSubtitleLabel.text = record.SubTitle
if let urlString = record.Icon {
cell.customImageView.af_setImageWithURL(NSURL(string: urlString)!)
}
return cell
}
Run Code Online (Sandbox Code Playgroud)这样,您不仅可以享受基本的异步图像更新,还可以享受图像缓存,可见图像的优先级排序,因为我们重新使用出列单元格,效率更高等等.并且通过使用带有约束的单元格原型和您的自定义表视图单元子类,一切都正确布局,使您无需手动调整frame代码.
无论UIImageView您使用哪种扩展程序,这个过程都大致相同,但目标是让您摆脱自己编写扩展程序的杂草.
| 归档时间: |
|
| 查看次数: |
15066 次 |
| 最近记录: |