神刀安全网

iOS UI布局详解—「UITableView表格视图」


引导


“ 本文不适合老司机… ”

本文章将介绍 iOS UI布局 详解,最常使用的三大控件之一 UITableView,将会分成两篇文章完整的讲述UITableView的常用属性方法使用(包括优化方面)及注意点 和 实战开发使用场景案例,文章编写周期会长一些,用到那点实用的东西,会及时补充。

本篇文章主要从【UITableView 属性方法注解】学习总结。
在「时间 & 知识 」有限内,总结的文章难免有「未全、不足 」的地方,还望各位好友指出,以提高文章质量。

目录:

  1. 概念
    1.UITableView的概念
    2.UITableViewCell的概念
    3.UITableViewController的概念
  2. UITableView的使用步骤
    1.大致个人总结六步
  3. UITableView的常用属性
  4. UITableViewCell的常用属性
  5. UITableView的代理方法
  6. UITableViewController的使用
  7. UITableViewCell的重用
  8. UITableViewCell的优化
  9. 期待后续 & About me

概念


UITableView的概念
iOS UI布局详解—「UITableView表格视图」

  • 简化可释义为 "表格视图" 或是 "列表视图", 且 支持选择和编辑的信息。在iOS开发中做列表数据类型的应用,最常用的做法就是使用UITableView控件。

  • UITbaleView 继承自 UIScrollView,因此支持竖直滚动,而且在 UIScrollView 做了性能优化,当然你也可以做到水平滚动,把cell 旋转一下就可以了。

UITableViewCell的概念
iOS UI布局详解—「UITableView表格视图」

  • 简化意思就是 UITableView的每一行都是一个UITableViewCell。其中类包含属性和方法用于设置和管理单元格;

  • UITableView内部有个默认的子控件:contentView,填充整个UITableViewCell的父控件。

  • UITableView的子控件实质都在contentView上。

UITableViewController的概念
iOS UI布局详解—「UITableView表格视图」

  • 简化意思就是 这个类是一个自带(管理)tableView 的控制器。

  • Xcodecmd UITableViewController 点进去,看到

    NS_CLASS_AVAILABLE_IOS(2_0) @interface UITableViewController : UIViewController

    简化意思就是UITableViewController是已经遵循了UITableViewDelegateUITableViewDataSource代理的控制器

UITableView使用步骤


下面示例以 tableView代码自定义不等高Cell 为例:

  • 第一步:创建 UITableView,采用懒加载方式。
- (UITableView *)tableView {     if (_tableView == nil) {         _tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0,kScreenWidth, kScreenHeight) style:UITableViewStylePlain];// Plain单组数据悬停 Grouped多组数据不悬停          _tableView.dataSource = self;// 数据源         _tableView.delegate = self;// 代理         _tableView.separatorStyle = UITableViewCellSeparatorStyleNone;// 分割线         _tableView.estimatedRowHeight = 100;// 预设高度         //[self.view addSubview:self.tableView];// 这里不要忘记添加上          //--------------------------- tableView 常用属性 ------------------------------//         //      }     return _tableView; }
  • 第二步:设置数据源和代理,遵守对应协议。

    @interface LNTableViewAttributeVC ()
  • 第三步:设置数据数组,采用懒加载方式加载数据(使用MJExtension字典数组转模型数组)。

- (NSMutableArray *)dataArray{     if (!_dataArray) {          _dataArray = [LNStatus mj_objectArrayWithFilename:@"statuses.plist"];     }     return _dataArray; }
  • 第四步:创建自定义Cell(视图继承UITableViewCell)。
    • 1.定义模型类对象。
    • 2.定义子控件属性。
    • 3.在initWithStyle:style reuseIdentifier:reuseIdentifier,添加子控件(设置约束Masonry)。
    • 4.layoutSubviews 设置子控件的 frame.
    • 5.模型类对象set方法setStatus:设置子控件的数据(赋值)。
