深挖UITableViewCell-编辑多选模式下,引发的深思

  • 前言

在移动端日常开发中,列表**UITableView**的使用频率非常高,而TableView里主要用的就是UITableViewCell,不管是自定义cell,还是系统的cell,还是后期为了优化列表,都离不开操作cell,那么UITableViewCell就有必要研究一下了;至于为什么要整理一篇关于Cell的文章,因为,搜了一下关于cell的文章,基本都是把头文件里面的方法简单介绍一下,没有介绍编辑模式下,cell的一些问题;

[说明规定]: 一些简单语法,便于说明问题:
* A-->B 意思是: A继承B,B是A的父类;

另外,不要觉得自己平时常用,然后想当然以为,自己对于这个"出镜率"极高的控件极为熟悉,曾经我也那么以为,然而,毕竟我们:

太年轻

  • 问题引导

遇到如下问题:猜猜看,可能是什么原因造成的?我在平时比较活跃的QQ交流群问了一下,然后石沉大海了......

bug

这只是其中在cell编辑多选中遇到一个问题;

还有一个就是,当用xib布局cell的时候,tableView进入编辑模式,多选不能全选cell,都是一些奇葩的问题;

  • 这篇文章主要说明什么问题
  1. Cell内部结构;
  2. 回答文章一开始的问题;
  3. Cell的重用机制;
  4. 能不能自己仿照系统Cell,实现一个继承自UIView并且重用的cell;
  • 一.cell内部结构

我们知道,cell可以根据不同的style显示不同的类型;
比如下面的几种:

  1. 右边显示感叹号或者对勾;
  2. 编辑模式,左边显示勾选框;
  • Cell内部有什么
  1.  cell的继承关系

首先,OC中,所有的类都是对象,都是继承自NSObject,这个元类;每个对象都有一个__isa__指针指向它的父类,实例对象的isa指针指向创建他的本类,类对象isa指向它的父类,元类指向它本身;这一块不做讨论;

不知你是否好奇,为什么对象都有一个isa指针?其实,OC对象中isa指针,并不是OC独有的,在Python中,也有类似的,isa分开来就是:

is a ,后面是省略的 kind of ,合起来就是 is a kind of ...翻译过来就是:是....的一类或者跟...是同一类的;

好理解,子类继承父类,便是 Son -> Father,B is A,就是B继承A;

当然以UI开头的属于UIKIt框架范畴,但是,所有的对象都是继承自NSObject;

 UITableViewCell --> UIView --> UIResponder --> NSObject

  • 工具:Runtime动态获取cell内部控件;

通过获取cell内部私有的子类,我们知道,除了TextLable,ContentView,ImageView等,还有重用标识符**reuseIdentifier**

cell内部的控件结构图如下:
cell内部结构

Runtime获取一个类的中所有的私有属性:
[备注]:需要导入Runtime相关的头文件
#import <objc/runtime.h> #import <objc/message.h>_

