tableView入门到性能优化

标签(空格分隔): UITableView UITableViewCell UITableView性能优化 UITableView性能优化精讲


参考文章

摘要:UITableView是iOS开发中非常重要的控件之一,它能够展示多行数据,支持滚动.在大部分APP中都占有很大的比重.那么有关UITableView的性能优化也显得尤为重要,本文后面也将着重讲这个。

##UITableView的简单使用 ###1、UITableView的创建

代码方式

//tableView的创建必须制定一个样式,样式在初始化之后不能修改
//tableView分两种风格,Plain:不分区,grouped:分区
_tableView = [[UITableView alloc]initWithFrame:self.view.bounds style:UITableViewStylePlain];
    _tableView.delegate = self;
    _tableView.dataSource = self;
//告诉tableView,它上面的cell是根据UItableViewCell创建的,如果重用的cell找不到,系统会根据这个类来创建一个cell,不需要编程人员创建
//解释2://给tableView的某个重用标示符注册一个类,当tableView找这个重用标示符的cell,如果找不到,就会自动创建这个类的对象,(始终能找到可重用的cell)
    [_tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"cell"];
    [self.view addSubview:_tableView];
复制代码

xib方式 在设计界面拖入,并在属性检查器中设置各种属性

###2、UITableView的属性

// 设置每一行cell的高度
    self.tableView.rowHeight = 80;
// 设置每一组的头部标题高度
    self.tableView.sectionHeaderHeight = 50;
// 设置每一组的尾部标题高度
    self.tableView.sectionFooterHeight = 50;
// 设置分割线的颜色
    self.tableView.separatorColor = [UIColor redColor];
// 设置分割线的样式
    self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
// 设置表头控件---这里主要应用是打广告
    self.tableView.tableHeaderView = [[UISwitch alloc] init];
// 设置表尾控件---这里主要应用是加载数据
    self.tableView.tableFooterView = [[UISwitch alloc] init];
// 设置索引条的文字颜色
    self.tableView.sectionIndexColor = [UIColor orangeColor];
// 设置索引条的背景颜色
    self.tableView.sectionIndexBackgroundColor = [UIColor yellowColor];
复制代码

###3、隐藏TableView分割线的方法

// 方法一:设置分割线的颜色
    self.tableView.separatorColor = [UIColor clearColor];
// 方法二:设置分割线的样式
    self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
复制代码

###4、UITableViewCell的属性

//cell中默认包含的空间
cell.imageView
cell.textLabel
cell.detaliTextLabel
cell.contentView
// 设置右边显示的指示控件
// accessoryView的优先级 > accessoryType
cell.accessoryView = [[UISwitch alloc] init];

// 设置右边显示的指示样式
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;

// 设置cell选中的样式
// 设置cell的选中样式无
cell.selectionStyle = UITableViewCellSelectionStyleNone;
// 下面三种方式在iOS7之后,表现形式一样了,都是灰色
cell.selectionStyle = UITableViewCellSelectionStyleBlue;
cell.selectionStyle = UITableViewCellSelectionStyleDefault;
cell.selectionStyle = UITableViewCellSelectionStyleGray;
复制代码

###5、设置cell的背景颜色

// 总体效果为大背景蓝色,字体背景为红色,下面代码可以调换顺序,效果一样
cell.backgroundColor = [UIColor redColor];

UIView *bg = [[UIView alloc] init];
bg.backgroundColor = [UIColor blueColor];
cell.backgroundView = bg;
复制代码

###6、设置cell选中的背景View

// 设置cell选中的背景view
    UIView *seletedBg = [[UIView alloc] init];
    seletedBg.backgroundColor = [UIColor yellowColor];
    cell.selectedBackgroundView = seletedBg;
复制代码

###7、两个协议

tablewView代理方法的执行顺序。 UITableView返回多少组----->每组返回多少行cell--->计算每个cell的高度---->指定cell布局

UITableViewDelegate