//  LNStatusViewCell.h @class LNStatus; @interface LNStatusViewCell : UITableViewCell  /** 模型类对象 */ @property (nonatomic, strong) LNStatus *status; @end //--------------------------- <#我是分割线#> ------------------------------//   //  LNStatusViewCell.m @interface LNStatusViewCell () /* 头像 */ @property (nonatomic, weak) UIImageView *iconImageView; /* 配图 */ @property (nonatomic, weak) UIImageView *pictureImageView; /* vip */ @property (nonatomic, weak) UIImageView *vipImageView; /* 名称 */ @property (nonatomic, weak) UILabel *nameLabel; /** 文字 */ @property (nonatomic, weak) UILabel *text_Label;// 自定义控件不要与系统textLabel重名 @end  @implementation LNStatusViewCell  /*1.  添加子控件(设置约束)(如:文字和颜色,一次性的设置)  注意点:把所有有可能显示的子控件都先添加进去  */ - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {     if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {         // 图像         // 配图         // vip         // 昵称         // 正文          // 自定义分割线         UILabel *lineLabel = [[UILabel alloc] initWithFrame:CGRectZero];         lineLabel.backgroundColor = [UIColor colorWithWhite:0.5 alpha:0.5];         [self.contentView addSubview:lineLabel];         [lineLabel mas_makeConstraints:^(MASConstraintMaker *make) {             make.bottom.left.right.equalTo(self.contentView).with.offset(0);             make.height.mas_equalTo(1.0f);         }];     }     return self; }   // 2.设置子控件的frame /*  - (void)layoutSubviews{     [super layoutSubviews];  }  */  // 3.set方法设置子控件的数据(赋值) - (void)setStatus:(LNStatus *)status{     _status = status;      self.iconImageView.image = [UIImage imageNamed:status.icon];     self.nameLabel.text = status.name;     self.text_Label.text = status.text;      if (status.isVip) {// 有无皇冠         self.vipImageView.hidden = NO;         self.nameLabel.textColor = [UIColor redColor];     } else {         self.vipImageView.hidden = YES;         self.nameLabel.textColor = [UIColor blackColor];     }      if (status.picture) {// 有无配图(有配图再赋值,无配图就不赋值)         self.pictureImageView.hidden = NO;         self.pictureImageView.image = [UIImage imageNamed:status.picture];     } else {         self.pictureImageView.hidden = YES;     }      /** 这里的下文会介绍到 */     self.iconImageView.frame = self.status.iconFrame;     self.nameLabel.frame = self.status.nameFrame;     self.vipImageView.frame = self.status.vipFrame;     self.text_Label.frame = self.status.textFrame;     self.pictureImageView.frame = self.status.pictureFrame; }
  • 第五步:创建自定义模型类(模型继承NSObject)。
    • 1.定义模型属性。
    • 2.提供构造方法(对象方法和类方法)。
    • 3.定义对应模型frame属性(注解:Cell的动态行高,这里解决方案: 在heightForRowAtIndexPath:这个方法返回之前就要计算cell的高度,即把cell子控件的frame封装到模型中,cellHeight返回行高(相当于懒加载))。
@interface LNStatus : NSObject // 1.定义模型属性 /* 文字 */ @property (nonatomic, copy) NSString *text; /* 头像 */ @property (nonatomic, copy) NSString *icon; /* 名称 */ @property (nonatomic, copy) NSString *name; /** VIP 到时候访问的时候可以.vip ,也可以.isVip */ @property (nonatomic, assign, getter=isVip) BOOL vip; /* 配图 */ @property (nonatomic, copy) NSString *picture;   /** 用MJExtension字典转模型框架,下面可以不写 */ // 2.提供构造方法 //+ (instancetype)statusWithDict:(NSDictionary *)dict;  //--------------------------- 返回cell的高度封装 ------------------------------// // /** 图像的frame */ @property (nonatomic, assign) CGRect iconFrame; /** 昵称的frame */ @property (nonatomic, assign) CGRect nameFrame; /** vip的frame */ @property (nonatomic, assign) CGRect vipFrame; /** 正文frame */ @property (nonatomic, assign) CGRect textFrame; /** 配图的frame */ @property (nonatomic, assign) CGRect pictureFrame; /** 返回cell的高度 */ @property (nonatomic, assign) CGFloat cellHeight;   //  LNStatus.m //+ (instancetype)statusWithDict:(NSDictionary *)dict { //    // 字典赋值模型属性 //}  // get方法返回行高(相当于懒加载) - (CGFloat)cellHeight{     if (_cellHeight == 0) {         /** 图像 */         self.iconFrame = CGRectMake(iconX, iconY, iconWH, iconWH);          /** 昵称:字符串自适应宽(昵称文字没有换行) */         self.nameFrame = CGRectMake(nameX, nameY, nameSize.width, nameSize.height);          /** vip */         if (self.isVip) {             self.vipFrame = CGRectMake(vipX, vipY, vipW, vipH);         }          /** 正文:字符串自适应宽高(文字有换行) */         self.textFrame = CGRectMake(textX, textY, textW, textH);          /** 配图 */         if (self.picture) { // 有配图             self.pictureFrame = CGRectMake(pictureX, pictureY, pictureWH, pictureWH);             _cellHeight = CGRectGetMaxY(self.pictureFrame) + space;         } else {             _cellHeight = CGRectGetMaxY(self.textFrame) + space;         }       }     return _cellHeight; }
  • 第六步:实现DataSource数据源(必须)Delegate代理(可选)协议方法创建自定义模型类(模型继承NSObject)。
