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的重用原理

lOS设备的内存有限,如果用UITableView显示成千上万条数据,就需要成千上万个UITableViewCell对象的话,那将会耗尽iOS设备的内存。要解决该问题,需要重用UITableViewCell对象
 
重用原理:当滚动列表时,部分UITableViewCell会移出窗口,UITableView会将窗口外的UITableViewCell放入一个对象池中,等待重用。当UITableView要求dataSource返回UITableViewCell时,dataSource会先查看这个对象池,如果池中有未使用的UITableViewCell,dataSource会用新的数据配置这个UITableViewCell,然后返回给UITableView,重新显示到窗口中,从而避免创建新对象
 
还有一个非常重要的问题:有时候需要自定义UITableViewCell(用一个子类继承UITableViewCell),而且每一行用的不一定是同一种UITableViewCell,所以一个UITableView可能拥有不同类型的UITableViewCell,对象池中也会有很多不同类型的UITableViewCell,那么UITableView在重用UITableViewCell时可能会得到错误类型的UITableViewCell
 
解决方案:UITableViewCell有个NSString *reuseIdentifier属性,可以在初始化UITableViewCell的时候传入一个特定的字符串标识来设置reuseIdentifier(一般用UITableViewCell的类名)。当UITableView要求dataSource返回UITableViewCell时,先通过一个字符串标识到对象池中查找对应类型的UITableViewCell对象,如果有,就重用,如果没有,就传入这个字符串标识来初始化一个UITableViewCell对象

 

7、注册cell

和前面第一种优化方案相比,在创建cell对象的时候,第二种方案没法设定cell的样式,也就是会保持默认的样式。

但是这个也是有解决方案的,可以通过xib自定义cell,自定义cell组件可以包含自己想要的子控件,而且还可以动态的更改设置,然后创建自定义cell就可以了。

这里给出cell的样式一览:

8、索引条

首先在plist文件对于归类的数据,另外可以添加专门用于显示在组头和索引条上的字母。

这个plist文件等会会提供的项目源码中会有,包括plist文件对应的数据也在项目中

然后重写下面的方法,

 另外可以在viewDidLoad方法中设置索引条样式

显示的效果:

项目源码链接: http://pan.baidu.com/s/1bnFQjOb 密码: hixf

9、关于UITableViewController

 在使用UITableView的所有功能,其实只要UITableViewController,很多事就做好了。所以接下来就直接使用
 继承UITableViewController,然后storyboard拖一个UITableViewController的控件,然后class改为ViewController 然后实现两个方法即可。
 
 

10、附录相关下载资源

 关于上面展示过的车辆品牌展示的项目的练习资源,其实前面提供了项目下载,项目里面也有,但是这里还是提供直接下载的,这里还配备图片资源哦,练习的时候,可以把图片加载到cell里去:

链接: http://pan.baidu.com/s/1mgMtybe 密码: j4dp

 

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
posted @ 2015-11-23 20:11  何杨  阅读(471)  评论(0编辑  收藏  举报