unsigned int count;
Ivar *ivarList = class_copyIvarList([cell class], &count);
for (int i = 0; i < count; i++) {
Ivar ivar = ivarList[i];
NSLog(@"%s", ivar_getName(ivar));
}
free(ivarList);
2017-11-30 22:10:42.095 UITtableViewCell[66184:9589083] _tableView
2017-11-30 22:10:44.336 UITtableViewCell[66184:9589083] _layoutManager
2017-11-30 22:10:46.753 UITtableViewCell[66184:9589083] _target
2017-11-30 22:10:48.696 UITtableViewCell[66184:9589083] _editAction
2017-11-30 22:10:50.070 UITtableViewCell[66184:9589083] _accessoryAction
2017-11-30 22:10:51.967 UITtableViewCell[66184:9589083] _oldEditingData
2017-11-30 22:10:51.967 UITtableViewCell[66184:9589083] _editingData
2017-11-30 22:10:51.967 UITtableViewCell[66184:9589083] _rightMargin
2017-11-30 22:10:51.967 UITtableViewCell[66184:9589083] _indentationLevel
2017-11-30 22:10:51.967 UITtableViewCell[66184:9589083] _indentationWidth
2017-11-30 22:10:51.968 UITtableViewCell[66184:9589083] _reuseIdentifier
2017-11-30 22:10:51.968 UITtableViewCell[66184:9589083] _floatingContentView
2017-11-30 22:10:51.968 UITtableViewCell[66184:9589083] _lineBreakModeBeforeFocus
2017-11-30 22:10:51.968 UITtableViewCell[66184:9589083] _contentView
2017-11-30 22:10:51.968 UITtableViewCell[66184:9589083] _imageView
2017-11-30 22:10:51.968 UITtableViewCell[66184:9589083] _textLabel
2017-11-30 22:10:51.968 UITtableViewCell[66184:9589083] _detailTextLabel
2017-11-30 22:10:51.969 UITtableViewCell[66184:9589083] _backgroundView
2017-11-30 22:10:51.969 UITtableViewCell[66184:9589083] _selectedBackgroundView
2017-11-30 22:10:51.969 UITtableViewCell[66184:9589083] _multipleSelectionBackgroundView
2017-11-30 22:10:51.974 UITtableViewCell[66184:9589083] _selectedOverlayView
2017-11-30 22:10:51.974 UITtableViewCell[66184:9589083] _selectionFadeDuration
2017-11-30 22:10:51.974 UITtableViewCell[66184:9589083] _backgroundColor
2017-11-30 22:10:51.974 UITtableViewCell[66184:9589083] _separatorColor
2017-11-30 22:10:51.975 UITtableViewCell[66184:9589083] _separatorEffect
2017-11-30 22:10:51.975 UITtableViewCell[66184:9589083] _topShadowColor
2017-11-30 22:10:51.975 UITtableViewCell[66184:9589083] _bottomShadowColor
2017-11-30 22:10:51.975 UITtableViewCell[66184:9589083] _sectionBorderColor
2017-11-30 22:10:51.975 UITtableViewCell[66184:9589083] _floatingSeparatorView
2017-11-30 22:10:51.975 UITtableViewCell[66184:9589083] _topShadowAnimationView
2017-11-30 22:10:51.976 UITtableViewCell[66184:9589083] _bottomShadowAnimationView
2017-11-30 22:10:51.976 UITtableViewCell[66184:9589083] _badge
2017-11-30 22:10:51.976 UITtableViewCell[66184:9589083] _unhighlightedStates
2017-11-30 22:10:51.976 UITtableViewCell[66184:9589083] _highlightingSupport
2017-11-30 22:10:51.976 UITtableViewCell[66184:9589083] _selectionSegueTemplate
2017-11-30 22:10:51.976 UITtableViewCell[66184:9589083] _accessoryActionSegueTemplate
2017-11-30 22:10:51.977 UITtableViewCell[66184:9589083] _accessoryActionPreviewingSegueTemplateStorage
2017-11-30 22:10:51.977 UITtableViewCell[66184:9589083] _accessoryView
2017-11-30 22:10:52.008 UITtableViewCell[66184:9589083] _editingAccessoryView
2017-11-30 22:10:52.008 UITtableViewCell[66184:9589083] _customAccessoryView
2017-11-30 22:10:52.009 UITtableViewCell[66184:9589083] _customEditingAccessoryView
2017-11-30 22:10:52.009 UITtableViewCell[66184:9589083] _separatorView
2017-11-30 22:10:52.009 UITtableViewCell[66184:9589083] _topSeparatorView
2017-11-30 22:10:52.009 UITtableViewCell[66184:9589083] _topShadowView
2017-11-30 22:10:52.010 UITtableViewCell[66184:9589083] _editableTextField
2017-11-30 22:10:52.010 UITtableViewCell[66184:9589083] _lastSelectionTime
2017-11-30 22:10:52.010 UITtableViewCell[66184:9589083] _deselectTimer
2017-11-30 22:10:52.010 UITtableViewCell[66184:9589083] _textFieldOffset
2017-11-30 22:10:52.010 UITtableViewCell[66184:9589083] _indexBarWidth
2017-11-30 22:10:52.010 UITtableViewCell[66184:9589083] _separatorInset
2017-11-30 22:10:52.011 UITtableViewCell[66184:9589083] _backgroundInset
2017-11-30 22:10:52.011 UITtableViewCell[66184:9589083] _returnAction
2017-11-30 22:10:52.011 UITtableViewCell[66184:9589083] _selectionTintColor
2017-11-30 22:10:52.011 UITtableViewCell[66184:9589083] _accessoryTintColor
2017-11-30 22:10:52.011 UITtableViewCell[66184:9589083] _reorderControlImage
2017-11-30 22:10:52.012 UITtableViewCell[66184:9589083] _menuGesture
2017-11-30 22:10:52.012 UITtableViewCell[66184:9589083] _representedIndexPath
2017-11-30 22:10:52.012 UITtableViewCell[66184:9589083] _focusable
2017-11-30 22:10:52.013 UITtableViewCell[66184:9589083] _swipeToDeleteConfirmationView
2017-11-30 22:10:52.013 UITtableViewCell[66184:9589083] _swipeToDeleteCancelationGesture
2017-11-30 22:10:52.013 UITtableViewCell[66184:9589083] _clearBlendingView
2017-11-30 22:10:52.013 UITtableViewCell[66184:9589083] _swipeToDeleteDistancePulled
2017-11-30 22:10:52.013 UITtableViewCell[66184:9589083] _sectionCornerRadius
2017-11-30 22:10:52.014 UITtableViewCell[66184:9589083] _sectionBorderWidth
2017-11-30 22:10:52.014 UITtableViewCell[66184:9589083] _defaultMarginWidth
2017-11-30 22:10:52.014 UITtableViewCell[66184:9589083] _editControlFocusGuide
2017-11-30 22:10:52.014 UITtableViewCell[66184:9589083] _reorderControlFocusGuide
2017-11-30 22:10:52.014 UITtableViewCell[66184:9589083] _constants
2017-11-30 22:10:52.014 UITtableViewCell[66184:9589083] _tableCellFlags
2017-11-30 22:10:52.015 UITtableViewCell[66184:9589083] _isLayoutEngineSuspended

 

  • 二 文章一开始的问题
  1. 2.1 为什么会有这个问题?

