一、什么是UITableView?
1. 再iOS开发中,要实现表格数据展示,做常用的做法就是使用UITableView
2、UITableView继承自UIScrollView,因此支持垂直滚动,而且性能极佳
3、UITableView的两种样式:UITableViewStylePlain、UITableViewStyleGrouped
4.UITableView 的常用属性
//分隔线
[self.tableView setSeparatorStyle:UITableViewCellSeparatorStyleSingleLine];
/**
32位真彩位:ARGB(Alpha、red、green、blue)
24位真彩位:RGB
*/
[self.tableView setSeparatorColor:[UIColor colorWithRed:1.0 green:1.0 blue:0 alpha:1.0]];
//setTableHeaderView视图通常用于放置图片轮播器内容,与分组无关
UIView *headerView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 20, 20)];
[headerView setBackgroundColor:[UIColor redColor]] ;
[self.tableView setTableHeaderView:headerView];
//setTableFooterView视图通常做上拉刷新功能
UIView *footerView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 20, 20)];
[footerView setBackgroundColor:[UIColor redColor]];
[self.tableView setTableFooterView:footerView];
二、如何展示数据
1、UITableView需要一个数据源来显示数据
2、UITableView会向数据源查询:一共有多少行数据、每一行显示什么数据
3、没有设置datasource的UITableView,只是一个空壳
4、凡是遵守UITableViewDatasource协议的oc对象,都可以是UITableView的datasource
5、tableView &datasource的关系
6、tableView展示数据的过程:
//1.调用数据源的下面方法得知一共有多少组数据
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView;
//2.调用数据源的下面方法得知每一组有多少行数据
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
//3.调用数据源的下面方法得知每一行显示什么内容
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
四、字典转模型举例(嵌套字典数组的模型转化)
//
// HSCarGroup.m
// 20160321-汽车品牌
//
// Created by devzkn on 3/21/16.
// Copyright © 2016 hisun. All rights reserved.
//
#import "HSCarGroup.h"
@implementation HSCarGroup
- (instancetype)initWithDictionary:(NSDictionary *)dict{
//KVC
self = [super init];//初始化父类属性
if (self) {
//初始化自身属性--将字典转化为HSCarGroup数据模型
[self setValue:dict[@"title"] forKey:@"title"];
//dict[@"cars"] 存放的是字段数组,与解析plist文件之后得到的数组类型一致;因此可以将dict[@"cars"]字典数组转化成HSCar模型数组
[self setCars:[HSCar cars:dict[@"cars"]]];
}
return self;
}
+ (instancetype)carGroupWithDictionary:(NSDictionary *)dict{
return [[self alloc]initWithDictionary:dict];
}
+ (NSArray *)carGroups{
NSMutableArray *tmpArrayM = [NSMutableArray array];
//解析plist
NSString *path = [[NSBundle mainBundle] pathForResource:@"cars_total" ofType:@"plist"];
NSArray *arrayDict = [NSArray arrayWithContentsOfFile:path];
for (NSDictionary *dict in arrayDict) {
[tmpArrayM addObject:[self carGroupWithDictionary:dict]];
}
return tmpArrayM;
}
- (NSString *) description{
return [NSString stringWithFormat:@"<%@: %p> {title: %@, cars: %@}",self.class,self,self.title,self.cars];
}
@end
2. KVC 之数组取值(常常应用于tableVie的索引数组生成)
// *****右侧索引列表*********
/**
返回的索引数组的“元素内容”和分组无关;索引数组的下表对应分组的下标
*/
- (NSArray<NSString *> *)sectionIndexTitlesForTableView:(UITableView *)tableView {
//1.使用遍历方式获取索引数组
// NSMutableArray<NSString *> *indexTitleArrayM = [NSMutableArray array];
// for (HSCarGroup *CarGroup in self.carGroups) {
// [indexTitleArrayM addObject:CarGroup.title];
// }
// return indexTitleArrayM;
//2.KVC(键值编码) cocoa的大招,用于间接修改、获取对象的属性的一种方式(字典转模型、过滤数组获取数据)
/**
1>使用kvc在获取数据时,如果取值对象没有包含keypath的“键名”,会自动进入对象的内部查找
2》若取值的对象是一个数组,则取值结果也是一个数组
*/
// NSLog(@"%@",[self.carGroups valueForKeyPath:@"cars.name"]);
return [self.carGroups valueForKeyPath:@"title"];
}
五、MVC(model、view、controller)
1、MVC的几个明显特性(体现)
1》View上面显示什么内容,取决于model
2》只要model数据改了,View的显示状态会跟着变更
3》controller负责初始化model,并将model数据传递给view去解析展示
2、MVC特性的应用(tableView的行移动、添加、删除)
/**
支持手势拖拽删除。删除事件的处理需要自己处理-》MVC,只要修改model,view显示的状态内容也会跟着改变
1.编辑风格类型:
typedef NS_ENUM(NSInteger, UITableViewCellEditingStyle) {
UITableViewCellEditingStyleNone,0
UITableViewCellEditingStyleDelete,1
UITableViewCellEditingStyleInsert2
};
2.表格格控件的修改操作实现过程,记得注重边界测试
*/
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath{
//1.处理删除键的监听事件
if ( UITableViewCellEditingStyleDelete == editingStyle) {
//1>删除处理,操作datasource
[self.dataList removeObjectAtIndex:indexPath.row];
//2>刷新表格
//*(重新加载所有数据)
// [self.tableView reloadData];
//*deleteRowsAtIndexPaths 表格控件动画地删除指定的行
[self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}else if (UITableViewCellEditingStyleInsert == editingStyle){
//2.插入数据处理
[self.dataList insertObject:@"new lydia" atIndex:indexPath.row+1];
NSIndexPath *idxPath = [NSIndexPath indexPathForRow:indexPath.row+1 inSection:indexPath.section];
//表格控件动画地在指定的indexPath数组添加指定的行
[self.tableView insertRowsAtIndexPaths:@[idxPath] withRowAnimation:UITableViewRowAnimationMiddle];
}
}
/**
移动表格的行
*/
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath{
//根据sourceIndexPath、destinationIndexPath调整model数据
// [self.dataList exchangeObjectAtIndex:sourceIndexPath.row withObjectAtIndex:destinationIndexPath.row];
//从数组取出源
id sourceObject = self.dataList[sourceIndexPath.row];
//从数组删除源
[self.dataList removeObjectAtIndex:sourceIndexPath.row];
NSLog(@"%@",self.dataList);
//将源插入数组的目标位置
[self.dataList insertObject:sourceObject atIndex:destinationIndexPath.row];
NSLog(@"%@",self.dataList);
}
#pragma mark - UITableViewDelegate代理方法
/**
If not implemented, all editable cells will have UITableViewCellEditingStyleDelete set for them when the table has editing property set to YES.
*/
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath{
return (indexPath.row % 2)?UITableViewCellEditingStyleDelete :UITableViewCellEditingStyleInsert;
}
表格的修改,常常用于本地数据缓存的场景,例如:个人通讯录、QQ聊天记录、本地日记本
六、cell的简介
UITableView的每一行都是一个UITableViewCell
1.cell的初始化: 通过datasource的tableView: cellForRowAtIndexPath: 方法来初始化每一行
2、UITableViewCell内部有一个默认的自视图ContentView :
ContentView是UITableViewCell所显示内容的父视图,可显示一些辅助视图(辅助视图的作用是显示一个表示动作的图标);contentView 默认有3个子视图(textLabel、detailTextLabel、UIImageView)
3、UITableViewCell的UITabelCellStyle属性:
用于决定使用contentView的哪些子视图,以及这个视图在contentView中的位置
4、cell的结构
七、cell的重用原理
1.重用原理
当滚动列表时,部分cell会移出窗口,UITableView会将窗口外的cell放入一个等待重用的对象池;
当UITableView要求datasource返回cell的时候,datasource会先查看这个对象池是否有未使用的cell,若有,datasource会用新的数据配置这个cell,并返回给UITableVIew重新显示到窗口中,从而避免创建新的对象。
2、解决一个TableView同时拥有不通类型的cell的问题
解决方案:
在初始化cell的时候,传入一个特定的“字符串标识”(通常使用cell的类名)来给cell的reuseIdentifier属性赋值;
当UITableView 要求datasource返回cell的时候,此时就利用reuseIdentifier属性到对象词中查找对应类型的cell对象,若找到就重用,否则利用这个reuseIdentifier属性来实例化一个cell对象
3. cell重用的例子:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// 1.定义一个cell的标识
static NSString *ID = @"mjcell";
// 2.从缓存池中取出cell
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
// 3.如果缓存池中没有cell
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID];
}
// 4.设置cell的属性...
return cell;
}
4、单元格循环引用概念
八、使用xib封装一个View的步骤
1、new一个xib文件来描述一个View的内部结构
2、new一个自定义的视图类(继承自xib根对象的class),类名通常与xib文件名保存一致
3、将xib中的控件与自定义视图类的.m文件建立连线 (建立连线之前,对根对象的class指定为刚刚新建的控件->在xi b属性面板指定可重用标识符
4、提供一个类方法,返回一个创建好的自定义视图类(屏蔽从xib加载的过程)
5、提供一个模型属性让外界传递模型数据,重写模型属性的setter方法(将模型数据展示到对应的子控件上面)
九、delegate:父控件(视图控制器)监听子控件的事件,当子控件发生某些事情的时候,通知父控件工作--备注:父控件通知子控件工作,直接调用子控件的方法即可
0、如果使用强引用,将造成循环的强引用--》儿子只能对父亲进行弱引用
@property (nonatomic,weak) id<HSGroupBuyingFooterViewDelegate> delegate;//在oc中,只有没有强用的时候,才会被立即释放;一旦自定义视图称为视图控制器的视图包含,极自定义视图为视图控制器的儿子时,且视图控制器为自定义视图(儿子)的代理,此时如果代理是强引用,将造成循环的强引用,”你中有我,我中有你“。--永远呆在内存
1、delegate的使用场合
1》对象A内部发生了一些事情,想通知对象B,对象A想传递数据给对象B
2》对象B想监听对象A内部发生了什么事情
3》对象A想在自己的方法内部调用对象B的某些方法,并且对象A不能对对象B有耦合依赖
2、使用delegate的步骤
1》搞清楚谁 是 谁的 delegate
2>定义代理协议(协议名称的命名规范:控件类名+Delegate)
3》定义代理方法:
*代理方法一般都定义为optional
*代理方法名称都以空间名开头;代理方法至少有个参数,用于将控件本事传递出去
4》设置代理对象(代理对象遵守协议,并实现协议方法)
5》在恰当的时刻调用delegate的协议方法,来通知delegate发生了什么事情(在调用之前判断代理是否实现了该代理方法)
十、通过代码自定义cell
1.new一个继承自UITableViewCell的类
2.重写initWithStyle: reuseIndentifier: 方法(对子控件的属性进行一次性赋值)
--有些属性只须设置一次,例如字体、固定的图片
3.并添加需要显示的子控件到contentView中
4、提供数据模型、frame模型
数据模型用于存放文字、图片数据;frame模型存放数据模型、所有子控件的frame、cell的高度
5.cell拥有一个frame模型
6、重写frame模型的setter方法(设置子控件的显示数frame);对frame的对象实例化采用懒加载方法,即进行getter方法的重写
//
// HSStatusCell.h
// 20160324-新浪微博
//
// Created by devzkn on 3/24/16.
// Copyright © 2016 hisun. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "HSStatus.h"
@interface HSStatusCell : UITableViewCell
//自定义视图的现实的数据来源于模型,即使用模型装配自定义视图的显示内容
@property (nonatomic,strong) HSStatus *status;//视图对应的模型,是视图提供给外界的接口
/**
通过数据模型设置视图内容,可以让视图控制器不需要了解视图的细节
*/
+ (instancetype) tableVieCellwWithStatus:(HSStatus *) status tableView:(UITableView *)tableView;//使用类方法获取自定义视图,参数用于视图的数据装配
//
+ (instancetype) tableVieCellwWittableView:(UITableView *)tableView;//使用类方法获取自定义视图
@end
- (void)setStatus:(HSStatus *)status{
_status = status;
[self settingData];
//设置位置
[self settingFrame];
}
- (void) settingData{
//设置位置
[self.textView setText:self.status.text];
[self.nameView setText:self.status.name];
[self.iconView setImage:self.status.iconImage];
if (self.status.picture.length > 0) {
[self.pictureView setImage:self.status.pictureImage];
[self.pictureView setHidden:NO];
}else{
[self.pictureView setImage:nil];//没有配图的时候,清空图片信息--cell重用的时候,对于可选视图要进行处理
[self.pictureView setHidden:YES];
}
if (self.status.vip) {
[self.vipView setImage:self.status.vipImage];
[self.vipView setHidden:NO];//显示VIP视图
[self.nameView setTextColor:[UIColor redColor]];
}else{
[self.vipView setImage:nil];//不是VIP的时候,清空VIP标识--cell重用的时候,针对可选视图进行特殊处理
[self.vipView setHidden:YES];
[self.nameView setTextColor:[UIColor blackColor]];
}
}
/**
设置位置信息
*/
- (void) settingFrame{
//定义间距
CGFloat padding =10;
CGFloat iconX = padding;
CGFloat iconY = padding;
CGFloat iconWidth = 30;
CGFloat iconHeiht = 30;
[self.iconView setFrame:CGRectMake(iconX, iconY, iconWidth, iconHeiht)];
//设置昵称,昵称的大小由文字的长度决定
/**
1.boundingRectWithSize 方法计算给定文本所占用的区域
2.options: 计算多行的的准确高度需要传入NSStringDrawingUsesLineFragmentOrigin
3、attributes 指定字体相关属性;UIKit框架中的第一个头文件 NSAttributedString.h
*/
NSDictionary *nameDict = @{NSFontAttributeName:KnameFont};
CGRect nameFrame = [self.status.name boundingRectWithSize:CGSizeMake(MAXFLOAT, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:nameDict context:nil];//返回一个x,y,都为0 的CGRect
nameFrame.origin.x = CGRectGetMaxX(self.iconView.frame)+padding;
nameFrame.origin.y = padding+(CGRectGetHeight(self.iconView.frame)-CGRectGetHeight(nameFrame))*0.5;
[self.nameView setFrame:nameFrame];
//设置VIP标识的frame
[self.vipView setFrame:CGRectMake(CGRectGetMaxX(self.nameView.frame)+padding, CGRectGetHeight(self.nameView.frame), 14, 14)];
//设置文本内容的frame
NSDictionary *textDict = @{NSFontAttributeName:KtextFont};
CGRect textFrame = [self.status.text boundingRectWithSize:CGSizeMake(300, MAXFLOAT) options: NSStringDrawingUsesLineFragmentOrigin attributes:textDict context:nil];
textFrame.origin.x = padding;
textFrame.origin.y = CGRectGetMaxY(self.iconView.frame)+padding ;
[self.textView setFrame:textFrame];
//设置picture的frame
[self.pictureView setFrame:CGRectMake(padding,CGRectGetMaxY(self.textView.frame)+padding, 100, 100)];
}
#pragma mark - tableView delegate方法
#if 1
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
/**
问题背景: 执行此方法的时候,cell还没被实例化;行高实在实例化cell之后,设置cell对象的模型属性status的时候计算的
解决方案:得到模型-》确定行高
*/
//在cell实例化之前,获取行高-》先获取到模型
HSStatus *statuse = self.stautses[indexPath.row];
//开始计算行高
// CGFloat height = padding+iconView.frame.height+padding+textView.frame.height+pictureView.frame.height+padidng;
CGFloat iconViewHeight = 30;
CGFloat pictureViewHeight = 100;
CGFloat padding = 10;
CGFloat textViewHeight = [statuse.text boundingRectWithSize:CGSizeMake(300, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:16]} context:nil].size.height;
CGFloat height =iconViewHeight+textViewHeight+((statuse.picture.length >0 ? pictureViewHeight+padding: 0))+padding*2;
return height;
}
#endif
十一、cell类型&右侧视图及代理方法
//设置右侧视图
/**
1.UITableViewCellAccessoryDisclosureIndicator 箭头,可以提示用户们当前行是可以点击的,通常选择行会跳到新的页面
2、UITableViewCellAccessoryDetailButton 按钮,通常点击按钮可以做独立的操作,例如进行alertView;点击按钮并不会选中行
3、UITableViewCellAccessoryDetailDisclosureButton 箭头+按钮,它们是各自工作的控件
4、UITableViewCellAccessoryCheckmark 对号,通常提示用户该行数据设置完毕,使用的比较少
*/
[cell setAccessoryType:UITableViewCellAccessoryDetailDisclosureButton];
//自定义右侧控件
/**
setAccessoryType 不满足实现的时候,才使用setAccessoryView进行自定义控件
1.setAccessoryView需要自行添加监听方法,应用场景通常是自定义cell,监听方法不要写在视图控制器中
2、
*/
UISwitch *switcher = [[UISwitch alloc] init];
[switcher addTarget:self action:@selector(updateSwitcher) forControlEvents:UIControlEventValueChanged];
[cell setAccessoryView: switcher];
#pragma mark - 代理方法通常没有返回值
/**
点击右侧按钮的监听方法,此协议方法只为setAccessoryType服务,对于自定义控件setAccessoryView不会进行响应
*/
- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath{
NSLog(@"%d,%s",indexPath.row,__func__);
}
//取消选择某一行,有箭头的,极少使用此协议方法,极容易出错
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath{
// NSLog(@"%d,%s",indexPath.row,__func__);
}
//选中某一行,有箭头的
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
// NSLog(@"%d,%s",indexPath.row,__func__);
}
实现新浪微博的重点地方
1、. cellHeight 可以通过Status计算
/** 利用数据模型计算控件位置-》今儿计算cell的行高/
- (void)setCellHeight{
CGFloat picHeight = (self.status.picture.length >0) ? KPading+CGRectGetHeight(self.pictureViewFrame): 0;
_cellHeight = CGRectGetHeight(self.iconViewFrame)+CGRectGetHeight(self.textViewFrame)+picHeight+2*KPading;
}
2.Frame模型 替代数据模型(数据模型用于存放文字、图片数据;frame模型存放数据模型、所有子控件的frame、cell的高度)
1》目前存在什么问题?
所有的单元格控件的计算都是重复的,而且每次表格滚动,设置内容都会重新计算。
2》解决这个问题:
目前控制器中的数组保存的是status模型,将status模型替换为statusFrame模型{status,所有控件的位置},同样具有status模型
3》解决步骤
* 只是修改视图控制器中的代码,暂时不要动cell。 --(将原有的status模型替换为statusFrame模型)
**重写statusFrame set方法--设置子控件的显示数frame
* 重构代码一定要小步来
//
// HSStatusFrame.m
// 20160324-新浪微博
//
// Created by devzkn on 3/24/16.
// Copyright © 2016 hisun. All rights reserved.
//
#import "HSStatusFrame.h"
#define KPading 10
#define KnameFont [UIFont systemFontOfSize:14]
#define KtextFont [UIFont systemFontOfSize:16]
@interface HSStatusFrame ()
//重写frame模型的setter方法(设置子控件的显示数frame);对frame的对象实例化采用懒加载方法,即进行getter方法的重写
@end
@implementation HSStatusFrame
-(void)setStatus:(HSStatus *)status{
_status = status;
[self setIconViewFrame];
[self setNameViewFrame];
[self setVipViewFrame];
[self setTextViewFrame];
[self setPictureViewFrame];
[self setCellHeight];
}
/** 计算头像控件的frame*/
- (CGRect)setIconViewFrame{
//定义间距
CGFloat iconX = KPading;
CGFloat iconY = KPading;
CGFloat iconWidth = 30;
CGFloat iconHeiht = 30;
_iconViewFrame= CGRectMake(iconX, iconY, iconWidth, iconHeiht);
return _iconViewFrame;
}
- (void)setNameViewFrame {
//设置昵称,昵称的大小由文字的长度决定
/**
1.boundingRectWithSize 方法计算给定文本所占用的区域
2.options: 计算多行的的准确高度需要传入NSStringDrawingUsesLineFragmentOrigin
3、attributes 指定字体相关属性;UIKit框架中的第一个头文件 NSAttributedString.h
*/
NSDictionary *nameDict = @{NSFontAttributeName:KnameFont};
CGRect nameFrame = [self.status.name boundingRectWithSize:CGSizeMake(MAXFLOAT, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:nameDict context:nil];//返回一个x,y,都为0 的CGRect
nameFrame.origin.x = CGRectGetMaxX(self.iconViewFrame)+KPading;
nameFrame.origin.y = KPading+(CGRectGetHeight(self.iconViewFrame)-CGRectGetHeight(nameFrame))*0.5;
_nameViewFrame =nameFrame;
}
- (void)setVipViewFrame{
_VipViewFrame = CGRectMake(CGRectGetMaxX(self.nameViewFrame)+KPading, CGRectGetHeight(self.nameViewFrame), 14, 14);
}
- (void)setTextViewFrame{
NSDictionary *textDict = @{NSFontAttributeName:KtextFont};
CGRect textFrame = [self.status.text boundingRectWithSize:CGSizeMake(300, MAXFLOAT) options: NSStringDrawingUsesLineFragmentOrigin attributes:textDict context:nil];
textFrame.origin.x = KPading;
textFrame.origin.y = CGRectGetMaxY(self.iconViewFrame)+KPading ;
_textViewFrame = textFrame;
}
/** 计算配图的frame*/
- (void)setPictureViewFrame{
self.pictureViewFrame = CGRectMake(KPading,CGRectGetMaxY(self.textViewFrame)+KPading, 100, 100);
}
/** 计算cell的行高*/
- (void)setCellHeight{
CGFloat picHeight = (self.status.picture.length >0) ? KPading+CGRectGetHeight(self.pictureViewFrame): 0;
_cellHeight = CGRectGetHeight(self.iconViewFrame)+CGRectGetHeight(self.textViewFrame)+picHeight+2*KPading;
}
//实例化frame模型
- (instancetype)initWithStatus:(HSStatus *)status{
//KVC
self = [super init];//初始化父类属性
if (self) {
//初始化自身属性
[self setStatus:status];
}
return self;
}
//实例化frame模型
+ (instancetype)statusFrameWithStatus:(HSStatus *)status{
return [[self alloc]initWithStatus:status];
}
//返回frame模型数组
+ (NSArray *)statusFrames{
NSMutableArray *tmpArrayM = [NSMutableArray array];
//解析plist
NSArray *arrayDict = [HSStatus statuses];
for (HSStatus *status in arrayDict) {
[tmpArrayM addObject:[self statusFrameWithStatus:status]];
}
return tmpArrayM;
}
@end
小结
1.编辑技巧:
1》代码块存放路径
/Users/devzkn/Library/Developer/Xcode/UserData/CodeSnippets 存储的是自定义的代码块文件
~/Library/Developer/Xcode/UserData/CodeSnippets--换新电脑,直接替换文件夹中的内容即可。
2》视图的分割线设置(使用UIView,高度为1,设置下背景颜色即可)
2、代理小结
1》作用:监听控件的某些事件、使用代理模式,是为了在程序直接”解耦”
2》代理例子:UITableView表格可以显示非常丰富的数据,为了达到这一效果,设置表格的”数据源”UITableViewDataSource
@required 必须实现的方法
@optional 可选的实现方法->不强求实现->如果实现了能得到特殊的效果,如果不实现,也不影响程序的正常运行
能够增加控件的灵活度
3》代理阶段性小结
1》》. 遵守协议,预先定义好方法,不实现,具体的实现工作由代理负责
<控件的名字+DataSource> 定义的与数据有关的方法,通常有返回参数
<控件的名字+Delegate> 定义的与事件有关的方法,通常用来监听控件事件的,通常没有返回值
2》》. 代理方法
* 方法名以控件名称开头(没有类前缀) -> 方便程序员编写的时候,快速找到需要的协议方法
* 第一个参数是自己 -> 意味着在协议方法中,可以直接访问对象的属性,或者调用方法
* 代理方法的返回值 -> 控制器向控件(委托)发送数据
3.
#pragma mark - 代理方法
/**
一、表格工作观察的小结
1、numberOfRowsInSection要知道每组的总共有多少数据-》
2、heightForRowAtIndexPath计算每行行高
作用:
1》此方法的提前调用,是为了计算contentSize,因为UITableView继承自UIScrollView,UIScrollView 要指定了contentsize之后才能滚动
2》 确定屏幕应该显示多少行,也就确定了cellForRowAtIndexPath执行次数
-》
3、cellForRowAtIndexPath表格明细,懒加载--只有要显示的cell才会被实例化
二、UITableView行高设置的小结论---时刻关注性能问题
1、代理方法heightForRowAtIndexPath:优先级高于[_tableView setRowHeight:80];
应用场景:每一行的高度都不一样,例如新浪微博;但效率差
2、[_tableView setRowHeight:80]; 效率更高,适用于所有的cell高度一致的场景
*/
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
NSLog(@"%s---------row-------%d",__func__,indexPath.row);
return (indexPath.row % 2) ? 60 : 44;//YES被定义为1
// return 44;可以使用[_tableView setRowHeight:80]替换,效率会更好
}
- 错误分析
1.CUICatalog: Invalid asset name supplied: (null)
_pictureImage = [UIImage imageNamed:self.picture];//self.picture为nil,将会报错CUICatalog: Invalid asset name supplied: (null)