#pragma mark ------------------ #pragma mark - UITableViewDataSource // TableView中 有多少组Sections // 说明:单组数据可不实现方法,默认返回1 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {     return 1; }  // 每组Sections 有多少行Rows - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{     return self.dataArray.count; }  // 每行 cell内容 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{     // 1.cell注册(只要用到forIndexPath:就必须要注册)     //[self.tableView registerClass:[LNStatusViewCell class] forCellReuseIdentifier:cellID];      // 2.cell复用队列(访问缓存池)     LNStatusViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID forIndexPath:indexPath];     // 3.设置数据(数据数组赋值模型类)     cell.status = self.dataArray[indexPath.row];     return cell; }

#pragma mark ------------------ #pragma mark - UITableViewDelegate // 解决方案:在这个方法返回之前就要计算cell的高度,即返回cell的高度封装在模型中 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {     LNStatus *status = self.dataArray[indexPath.row];      return status.cellHeight; }

UITableView基本使用效果图


iOS UI布局详解—「UITableView表格视图」

UITableView基础.gif

UITableView常用属性


  • 全局设置行row高:
属性: @property (nonatomic) CGFloat rowHeight;  使用格式: self.tableView.rowHeight = 70;  说明: 默认是44
  • 全局设置区头(区尾)高:
属性: @property (nonatomic) CGFloat sectionHeaderHeight;  @property (nonatomic) CGFloat sectionFooterHeight;  使用格式: self.tableView.sectionHeaderHeight = 50; self.tableView.sectionFooterHeight = 50;
  • 设置分割线的样式、铺满和颜色:
属性: @property (nonatomic) UITableViewCellSeparatorStyle separatorStyle __TVOS_PROHIBITED; @property (nonatomic, strong, nullable) UIColor *separatorColor UI_APPEARANCE_SELECTOR __TVOS_PROHIBITED; @property (nonatomic) UIEdgeInsets separatorInset NS_AVAILABLE_IOS(7_0) UI_APPEARANCE_SELECTOR;  使用格式: self.tableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine; self.tableView.separatorColor = [UIColor redColor]; self.tableView.separatorInset = UIEdgeInsetsMake(0, 0, 0, 0);  说明: 设置 StyleNone 代表隐藏分割线,[UIColor clearColor]为透明色对象 枚举UITableViewCellSeparatorStyle常用的枚举元素: UITableViewCellSeparatorStyleNone // 隐藏分割线 UITableViewCellSeparatorStyleSingleLine // 默认样式 UITableViewCellSeparatorStyleSingleLineEtched // 仅支持在grouped样式,但是和默认样式没什么区别
  • 设置tableView表格头(尾)视图:
属性: @property (nonatomic, strong, nullable) UIView *tableHeaderView;                            @property (nonatomic, strong, nullable) UIView *tableFooterView;   使用格式: self.tableView.tableHeaderView = [[UISwitch alloc] init]; self.tableView.tableFooterView = [[UISwitch alloc] init];
  • 设置table背景视图和颜色:
属性: @property (nonatomic, strong, nullable) UIView *backgroundView NS_AVAILABLE_IOS(3_2); @property (nonatomic, copy, nullable) UIColor *backgroundColor;  使用格式: self.tableView.backgroundView = [[UIView alloc] init]; self.tableView.backgroundColor = [UIColor grayColor];
  • 设置TableView的cell的预设高度(性能优化):
