关于UITableView的几个秘密

13 篇文章 0 订阅
4 篇文章 0 订阅

1·捉摸不定的contentOffset

UISrollview在滑动的时候,我们要获取其不断变化的contentOffset值,即可通过其协议来获取也可以在其layoutSubviews里面获得,而后者所获取到的offset值会来得频繁很多——当快速滑动的时候,scrollView的协议回调次数远远低于layoutSubviews调用次数,也即contentOffset的获取次数更少,那样一旦我们需要根据contentOffset来做某些精确的工作的话,则效果会更差。

即使我们选择最容易被调用的layoutSubviews,其调用次数也并非都是线性变化的,layoutSubviews被回调的次数是有限(n次1s)的,所以一旦急速滑动,并不会逐像素回调,而是总的滑动距离内调用一定次数,最后每次获得的contentOffset也将呈跳跃状

2·关于UIView的视图层次

发现在addSubview之后通过insertSubview: AtIndex:来设置子视图的层级后,一旦重新修改子视图的frame则这个index将失效。而后面发现了通过view.layer.zPosition的提升则可以令view在其父视图中一直处于高层级或者低层级。

这个是在实现plain模式下tableview的section header/footer停浮时候,需要解决的问题。因为section视图很多是比cell早加入显示区域的(header),那么当滚动到需要header浮在cell的头上时(遮住),则会出现cell遮住header的情况。一开始选择了zPosition,但是发现这个就破坏了视图的原始状态,后面选择的是另一种方式实现plain的section悬浮。

3·UITableView的update相关

多路insert/delete/reload操作,以beginUpdates和endUpdates组成一起的时候。

(1)最好把某种操作的IndexSet/Array全部整合成一个数组,这样最多就只有3个数组了

(2)这3种操作中reload是优先进行的,至于insert和delete,UIKit是让delete先进行,再进行insert,在这里面,reload其实就是做了先delete后insert操作。

(3)经常可以看见某些app进行reload某个cell来进行扩张其内容(cell的height变大)的效果,如果这个cell底部有分割线或者是其他内容的话,那么在这个扩张cell的过程中,动画效果是这个cell下面的cell往下移动,cell的高度被扩充,但是那个分割线却没有移动的效果而是直接出现在了最底部。这是由于我们通常选择了None的动画方式来进行reload,而None的动画方式其实就是单纯的视图hidden.

(4)willInsertCell这个协议在insert动画之前是会被回调的,如果在insert操作里面选择了None的动画枚举,那么兴许可以通过这个协议做点自定义效果动画哦。这个我没有尝试,也不知道apple是否提供了这个机制,我在实现tableview的过程中,新增了数个协议,可以满足在对tableview进行insert/delete的时候进行自定动画的需求。

4·有一个很棒的发现,c++类里面的NSObject成员会在类析构时自动dealloc掉!同样适用于STL容器!

5·UIScrollView的layoutSubviews调用时机

UIScrollView先进行setContentSize再进行addSubview操作,会执行layoutSubviews多次,而如果把setContentSize操作放到最后,那么只会执行一次layoutSubviews

6·UITableView的cell重用限制

UITableView并未对重用cell的数量做限制,至少在我测试过程中,被划出屏幕外的所有cell都没被销毁。这个测试过程中,我将cell按顺序用height递增,即每个cell的高度越来越大,那么将会导致一个情况,越是滑动到后面,显示区域里面的cell会越少(相比之前滑动经过的区域来说),导致了进入重用的cell数量递增(因为前面一屏显示的cell数量会更多),那么这些等待重用的cell虽然数量很大也不会被释放掉的

7·实用的hidden。

UITableView对滑出屏幕的cell都是进行hidden的而非remove,最早发现这个,是我在进行cell入队缓存的时候,发现了一旦频繁的进行cell的remove操作会比使用hidden的cpu使用率高上10%(在iPhone5上面),而后面我天真地以为,UITableView没有发现这一点——实际上是我傻逼了。

在实际开发中,如果不是要销毁了UIView,那么hidden是比直接remove来得快。另外,貌似设置hidden为YES会触发一次其子视图的removeFromSuperView操作。

8·xib和纯frame设置的问题

xib加载的视图再用frame进行设置,接着如果改变父视图的frame会导致其height变为0,那么就是因为xib加载的这个视图的默认autoresizingMask有height相关的,置为none即可。这个问题之前很困扰我,一直没有发现这个猫腻,当然了,只有在前面提到的这个特定条件下(手动改变视图frame,而其上面的子视图又是通过xib加载的)才会出现的。

9·cell的秘密。

(1)dequeueReusableCellWithIdentifier:identifier这个函数不调用并不能帮助你实现cell的不重用,仅需要在进行初始化的时候把reuseIdentifier赋值为nil即可,在这里initWithFrame创建的cell应该就是默认reuseIdentifier为nil了,因为UITableViewCell的NS_DESIGNATED_INITIALIZER不在initWithFrame。

(2)UITableViewCell被点选的触发操作,其实并非在cell内部实现手势点击,而是通过在UITableView里面通过获得当前点击的位置,来判断点击的是哪一个cell,接着调用cell的setSelected:animated函数的。这样做应该也是为了避免耦合吧。

