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

前言

在移动端日常开发中,列表UITableView的使用频率非常高,而TableView里主要用的就是UITableViewCell,不管是自定义cell,还是系统的cell,还是后期为了优化列表,都离不开操作cell,那么UITableViewCell就有必要研究一下了; 至于为什么要整理一篇关于Cell的文章,因为,搜了一下关于cell的文章,基本都是把头文件里面的方法简单介绍一下,没有介绍编辑模式下,cell的一些问题;
说明规定: 一些简单语法,便于说明问题:
  • A-->B 意思是: A继承B,B是A的父类;
另外,不要觉得自己平时常用,然后想当然以为,自己对于这个"出镜率"极高的控件极为熟悉,曾经我也那么以为,然而,毕竟我们:
深挖UITableViewCell-编辑多选模式下|深挖UITableViewCell-编辑多选模式下,引发的深思
文章图片
太年轻 问题引导
遇到如下问题:猜猜看,可能是什么原因造成的?我在平时比较活跃的QQ交流群问了一下,然后石沉大海了......
cell编辑多选bug 这只是其中在cell编辑多选中遇到一个问题;
还有一个就是,当用xib布局cell的时候,tableView进入编辑模式,多选不能全选cell,都是一些奇葩的问题;
这篇文章主要说明什么问题
  • Cell内部结构;
  • 回答文章一开始的问题;
  • Cell的重用机制;
  • 能不能自己仿照系统Cell,实现一个继承自UIView并且重用的cell;
一.cell内部结构
我们知道,cell可以根据不同的style显示不同的类型;
比如下面的几种:
  • 右边显示感叹号或者对勾;
  • 编辑模式,左边显示勾选框;
Cell内部有什么
  • 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内部的控件结构图如下:

深挖UITableViewCell-编辑多选模式下|深挖UITableViewCell-编辑多选模式下,引发的深思
文章图片
cell内部的控件结构 Runtime获取一个类的中所有的私有属性:
备注:需要导入Runtime相关的头文件
#import #import
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

二 文章一开始的问题
2.1 为什么会有这个问题? 项目中,基本上都是多人协作开发,有些UI是复用的,团队中,又没有codeReview,每个人对自己要求又不一样,在日常开发中,由于我们偷懒,能少写一点就少写一点,而文章出现的自定义cell正好是copy过来的,也没检查,当在非编辑模式下,显示正常; 当在编辑模式下,就出问题了?
  • 解决办法
    首先,在编辑模式下,正常的话,cell整体发生位移了,那么,只要涉及到控件位移的,第一就是打印frame,我们看下,编辑时和非编辑情况下,cell的frame变化情况:
    • tableView非编辑下,cell的frame:
      非编辑cell Frame:
    • tableView编辑下,cell的frame
    编辑下cell Frame:
    我们惊奇的发现,两种模式下,cell的frame居然是一致的,那也就是说:
    编辑状态下,cell的整体位移不是通过修改cell本身的frame实现的;
接下来,cell内部还有一个contentView,我们打印它的frame:
  • tableView非编辑下,cell.contentView的frame:
    非编辑cell Frame:
    • tableView编辑下,cell.contentView的frame
    编辑下cell Frame:
我靠,contentView的frame也是一致的,奇怪,那tableView,编辑下,怎么实现cell的整体位移呢?
既然,cell的本身的frame和cell内部contentView的frame,在编辑和非编辑模式下,frame在打印出来,结果并没有改变,而实际上,在编辑模式下,cell明显发生位移了,接下来,我们看一下cell的的视图层级关系:
  • cell 的视图层级关系
    进入xcode,视图层级关系,如下:
深挖UITableViewCell-编辑多选模式下|深挖UITableViewCell-编辑多选模式下,引发的深思
文章图片
cell视图层级关系 通过视图层级,我们可以看出,在编辑模式下,每一个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中,行不行? 深挖UITableViewCell-编辑多选模式下|深挖UITableViewCell-编辑多选模式下,引发的深思
文章图片
image
行啊,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); }];

【深挖UITableViewCell-编辑多选模式下|深挖UITableViewCell-编辑多选模式下,引发的深思】点击获取文章涉及代码
后续 cell的重用机理及尝试模仿写一个cell,打算从新开始一个文章论述,如果有问题的话,欢迎评论区留言,我们一起讨论!
如果您喜欢我的这篇文章,欢迎您给我点个赞,谢谢!

    推荐阅读