属性: @property (nonatomic) CGFloat estimatedRowHeight NS_AVAILABLE_IOS(7_0);  使用格式: self.tableView.estimatedRowHeight = 100;  说明: 这里的高度也不是越大越好,要适时而定
  • 隐藏多余分割线:
使用格式: self.tableView.tableFooterView = [[UIView alloc] init];
  • 设置右侧索引文字和背景颜色:
属性: @property (nonatomic, strong, nullable) UIColor *sectionIndexColor NS_AVAILABLE_IOS(6_0) UI_APPEARANCE_SELECTOR; @property (nonatomic, strong, nullable) UIColor *sectionIndexBackgroundColor NS_AVAILABLE_IOS(7_0) UI_APPEARANCE_SELECTOR;   使用格式: self.tableView.sectionIndexColor = [UIColor grayColor]; self.tableView.sectionIndexBackgroundColor = [UIColor yellowColor];

UITableViewCell的常用属性


  • 设置cell附加样式(比如右侧的箭头):
属性: @property (nonatomic) UITableViewCellAccessoryType    accessoryType;    使用格式: cell.accessoryType = UITableViewCellAccessoryCheckmark;
  • 设置cell右边的指示控件(比如右侧的开关):
属性: @property (nonatomic, strong) UIView *accessoryView;  使用格式: cell.accessoryView = [[UISwitch alloc] init];
  • 设置cell选中样式(StyleNone选中不变灰色):
属性: @property (nonatomic) UITableViewCellSelectionStyle   selectionStyle;  使用格式: cell.selectionStyle = UITableViewCellSelectionStyleNone;
  • 设置cell的背景控件:
属性: @property (nonatomic, strong) UIView *backgroundView;  使用格式: UIView *bg = [[UIView alloc] init]; bg.backgroundColor = [UIColor redColor]; cell.backgroundView = bg;  说明: 背景控件 和 背景颜色 同时存在时,(优先级: backgroundView > backgroundColor)
  • 设置cell的背景颜色:
属性: @property(nullable, nonatomic,copy) UIColor *backgroundColor  使用格式: cell.backgroundColor = [UIColor blueColor];  说明: 还可以设置cell的子控件背景图片 cell.textLabel.backgroundColor = [UIColor greenColor];
  • 设置cell选中时背景View:
属性: @property (nonatomic, strong) UIView *selectedBackgroundView;  使用格式: UIView *seletedBg = [[UIView alloc] init]; seletedBg.backgroundColor = [UIColor purpleColor]; cell.selectedBackgroundView = seletedBg;

UITableView的代理方法


  • 返回每一行cell的高度:
方法 和 使用格式: - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{     // 获取indexPath //    if (indexPath.row%2) { //        return 100; //    }else     return 44; }
  • 当选中某一行cell的时候就会调用这个方法:
方法 和 使用格式: - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{     NSLog(@"选中第%ld区---第%ld行 ",indexPath.section,indexPath.row); }
  • 返回每一组的区头(区尾) 标题 和 控件:
方法 和 使用格式: // 区头(区尾)标题 - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{     return @"简书-白开水ln"; }  // 区头(区尾)View - (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {     return [[UISwitch alloc] init]; }  说明: 标题 和 View 同时存在时,View 会覆盖 Title
  • 返回每一组的头部(尾部)高度:
方法 和 使用格式: - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section{     // 获取indexPath     NSIndexPath *indexPath = [[NSIndexPath alloc]initWithIndex:section];     if (indexPath.section == 0) {         return 60;     }else         return 30; }
  • 返回右侧索引标题:
方法 和 使用格式: -(NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView{     //return [self.dataArray valueForKeyPath:@"title"];     return @[@"黄焖鸡小份",@"黄焖鸡中份",@"黄焖鸡大份",]; }
  • 返回某一行缩进:
方法 和 使用格式: - (NSInteger)tableView:(UITableView *)tableView indentationLevelForRowAtIndexPath:(NSIndexPath *)indexPath{     return 7; }

UITableViewController的使用


  • 将控制器设置为UITableView的方法和步骤

    • 第一步:创建新的类或修改原有的ViewController类,继承自UITableViewController

    • 第二步:在Main.storyboard中删除自带的UIViewController控制器,然后往里面拖一个UITableViewController控制器

      iOS UI布局详解—「UITableView表格视图」

    • 第三步:修改新拖进来的TableViewController控制器的自定义类名为第一步中继承自UITableViewController类的类名

      iOS UI布局详解—「UITableView表格视图」

    • 第四步:勾选TableViewController控制器为程序启动第一个加载的控制器

      iOS UI布局详解—「UITableView表格视图」

  • 注意点:

    • tableVieController有个tableView属性,指向一个tableView
    • tableViewdataSourcedelegate属性指向的就是这个控制器,并且这个控制器已经遵守了UITableViewDataSourceUITableViewDelegate
    • 每个控制器的内部都有一个view属性,在tableVieController中,viewtableView属性指向的是同一个对象(控制器的view就是tableView)。

UITableViewCell的重用


  • 原因:
    • iOS设备的内存有限,如果用 UITableView显示成千上万条数据,就需要成千上万个UITableViewCell对象的话,那将会耗尽iOS设备的内存。
    • 有可能导致显示数据错乱。
  • 原理:
    • 当滚动列表时,部分UITableViewCell会移出窗口,UITableView会将窗口外的UITableViewCell放入一个缓存池中,等待重用
    • UITableView要求dataSource返回UITableViewCell时,dataSource会先查看这个对象池,如果池中有未使用的UITableViewCelldataSource会用新的数据配置这个UITableViewCell,然后返回给UITableView,重新显示到窗口中,从而避免创建新的UITableViewCell对象
  • Cell重用的实现代码
  • 方法一:
    • 1.定义一个cell的重用标识
    • 2.根据这个ID去缓存池中看有没有可循环利用的cell
    • 3.如果缓存池中没有可循环利用的cell,自己创建
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {    // 1. 定义一个重用标识    static NSString *cellID = @"cellID";    // 2. 根据这个cellID去缓存池中看有没有可循环利用的cell    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cellID"];    // 3. 如果缓存池中没有可循环利用的cell, 自己创建    if (cell == nil) {        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cellID"];    }    return cell; }
  • 方法二:
    • 1.定义一个cell的重用标识。
    • 2.根据这个ID去缓存池中看有没有可循环利用的cell
    • 3.如果缓存池中没有会看有没有,根据ID这个标识注册对应的cell类型。
    • 4.如果有注册,会根据这个ID创建对应的类型的cell,并且会绑定这个ID标识,返回这个cell
// 1. 定义一个重用标识 static NSString *cellID = @"CellID"; static NSString *testID = @"testID"; - (void)viewDidLoad {    [super viewDidLoad];     // 3. 根据ID 这个标识 注册 对应的cell类型是UITableViewCell    [self.tableView registerClass:[LNTableViewCell class] forCellReuseIdentifier:cellID];    [self.tableView registerNib:[UINib nibWithNibName:NSStringFromClass([LNTableViewXibCell class]) bundle:nil] forCellReuseIdentifier: testID]; }  - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {      // 2. 根据这个ID去缓存池中看有没有可循环利用的cell      LNTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];     if (indexPath.row%2 == 0) {         LNTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: CellID];         return cell;     }else{         LNTableViewXibCell *cell = [tableView dequeueReusableCellWithIdentifier:testID];         return cell;     } }
  • 总结:
    • 不同类型的Cell共存(重要点是CellID不同)。

UITableViewCell的优化


  • UITableView 的优化主要从三个方面入手:

    • 1.提前计算并缓存好高度(布局),因为heightForRowAtIndexPath:是调用最频繁的方法;

    • 2.异步绘制,遇到复杂界面,遇到性能瓶颈时,可能就是突破口;

    • 3.滑动时按需加载,按照用户滚动的速度去选择加载哪个cell。这个在大量图片展示,网络加载的时候很管用!(SDWebImage已经实现异步加载,配合这条性能杠杠的)。

  • 除了上面最主要的三个方面外,还有很多几乎大伙都很熟知的优化点:

    • 1.正确使用reuseIdentifier来重用Cells
    • 2.尽量使所有的view opaque,包括Cell自身。
    • 3.尽量少用或不用透明图层。
    • 4.如果Cell内显示的内容来自web,使用异步加载,缓存请求结果。
    • 5.减少subviews的数量。
    • 6.在heightForRowAtIndexPath:中尽量不使用。cellForRowAtIndexPath:,如果你需要用到它,只用一次然后缓存结果。
    • 7.尽量少用addViewCell动态添加View,可以初始化时就添加,然后通过hide来控制是否显示。
  • 案例(按需加载):
    • 原理:在快速滑动松手后滚动的cell个数超过预定的个数,只显示最后出现的cell的前三个cell,把这三个cellindexPath存到数组中,在数据源方法里判断如果数组count>0,且数组不包含当前的indexPath,那就说明此cell是在快速滑动中需要隐藏的:
//按需加载 - 如果目标行与当前行相差超过指定行数,只在目标滚动范围的前后指定3行加载。 - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset{     NSIndexPath *ip = [_titleTableView indexPathForRowAtPoint:CGPointMake(0, targetContentOffset->y)];     NSIndexPath *cip = [[_titleTableView indexPathsForVisibleRows] firstObject];     NSInteger skipCount = 1;    // 这里我为了方便演示写的1,大家可以按需求自行设定     if (labs(cip.row-ip.row)>skipCount) { //        此方法可以获取将要显示的组 //        visibleSections = [NSSet setWithArray:[[_titleTableView indexPathsForVisibleRows] valueForKey:@"section"]];          NSArray *temp = [_titleTableView indexPathsForRowsInRect:CGRectMake(0, targetContentOffset->y, _titleTableView.frame.size.width, _titleTableView.frame.size.height)];         NSMutableArray *arr = [NSMutableArray arrayWithArray:temp];         if (velocity.y<0) {      // 上滑             NSIndexPath *indexPath = [temp lastObject];             if (indexPath.row+33) {                 [arr addObject:[NSIndexPath indexPathForRow:indexPath.row-3 inSection:indexPath.section]];                 [arr addObject:[NSIndexPath indexPathForRow:indexPath.row-2 inSection:indexPath.section]];                 [arr addObject:[NSIndexPath indexPathForRow:indexPath.row-1 inSection:indexPath.section]];             }         }         [needLoadArr addObjectsFromArray:arr];     } }

相应的,每次开始拖动的时候去清空数组。还有种情况,如果界面上有显示空白cell的时候突然手动停止滚动呢?

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {     [needLoadArr removeAllObjects];     // 清空数组     // 取到当前界面上能显示的indexPaths,判断是否有隐藏     NSArray *indexpaths = [_titleTableView indexPathsForVisibleRows];     UITableViewCell *firstCell  =   [_titleTableView cellForRowAtIndexPath:indexpaths.firstObject];     UITableViewCell *lastCell   =   [_titleTableView cellForRowAtIndexPath:indexpaths.lastObject];     //  在当前可见的区域中,第一个cell或者最后一个cell是隐藏状态,那么重新加载可见区域内的cell     if (firstCell.isHidden == true || lastCell.isHidden == true) {         [_titleTableView reloadRowsAtIndexPaths:indexpaths withRowAnimation:UITableViewRowAnimationNone];     } } 也可以把判断的代码写在scrollView停止滚动监听方法里,但是个人觉得没必要,因为这种情况必定是手动触碰去停止的,这里处理没问题

数据源方法:

   if (needLoadArr.count > 0) {         if (![needLoadArr containsObject:indexPath]) { //            NSLog(@"该cell是快速滑动中的cell,所以隐藏");             cell.hidden = true;             return cell;         }     }     cell.hidden = false;      // 正常显示的cell

附上:按需加载参考 VVeboTableViewDemo: 这位前辈在tableView优化上做到了极致。

  • 总结
    tableView性能优化的方式有很多,但不是所有的我们都需要。比如不是必需要显示的界面,预先计算行高就是浪费(用户流量)。应适时而定。站在用户体验的角度开发才是好的伐码猿。

温馨提示:更多有关本文系统文件的属性和方法及常用功能案例,请移步这里【UIKit框架】← ☕️ →【GitHub】

期待


iOS UI布局详解—「UITableView表格视图」

后续 & About me


【我也是对所花费时间的一个总结】

About me@「伐码猿」

我只是个【有思想的伐码猿🐒】加上【自己的学习总☕️】写出来的文章。

转载本站任何文章请注明:转载至神刀安全网,谢谢神刀安全网 » iOS UI布局详解—「UITableView表格视图」

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址