项目中,基本上都是多人协作开发,有些UI是复用的,团队中,又没有__codeReview__,每个人对自己要求又不一样,在日常开发中,由于我们偷懒,能少写一点就少写一点,而文章出现的自定义cell正好是copy过来的,也没检查,当在非编辑模式下,显示正常;当在编辑模式下,就出问题了?

  •  解决办法


首先,在编辑模式下,正常的话,cell整体发生位移了,那么,只要涉及到控件位移的,第一就是打印frame,我们看下,编辑时和非编辑情况下,cell的frame变化情况:

  • tableView非编辑下,cell的frame:

 非编辑cell Frame:<UITableViewCell: 0x7f9bd2020600; frame = (0 0; 320 44)

  • tableView编辑下,cell的frame

编辑下cell Frame:<UITableViewCell: 0x7f9bd401ea00; frame = (0 0; 320 44);

我们惊奇的发现,两种模式下,cell的frame居然是一致的,那也就是说:
编辑状态下,cell的整体位移不是通过修改cell本身的frame实现的;

接下来,cell内部还有一个contentView,我们打印它的frame:

  • tableView非编辑下,cell.contentView的frame:

 非编辑cell Frame:<UITableViewCellContentView: 0x7f9fc31374a0; frame = (0 0; 320 44);

  • tableView编辑下,cell.contentView的frame

 编辑下cell Frame:<UITableViewCellContentView: 0x7fb522c02070; frame = (0 0; 320 44);

我靠,contentView的frame也是一致的,奇怪,那tableView,编辑下,怎么实现cell的整体位移呢?

既然,cell的本身的frame和cell内部contentView的frame,在编辑和非编辑模式下,frame在打印出来,结果并没有改变,而实际上,在编辑模式下,cell明显发生位移了,接下来,我们看一下cell的的视图层级关系:

  •  cell 的视图层级关系

进入xcode,视图层级关系,如下:

层级关系

通过视图层级,我们可以看出,在编辑模式下,每一个cell都动态添加了
UITableViewCellEditControl的控件,而这个editControl的并没有add到contentView中,而是add在cell本身中,因此,如果自定义cell,布局相对cell本身,编辑模式,cell内部子控件就不会整体移动,必须相对于contentView;

其实,cell本身就是一个View,其内部又内置了一个容器view---ContentView,而ContView的文档说明如下:

  If you want to customize cells by simply adding additional views, you should add them to the content view so they will be positioned appropriately as the cell transitions into and out of editing mode. 就是说,如果想自定义cell,创建的子控件,如lable,imgView等,建议最好把子控件添加到ContentView中,这时候,在cell动画的时候,子控件也会随着ContView一块动画;

  •  2.2 自定义cell中,子控件不放在cell.contentView中,行不行?

问号

行啊,UI显示是没问题,但是,如果cell涉及动画,或者编辑模式下,多选,整体cell往右移动,这时候,如果子控件没有添加到contentView中,就会出现文章一开始的问题

  • 三 建议及后续

这篇文章,本来想通过cell的frame来探究编辑模式下,系统如何实现cell的整体移动,但是,frame居然是一致的,但是,通过层级关系,我们也知道,contentView在编辑状态下,frame改变了;

  • 建议

因此,建议:我们在日常开发中,尤其是团队开发中,在自定义tableVIewcell的时候,布局相对contentView布局,不要像如下这样布局:以下代码就是罪魁祸首!

[self.videoCoverImgView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(weakSelf).offset(15*X_WidthScale);
make.top.equalTo(weakSelf).offset(15*Y_HeightScale);
make.width.offset(130*X_WidthScale);
make.height.offset(74*Y_HeightScale);
}];

[点击获取文章涉及代码]:https://github.com/lucyios/UITableViewCell.git

  • 后续

cell的重用机理及尝试模仿写一个cell,打算从新开始一个文章论述,如果有问题的话,欢迎评论区留言,我们一起讨论!

如果您喜欢我的这篇文章,欢迎您给我点个赞,谢谢!

好好做好分内之事,闲来之余,随便逛逛!
原文地址:https://www.cnblogs.com/20150728-IOS/p/7978548.html