内容简介:讲到闲话少说,最近在写关于
讲到 UITableView
,大家一定都不陌生。有一个相对夸张的说法,叫做学好 UITableView
,你就是一名合格的 iOS
工程师
闲话少说,最近在写 Swift
的过程中碰到了以下几个问题,特别在此记录。
遇到的问题
-
cellForRowAtIndexPath
代理中,对cell
(尤其是自定义cell
) 的初始化异同- 和
OC
的区别 —— 不能使用OC
的那种判空方式来初始化 - 初始化不能使用自定义的方法 —— 通过
dequeue
方法得到的cell
永远都是非空的,换言之,即便你自定义了一个初始化方法,它也不会被执行到。 - 通过渲染方式(render)来绘制图像,赋值
- 和
- 刷新的问题
- 使用
reloadData
时候,在iOS 11
上会产生抖动 - 慎用局部刷新
reloadRows
的相关方法,会造成cell
复用混乱 -
insertRow
和deleteRow
和reloadRows
一样都属于局部刷新的范畴,需要用beginUpdate
和endUpdate
包起来。这上上一点类似
- 使用
先明确两个概念
- 代码中的
setup
表示只会执行一次,而且在 cell 的初始化中表示他的绘图(不带数据)也只会执行一次 - 代码中的
render
表示渲染,实际上是意味着setup
已经完成了绘图,我要在每次重用时把数据传进去渲染
初始化问题 cellForRowAtIndexPath
关于 cellForRowAtIndexPath
的初始化问题其实在这篇文章中已经讨论过,这里不作赘述 Swift 踩坑笔记(二)—— 初始化Tableview 及自定义 TableviewCell
我们要讨论的是在 cell
复用过程中的赋值问题。
简单的来说,tableview 的复用机制是我们在 cellForRowAtIndexPath
初始化时绘制好必要的控件及相关约束,但是并不会去赋值。因为每次上下滚动都会重新从复用池中取出 cell,将 DataSource 对应的数据赋值一次 . 鉴于 Swift
无法自定义 cell
的初始化,那么上下滚动时,怎么重新赋值而不重复绘制就显得格外重要。
来看下面的代码
// tableview 代理 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: someCellID, for: indexPath) as! MyCell cell.renderCell(info: dataSource[indexPath.row]) return cell } 复制代码
再来看下面的 Cell
示意图
我们这里的 Cell
分了很多层级,
除了顶部的 Header
区域是固定知道的高度外,下面的 区域 InfoA, InfoB, InfoC ...
等等,都是根据具体的信息去绘制的。 换言之,我不知道每个 Cell 具体要画几个 InfoX
普通的 cell
初始化,只能满足固定的视图,这种动态的,只能用 cell
自定义一个 render
方法来做了。
再来看下面的这段 自定义 Cell
的代码
// 略去类的初始化,这里为了 render 的目的,必须去持有这两块视图 private var headerBaseInfoView: IGGIDBaseInfoView private var infoViews: [infoView] = [] public func renderCell(info: IGGIDAccountModel) { headerBaseInfoView.render(renderInfo: info.baseInfo) renderInfoViews(info.infos) } private func renderInfoViews(_ infos: [someInfoModel]?) { guard let infos = infos, infos.count > 0 else { return } // 如果已经创建过了,那么只要赋值就可以了 if (infoViews.count > 0) { for (index, bindInfo) in bindInfos.enumerated() { let infoView = infoViews[index] infoView.render(info) } } else { setupInfoViews(infos) // 如果没有持有,表示初次渲染,这时候要赋值 } } private func setupPlatformBindInfoViews(_ infos: Array<Info>) { for (index, bindInfo) in infos.enumerated() { let infoView = InfoView() containerView.addSubview(infoView) infoView.snp.makeConstraints { (make) in //... 写约束 } infoViews.append(infoView) infoView.render(info) // 记得再去调用一次 } } 复制代码
下面是讲解:
- 类中要去持有那些会被渲染的视图,作为属性内容。
- headerBaseInfoView 是固定的内容,所以实际上我们在重写他的初始化方法的时候,直接就把
setupUI()
(只会执行一次)这个绘图的工作做掉了 - infoViews 属于我一开始没办法知道你有几个,所以我无法初始化。因此我只能在
render
的时候做下面两个操作:- 先对
infoViews
判空,不存在,那么就做第一次的setupUI
操作,这时候根据model
也已经知道数量了,先把绘图工作做了。- 绘图完毕之后,也要做一次正规的
render
操作,不然会没数据,变成空白
- 绘图完毕之后,也要做一次正规的
- 完成了
setupUI
之后,刷新数据
- 先对
综上所述,就是和概念中说的一样:
cell
的操作都是先通过 setupUI
绘制视图,然后去 render
把数值赋过去。只是一些条件下,我们不知道要画几个,那么只能在 render
的时候根据当前数据,补上 setupUI
之后,再去做真正的 render
刷新的问题
先来说说 reloadData
的缺点
-
性能问题 我们都知道,
UITableview
中reloadData
是需要慎用的。因为他会将整个tableview
都刷新一遍。这意味着也许我只需要刷新2个cell
,你却让所有的cell
都重渲染了一遍。从性能而言这显然是不可取的。 所以我们才会想到去用局部刷新。 -
reloadData
无法像系统提供的其他刷新方法一样,带有animate
参数,这让刷新时,整个页面看起来非常突兀。如果你不自己加动画,那么体验真的不太好 -
在
iOS 11
上会有一个问题,就是重载之后页面会乱跑:-
解决办法:
google
后,得到的内容是说 Self-Sizing在iOS11下是默认开启的,Headers, footers, and cells都默认开启Self-Sizing,所有estimated 高度默认值从iOS11之前的 0 改变为UITableViewAutomaticDimension
if #available(iOS 11.0, *) { taleview.estimatedRowHeight = 0 taleview.estimatedSectionHeaderHeight = 0 taleview.estimatedSectionFooterHeight = 0 } 复制代码
-
局部刷新的问题
鉴于上面讲的 reloadData
,我们很自然的就会想到使用局部刷新来做。
tableview.beginUpdates() tableview.reloadRows(at: tableview.indexPathsForVisibleRows!, with: .none) tableview.endUpdates() 复制代码
然而,事实比我们想象的要残酷:
局部刷新的效果
使用 reveal 查看,发现多了一个层级,盖在应该有的位置
我查看了官方的文档,也自己做了断点调试,发现:
不会创建新的cell cell
下面两篇文章也提到了类似的问题。参考文章一 慎用局部刷新
目前来看,似乎是 OC
下不会出问题, swift 3
(我用的是4.0的版本)下会有这个 bug。 目前还是先使用 reloadData
的全局刷新替换局部刷新,希望后续会有更好的办法
如果有更好的办法解决,欢迎告知。
以上所述就是小编给大家介绍的《Swift 踩坑笔记 —— UITableView Cell初始化和刷新的问题探讨》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- Swift iOS : 上拉刷新或者下拉刷新
- Swift iOS : 上拉刷新或者下拉刷新
- C++ 的一大误区——深入解释直接初始化与复制初始化的区别
- 初始化监听端口
- 类初始化导致死锁
- nodejs源码—初始化
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Designing Data-Intensive Applications
Martin Kleppmann / O'Reilly Media / 2017-4-2 / USD 44.99
Data is at the center of many challenges in system design today. Difficult issues need to be figured out, such as scalability, consistency, reliability, efficiency, and maintainability. In addition, w......一起来看看 《Designing Data-Intensive Applications》 这本书的介绍吧!