(3)UITableViewCell的选中高亮。当我们选中cell的时候,你会发现cell上面的所有视图,都变成了clearColor来让cell的高亮背景色显示出来,而当取消cell高亮的时候又会变回去,那么这个问题出现了。这些子视图的背景色被动了!那么原本的背景色又被记录在哪里呢,需要复原的时候又能跑出来。

在这里我有2个思路:

·继承UIColor来写一个类,提供一个属性,受记录的颜色,当setBackgroundColor的时候碰到的是这个class,我们就用他记录的颜色来设置颜色。每次要将所有子视图都设置为clearColor的时候,就将这些子视图的背景色都用这个类记录下来。

·用一个容器存储这些视图对应的背景色,按键值对应,这里的key则选用每个UIView的内存地址,我这边选的是这个方法。

很重要的一点是,当cell上面的willRemoveSubview被触发时,嘿嘿,如果当前cell呈被选中高亮状态,他就会帮这个要被remove的子视图回复原有的背景色啦。

(4)UITableViewCell的layoutSubviews。说实话cell上面的那些默认元素,titleLabel和删除按钮左右滑动的,很多时候是没有用的。而我们一旦在UITableViewCell子类的layoutSubviews里面进行super layoutSubviews操作,也会照顾到这些默认视图,实测中也会使cpu的占用率下滑几个%。

==============================分割线=================================

下面介绍一下博主的全新tableview,所能提供的,对UITableView的改进的功能点(好像这才是写这篇文章的真实目的:

·delegate和dataSource两个协议以及各种public方法都同UITableView,然后我将header/footer和其高度的协议都放在了dataSource,也就是所有数据返回的协议都放在dataSource了。

·缓存所有的高度信息,在每次reload的时候记住。缓存cell高度这个问题网上有百度的sunny写的非常棒的cell缓存工具FDxxxx来辅助UITableView。在我这边则是默认就是存储到数组里面(因为做成动态的很难...),这样一来又怕有性能问题——比如你在tableview获取到网络数据之后,一口气刷1000+的cell出来并且每个cell的高度计算都很麻烦,那么我也提供了一个方法- (void)reloadDataAsyncWithCompletion:(void(^)(void))completion来进行异步的加载cell高度,我选择了gcd的全局队列去计算和缓存高度完了再主线程刷新tableview——在这个过程中,如果你的tableview先前还有内容显示,那么在这个异步计算过程中,你仍然可以滑动浏览原本的内容。

·更加流畅的update动画,特别针对plain模式下,进行多路的insert/delete/reload操作之后,section的header/footer都会有很奇怪的运动轨迹。我也提供了自定义对insert/delete操作时候的动画的协议。

·可以选择强制在reload的时候,继续重用之前产生的cell重用对象。UITableView的每次reload都会清除所有数据,可是我们很多时候,其实cell都是跟先前的一样,这样每次reload,无疑是多了删除旧的cell和创建新的过程——而实际上我们可以使用旧的。

·还有一些边边角角的改动,总体都是跟UITableView一样的。然后说到最重要的功能,就是同样的cell配置下,cpu的占用率一定会比UITableView来得低!!!再在同样的update操作下面,cpu的占用率也比UITableView来得低!!!


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在iOS中,一个视图只能有一个UITableView。但是可以通过创建多个UITableView来实现一个视图中显示多个表格的效果。以下是一个示例代码: 首先,你需要在视图控制器中添加多个UITableView的实例变量: ```swift class YourViewController: UIViewController { var tableView1: UITableView! var tableView2: UITableView! // ... } ``` 然后,在视图加载完成后,你可以创建和配置这些UITableView的实例: ```swift override func viewDidLoad() { super.viewDidLoad() // 创建第一个UITableView tableView1 = UITableView(frame: CGRect(x: 0, y: 0, width: view.frame.width, height: view.frame.height/2)) tableView1.dataSource = self tableView1.delegate = self view.addSubview(tableView1) // 创建第二个UITableView tableView2 = UITableView(frame: CGRect(x: 0, y: view.frame.height/2, width: view.frame.width, height: view.frame.height/2)) tableView2.dataSource = self tableView2.delegate = self view.addSubview(tableView2) // ... } ``` 接下来,你需要实现UITableViewDataSource和UITableViewDelegate协议的相关方法来提供表格的数据和处理交互事件。例如: ```swift extension YourViewController: UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { if tableView == tableView1 { // 返回第一个UITableView的行数 return 10 } else if tableView == tableView2 { // 返回第二个UITableView的行数 return 5 } return 0 } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) if tableView == tableView1 { // 配置第一个UITableView的单元格 cell.textLabel?.text = "Table View 1 - Row \(indexPath.row)" } else if tableView == tableView2 { // 配置第二个UITableView的单元格 cell.textLabel?.text = "Table View 2 - Row \(indexPath.row)" } return cell } } extension YourViewController: UITableViewDelegate { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { if tableView == tableView1 { // 处理第一个UITableView的行选中事件 print("Table View 1 - Row \(indexPath.row) selected") } else if tableView == tableView2 { // 处理第二个UITableView的行选中事件 print("Table View 2 - Row \(indexPath.row) selected") } } } ``` 这样,你就可以在同一个视图中使用多个UITableView了。记得在视图控制器中遵循UITableViewDataSource和UITableViewDelegate协议,并在视图加载完成后设置数据源和代理。 希望这能帮到你!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值