/**
 *  1.当用户点击(选中)某一行cell就会调用这个方法
 */
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSLog(@"选中了:%zd",indexPath.row);
}

/**
 *  2.当用户取消点击(选中)某一行cell就会调用这个方法
 */
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSLog(@"取消选中了:%zd",indexPath.row);
}

/**
 *  3.返回每一组的头部高度
 */
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{

}

/**
 *  4.返回每一组的尾部高度
 */
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section
{

}

/**
 *  5.返回每一行cell的高度
 */
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (indexPath.row % 2 == 0) {
        return 50;
    } else {
        return 100;
    }
}

/**
 *  6.返回每一组的头部控件
 */
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
    return [UIButton buttonWithType:UIButtonTypeContactAdd];
}

/**
 *  7.返回每一组的尾部控件
 */

- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section
{
    return [UIButton buttonWithType:UIButtonTypeContactAdd];
}
/*
* 8.设置每一行的编辑样式:当表处于编辑状态时,默认的编辑样式为删除
*/
-(UITableViewCellEditingStyle)tableView:(UITableView *)tableView 		editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
		{
			 //编辑状态为删除
   		 return UITableViewCellEditingStyleDelete;
   		 //编辑状态为插入
    		 //return UITableViewCellEditingStyleInsert;
		}
		
//通过edditing的值来显示编辑状态
_tableView.editing = !_tableView.editing;


/*
*  9.表提交编辑状态的时候会调用这个方法
*/
-(void)tableView:(UITableView *)tableView commitEditingStyle:	(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath 	*)indexPath
	{
	    [_dataArray removeObjectAtIndex:indexPath.row];
	    [_tableView reloadData];
	}
/*
*  10.设置编辑中删除的文字
*/
-(NSString *)tableView:(UITableView *)tableView 	titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath 	*)indexPath
{
	return @"走你";
}
/*
*  11.右侧按钮被点击时会调用该方法
*/
	-(void)tableView:(UITableView *)tableView 	accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath
	{
  		 NSLog(@"调用了cell右侧按钮的方法");
	}



复制代码

###8、两个协议二:UITableViewDataSources



/**
 *  告诉tableView一共有多少组数据
 */
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView


/**
 *  告诉tableView第section组有多少行
 */
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section

/**
 *  告诉tableView每一行显示的内容(tableView每一行的内容是是第一个UITableViewCell)
 */
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

/**
 *  告诉tableView每一组的头部标题
 */
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section

/**
 *  告诉tableView每一组的尾部标题
 */
- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section

/**
 *  返回索引条的文字
 */
