IOS之UI -- UITableView -- 1 -- 相关初识
时间:2015年11月23日 感谢MJ老师,感谢朱黄辰老师
内容大纲:
1、初识UITableView和UITableViewDataSource
2、使用UITableViewDataSource做小实例展示多组数据
3、常见属性
4、性能优化
5、性能优化的使用注意
6、Cell的重用原理
7、注册cell
8、索引条
9、关于UITableViewController
10、附录
1、初识UITableView和UITableViewDataSource
继承自UIScrollView
-
如何展示数据:
- 需要一个数据源(UITableViewDataSource),接受这个协议,然后实现必须要实现的方法
- UITableView会向数据源查询一共有多少行数据以及每一行显示什么数据等
- 没有设置数据源的UITableView只是一个空壳
-
但凡遵守UITableViewDataSource的OC对象,都可以是UITableView的数据源
简单实现协议里的方法实例:
-
UITableViewStylePlain:平板格式 UITableViewStyleGrouped:分组格式
其中需要积累的两个小知识点,至少要知道意思,不然用到这点知识不知道怎么用不行的:
1、NSIndexPath对象,有两个属性 indexPath.section == 1 组号 indexPath.row == 2 行号 2、(NSInteger对象的占位符是%zd,对象地址的占位符是%p)
向上移动header会保留在上方直到这个组完全不在视野内 ,向下移动fooder会保留在底部直到这个组完全不在视野内
托线添加数据源或代理:
如果需要添加UITableView组的头部尾部描述信息,可以command+点击协议里找需要重写的方法,同样,如果需要查看UITableViewDelegate的代理的方法,也可以在代理里的源码看方法,这也是一种常用的自学不管是苹果提供的还是第三方框架框架使用的学习方法。
如果在每一行显示的内容中,也就是上面实现协议的第三个方法中,如果涉及要设置cell的accessroyType的显示样式
accessory n. 附件, 配件
- cell.accessroyType = UITableViewCellAccessory......
- cell.accessroyView = [[UISwitch alloc] init];
如果在代码中使用以上两种,不论顺序如何,比如上面的,最后的顺序都是现实UI控件,因为UI控件设置优先级比较高。
2、使用UITableViewDataSource做小实例展示多组数据
通过重写协议里的方法来实现:
使用模型改进数据并已经进行了后面提到的cell循环利用的优化的源代码下载:
百度云 链接: http://pan.baidu.com/s/1hqubma4 密码: vkdz
3、常见属性
常见属性有个大致印象就好,先上图一览用到的方法,后面的代码过一遍即可
1 #import "ViewController.h" 2 #import "XMGWine.h" 3 4 @interface ViewController () <UITableViewDataSource, UITableViewDelegate> 5 @property (weak, nonatomic) IBOutlet UITableView *tableView; 6 /** 酒数据 */ 7 @property (nonatomic, strong) NSArray *wineArray; 8 @end 9 10 @implementation ViewController 11 12 - (NSArray *)wineArray 13 { 14 if (!_wineArray) { 15 // 加载字典数组 16 NSArray *dictArray = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"wine.plist" ofType:nil]]; 17 18 // 将字典数组 -> 模型数据 19 NSMutableArray *wineArray = [NSMutableArray array]; 20 for (NSDictionary *dict in dictArray) { 21 [wineArray addObject:[XMGWine wineWithDict:dict]]; 22 } 23 24 _wineArray = wineArray; 25 } 26 return _wineArray; 27 } 28 29 - (void)viewDidLoad { 30 [super viewDidLoad]; 31 32 self.tableView.delegate = self; 33 34 // 设置每一行cell的高度 35 self.tableView.rowHeight = 100; 36 37 // 设置每一组头部的高度 38 self.tableView.sectionHeaderHeight = 50; 39 40 // 设置每一组尾部的高度 41 // self.tableView.sectionFooterHeight = 50; 42 43 // 设置分割线颜色 44 self.tableView.separatorColor = [UIColor redColor]; 45 // 设置分割线样式 46 self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone; 47 48 // 设置表头控件 49 self.tableView.tableHeaderView = [[UISwitch alloc] init]; 50 // 设置表尾控件 51 self.tableView.tableFooterView = [UIButton buttonWithType:UIButtonTypeContactAdd]; 52 } 53 54 #pragma mark - <UITableViewDataSource> 55 // 如果不实现这个数据源方法,那么就是1组数据 56 //- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 57 //{ 58 // return 1; 59 //} 60 61 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 62 { 63 return self.wineArray.count; 64 } 65 66 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 67 { 68 // 创建一个UITableViewCellStyleSubtitle样式的cell 69 UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:nil]; 70 71 // 取出indexPath位置对应的XMGWine模型 72 XMGWine *wine = self.wineArray[indexPath.row]; 73 74 // 设置数据 75 cell.imageView.image = [UIImage imageNamed:wine.image]; 76 cell.textLabel.text = wine.name; 77 cell.detailTextLabel.text = [NSString stringWithFormat:@"¥%@", wine.money]; 78 cell.detailTextLabel.textColor = [UIColor orangeColor]; 79 cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; 80 81 // 设置cell的选中样式 82 // cell.selectionStyle = UITableViewCellSelectionStyleNone; 83 84 85 // backgroundView优先级 > backgroundColor 86 87 // 设置背景色 88 cell.backgroundColor = [UIColor redColor]; 89 90 // 设置背景view 91 UIView *bg = [[UIView alloc] init]; 92 bg.backgroundColor = [UIColor blueColor]; 93 cell.backgroundView = bg; 94 95 // 设置选中的背景view 96 UIView *selectedBg = [[UIView alloc] init]; 97 selectedBg.backgroundColor = [UIColor purpleColor]; 98 cell.selectedBackgroundView = selectedBg; 99 100 return cell; 101 } 102 103 //- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section 104 //{ 105 // return @"头部"; 106 //} 107 108 #pragma mark - <UITableViewDelegate> 109 /** 110 * 当选中一行的时候调用(点击) 111 */ 112 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 113 { 114 // XMGWine *wine = self.wineArray[indexPath.row]; 115 // NSLog(@"点击了:%@", wine.name); 116 NSLog(@"选中了:%zd", indexPath.row); 117 } 118 119 /** 120 * 当取消选中一行的时候调用 121 */ 122 - (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath 123 { 124 NSLog(@"取消选中了:%zd", indexPath.row); 125 } 126 127 - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section 128 { 129 return [UIButton buttonWithType:UIButtonTypeInfoDark]; 130 } 131 132 - (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section 133 { 134 return [[UISwitch alloc] init]; 135 } 136 137 //- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section 138 //{ 139 // if (section == 0) return 20; 140 // if (section == 1) return 50; 141 //} 142 143 /** 144 * 返回每个cell的高度 145 */ 146 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 147 { 148 if (indexPath.row % 2 == 0) { 149 return 50; 150 } else { 151 return 100; 152 } 153 } 154 @end
另外,如果你想要隐藏状态栏,你可以直接重写下面的方法,并返回YES
我个人在敲代码的时候遇到容易失误的地方,我自己引以为鉴,路人可以飘过:
4、性能优化
出于性能优化考虑:如果有200个cell,如果全部加载好了,那么界面以外还没显示的就太浪费了,所以就需要懒加载,等马上要显示,就加载需要的,苹果这一点已经给我们内置做好了这个优化,下面我们来测试验证一下懒加载的优化结果:
首先,我们都知道,每当有一个cell要进入视野范围内,就会调用一次的方法,所以我在这个方法内添加打印创建cell的内存地址的方法:
然后通过打印创建的cell的内存地址来看看其在内存的情况,然后推断性能好不好。
所以,拖拽TableView,是会频繁的创建和销毁。创建即将显示的cell,销毁已经不显示的cell。这个就是数据结构里的队列思想,先进先出,后进后出。只不过要改一改,先进先销毁,后进后销毁。只不过,频繁的创建和销毁也是耗费内存的计算复杂度的,也会耗费CPU的计算资源。
其实,还可以有更好的优化的方法:将cell循环利用。
循环利用的思想概括:假如屏幕最多刚好显示出7个cell,分别是1~7个cell,然后向上拖拽TableView,第8个即将显示,当然会创建出第8个 cell,这时候,第1个即将消失,到了完全消失的时候,利用用系统已经内置好的缓冲池,将这个第1个cell放置在缓冲池中不销毁,然后继续往上拖拽 TableView当第9个即将显示创建出来的时候,不是通过alloc init开辟新的内存空间创建的,而是直接将缓冲池之间存储的第一个cell拿过来用,然后仅仅要做的就是在这个cell上重新加载显示数据。这样循环利 用cell,可以很好地节约内存空间复杂度和内存的运行时间复杂度。 |
下面就在上面的实例将其循环利用cell的思想实现:
上动态图看看:
然后上静态图看结果:
5、性能优化的使用注意
1、标签取名与源数据相关而且有意义的。比如跟酒的数据相关的就用:NSString *ID = @"wine";
2、使用static NSString *ID = @"wine"; 使得ID只会创建一次
3、也可以使用全局,但是不建议,以为全局外部也可以访问那个全局数据。
4、在循环利用的代码里,因为那段设置每一行显示内容的方法会被频繁的调用,所以相同的代码最好写在if(!cell)语句里,而需要动态加载数据的不同的方法写在if(!cell)语句之外。
5、循环利用注意事项, 额外的if语句(不是if(!cell)的语句)有if肯定有else,因为只有if的话,又因为循环重复利用的特点,会使得所有if语句中的数据同化。
下面见证奇迹的时刻到了:
所以要把else补上。
6、Cell的重用原理
7、注册cell
和前面第一种优化方案相比,在创建cell对象的时候,第二种方案没法设定cell的样式,也就是会保持默认的样式。
但是这个也是有解决方案的,可以通过xib自定义cell,自定义cell组件可以包含自己想要的子控件,而且还可以动态的更改设置,然后创建自定义cell就可以了。
这里给出cell的样式一览:
8、索引条
首先在plist文件对于归类的数据,另外可以添加专门用于显示在组头和索引条上的字母。
这个plist文件等会会提供的项目源码中会有,包括plist文件对应的数据也在项目中
然后重写下面的方法,
另外可以在viewDidLoad方法中设置索引条样式
显示的效果:
项目源码链接: http://pan.baidu.com/s/1bnFQjOb 密码: hixf
9、关于UITableViewController
10、附录相关下载资源
关于上面展示过的车辆品牌展示的项目的练习资源,其实前面提供了项目下载,项目里面也有,但是这里还是提供直接下载的,这里还配备图片资源哦,练习的时候,可以把图片加载到cell里去:
链接: http://pan.baidu.com/s/1mgMtybe 密码: j4dp