- (NSArray<NSString *> *)sectionIndexTitlesForTableView:(UITableView *)tableView
{
//    NSMutableArray *titles = [NSMutableArray array];
//    for (XMGCarGroup *group in self.carGoups) {
//        [titles addObject:group.title];
//    }
//    return titles;

// 抽取self.carGoups这个数组中每一个元素(XMGCarGroup对象)的title属性的值,放在一个新的数组中返回
    return [self.carGoups valueForKeyPath:@"title"];
复制代码

##二、UITableViewCell 在UITableView中,每一个单元格被称为cell,如果想在UITableView中显示数据,需要设置UITableView中cell的数量及每个cell显示的内容.UITableView并不能直接显示数据,它需要设置数据源(datasource),数据源遵守协议,并实现其中对应的方法设置数据及内容即可. ###UITableViewCell的创建

  1. 设置Cell的三个属性

    cell.imageView     //cell左边的图片
    cell.textLabel		  
    cell.detailTextLabel
    复制代码
  2. 通过自定义类 创建一个累继承于UITableViewCell,添加如下属性

    @property (nonatomic,retain)UIImageView *headerImageView;
    @property (nonatomic,retain)UILabel *nameLabel;
    @property (nonatomic,retain)UILabel *timeLabel;
    @property (nonatomic,retain)UILabel *messageLabel;
    @property (nonatomic,retain)UIImageView *picImageView;
    复制代码

    .m文件中的实现

    //如果是alloc创建的cell,各个自定义属性空间的初始化代码写在init方法中
    (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString 	*)reuseIdentifier{
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
            //头像
        _headerImageView = [[UIImageView alloc] initWithFrame:CGRectMake(8, 8, 60, 60)];
        _headerImageView.layer.cornerRadius = 30;
        _headerImageView.layer.masksToBounds = YES;
        [self.contentView addSubview:_headerImageView];       
        //名字
        _nameLabel = [[UILabel alloc] initWithFrame:CGRectMake(76, 8, 240, 20)];
        _nameLabel.font = [UIFont boldSystemFontOfSize:16];
        _nameLabel.textColor = [UIColor redColor];
        [self.contentView addSubview:_nameLabel];   
        //发布时间
        _timeLabel = [[UILabel alloc] initWithFrame:CGRectMake(76, 36, 240, 20)];
        _timeLabel.font = [UIFont systemFontOfSize:12];
        [self.contentView addSubview:_timeLabel];        
        //动态正文
        _messageLabel = [[UILabel alloc] initWithFrame:CGRectMake(8, 76, 304, 60)];
        _messageLabel.font = [UIFont systemFontOfSize:16];       
        //numberOfLines行数,设置为0不限制行数。
        _messageLabel.numberOfLines = 0;        
        [self.contentView addSubview:_messageLabel];        
        //动态配图
        _picImageView = [[UIImageView alloc] initWithFrame:CGRectMake(8, 144, 100,100)];
        [self.contentView addSubview:_picImageView];
        }
        return self;
        }	
    复制代码
  3. 通过xib自定义 首先创建一个带xib的UITableViewCell,拖控件设置cell高度。拉线生成属性;

     自定义类中
     //如果cell是从xib中加载的,那么不走init方法,所以初始化代码不能写在init方法中
		//当自身从xib中加载的时候调用,所以自定义xib的代码应该在这里实现
		(void)awakeFromNib {
    		[super awakeFromNib];
   	 // Initialization code
    		_headerImageView.layer.cornerRadius = 30;
   	 _headerImageView.layer.masksToBounds = YES;
		}
复制代码

注册重用

```
//给TableView的某个重用标示符注册一个xib,当tableView找这个标识符时,如果找不		    到,就会自动从注册的西边中区加载一个cell。
[_tableView registerNib:[UINib nibWithNibName:@"CustomCell" 		bundle:nil]     forCellReuseIdentifier:@"cell"];
```
复制代码

##三、性能优化

1)尽量使用cell的复用。

使用cell的复用,可以减少内存的开销,没有开辟新的空间,也减少了一些计算量。

复用原理:当滚动列表时(UITableView)部分cell会移除Window 但是移除的cell并没有被立即释放 而是放到了一个叫做复用池的对象池中,处于待定状态,当有新的cell要出现在Window界面上时,首先会从复用池中寻找是否有相同类型的cell,如果有直接拿过用(最直观的表现是新出现的cell有没有开辟新的内存空间),如果没有,创建一个新的类型的cell,所以UITableView可能拥有多种类型的cell,复用池也可能存储着多种类型的cell,系统通过定义reuseIndentifer作为每个cell的唯一标示符来确定即将出现的cell复用何种类型的cell。

2)对于不定高的cell 提前将每个cell的高度存入数组,出现一个cell的时候,直接从数组中拿出确切的高度即可,不用临时计算cell的高度。对于固定高的cell和不定高的cell同样适用。同样也可以在存储在model中,在获取数据后要赋值给model时进行计算。

3)涉及网络请求加载数据在UITableView滑动结束的时候在进行加载数据(渲染)避免卡顿。

1、UITableView继承自UIScrollView,继承了后者的方法。
//滑动结束的方法
- (void)scrollViewDidEndDragging:(UIScrollView*)scrollView willDecelerate:(BOOL)decelerate
//减速结束之后的方法
- (void)scrollViewDidEndDecelerating:(UIScrollView*)scrollView

2、在tableView必须实现的二个方法中(加载cell的方法中)将数据的渲染写在以下if语句中
if(self.tableView.dragging==NO&&self.tableView.decelerating==NO)
复制代码

4)对于tableView的自定义控件 尤其是UIImageView,尽量减少使用圆角,阴影等layer属性,尽量减少使用alpha(透明度)来设置透明度,(在项目开发中,让UI设计师设计原图就是带圆角的图) 阴影,圆角这些layer效果都是在GPU中完成的。

1、当多个视图重叠时,一个像素同时属于很多subviews,GPU会进行合成渲染,需要渲染一个像素两次或多次,而渲染的最慢的操作就是混合。因此当视图结构太过复杂,就会大量消耗GPU的资源。所以当一个空间本身是不透明,注意设定alpha为1,这样可以避免无用的alpha通道合成,降低GPU的负载。 另外在layer层渲染图层时会涉及上下文切换以及离屏渲染之类的,系统开销会很大,特别是在cell视图很复杂的时候,由于渲染问题导致的内存开销会让你的tableview非常卡顿。比如cell中需要设置头像圆形直接设置圆角会很卡,那么我们可以用Quartz2d把拿到的图片处理一遍在给cell使用。

2、对控件设置cornerRadius后对其进行clip或mask操作时 会导致offscreenrendering这个也是在GPU中进行的 如果在滑动时 圆角对象太多 回到GPU的负载大增幅。

这时我们可以设置layer的shouldRasterize属性为YES,可以将负载转移给CPU,更彻底的是直接使用带圆角的原图。

5)尽量使用懒加载

懒加载又称为延迟加载,实际上是重写某个对象的getter方法 原理:程序一开始并不对它进行初始化 而是在用到他的时候 才为他开辟内存供它使用。

好处:

1、不必将创建的对象的代码全部写在ViewDidLoad中,代码可读性强。 2、每个控件的getter方法,分别负责各自的实例化处理,代码彼此之间独立性强 松耦合。

6)减少返回给的cell里面的处理逻辑和处理时间。

以驴妈妈为例:各个UI控件整合到一起,实际上只有一个控件。

7)设置每个cell的opaque属性 ----面试亮点

opaque意思是不透明的 浑浊的 有YES和NO二个结果,如果控件本身不透明,我们设置opaque为YES。

opaque为YES表示告诉iOS当前视图背后没有需要绘制的内容,同时允许iOS的绘图方法通过一些优化来加速当前视图的绘制。

为什么我们设置Alpha的值为1的时候仍然要设置opaque的属性为YES? Alpha属性只对当前需要绘制的视图起作用,如果当前视图并没有填满父视图或者当前视图上存在一些洞(由Alpha通道所致),那么图像视图下方的内容将仍然可见,不管Alpha的值是多少。选中就是让iOS明白不需要为图像视图之后的东西浪费绘制时间。

以下是官方描述

default is YES. opaque views must fill their entire bounds or the results are undefined. the active CGContext in drawRect: will not have been cleared and may have non-zeroed pixels

8)分段加载数据

设置分页加载数据,也就是上拉刷新和下拉加载。

以下是cell简单复用代码

#import "ViewController.h"
#import "XMGTestCell.h"

@interface ViewController ()

@end

@implementation ViewController

NSString *ID = @"wine";
- (void)viewDidLoad {
    [super viewDidLoad];

    self.tableView.rowHeight = 100;

    // 注册 ID这个标识 对应的 cell类型 为UITableViewCell这种类型
    [self.tableView registerClass:[XMGTestCell class] forCellReuseIdentifier:ID];
}

#pragma mark - 数据源方法

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return 200;
}

/**
 *  每当一个cell进入视野范围内就会调用1次
 */
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // 1.去缓存池取可循环利用的cell
    XMGTestCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];

    // 2.设置数据
    cell.textLabel.text = [NSString stringWithFormat:@"第%zd行数据",indexPath.row];
    return cell;

}
复制代码
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值