前言
NS_CLASS_AVAILABLE_IOS(2_0) @interface UITableView : UIScrollView <NSCoding>
@available(iOS 2.0, *) public class UITableView : UIScrollView, NSCoding
1、tableView 的创建
- Objective-C
- 遵守 UITableViewDelegate, UITableViewDataSource 协议
- 数据源 初始化
// 声明数据源,必须声明为全局变量 @property(nonatomic, retain)NSMutableArray *myDataArray; // 数据源数组初始化,定义一个可变数组做为表格的数据源 myDataArray = [[NSMutableArray alloc] init]; NSArray *array1 = @[@"UIWindow", @"UIApplication", @"UIView", @"UILabel", @"UIProgressView", @"UIAlertView", @"UIActionSheet", @"UIPickerView"]; NSArray *array2 = @[@"窗口", @"应用", @"视图", @"标签", @"进度条", @"警告框", @"操作表", @"选择框", @"风火轮", @"图像视图", @"网页视图", @"滚动视图", @"多行文本视图"]; // 向数据源中添加数据 [myDataArray addObject:array1]; [myDataArray addObject:array2];
- tableView 初始化
// 声明表格视图对象,头标题和脚标题悬浮显示,默认类型 UITableView *myTableView = [[UITableView alloc] initWithFrame:self.view.bounds]; // 设置 tableView 的代理 myTableView.delegate = self; myTableView.dataSource = self; // 将 tableView 添加到窗口中 [self.view addSubview:myTableView];
- 协议方法
// 设置分段数 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { // 数据源数组为多维数组时,用数组计算 return myDataArray.count; } // 设置行数 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { // section 段,返回每段中有多少行 return [[myDataArray objectAtIndex:section] count]; } // 设置每一行显示的内容 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { // 创建标识词,随意设置,但不能和其它 tableView 的相同 static NSString *indentifier = @"testIdentifier"; // 根据标志词先从复用队列里查找 UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:indentifier]; // 复用队列中没有时再创建 if (cell == nil) { // 创建新的 cell,默认为主标题模式 cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:indentifier]; } // 设置每一行显示的文字内容 cell.textLabel.text = [[myDataArray objectAtIndex:indexPath.section] objectAtIndex:indexPath.row]; // indexPath.section 分段数,indexPath.row 行数,设置图片内容,图片在 Cell 的左端,图片大小自动压缩 cell.imageView.image = [UIImage imageNamed:@"HQ_0003"]; return cell; }
- Swift
- 遵守 UITableViewDelegate, UITableViewDataSource 协议
- 数据源 初始化
// 声明数据源,必须声明为全局变量 var myDataArray:[[String]] = Array() let array1:[String] = ["UIWindow", "UIApplication", "UIView", "UILabel", "UIProgressView", "UIAlertView", "UIActionSheet", "UIPickerView"] let array2:[String] = ["窗口", "应用", "视图", "标签", "进度条", "警告框", "操作表", "选择框", "风火轮", "图像视图", "网页视图", "滚动视图", "多行文本视图", "工具条"] // 向数据源中添加数据 myDataArray.append(array1) myDataArray.append(array2)
- tableView 初始化
// 声明表格视图对象,头标题和脚标题悬浮显示,默认类型 let myTableView:UITableView = UITableView(frame: self.view.bounds) // 设置 tableView 的代理 myTableView.delegate = self myTableView.dataSource = self // 将 tableView 添加到窗口中 self.view.addSubview(myTableView)
- 协议方法
// 设置分段数 func numberOfSectionsInTableView(tableView: UITableView) -> Int { // 数据源数组为多维数组时,用数组计算 return myDataArray.count } // 设置行数 func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { // section 段,返回每段中有多少行 return myDataArray[section].count } // 设置每一行显示的内容 func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { // 创建标识词,随意设置,但不能和其它 tableView 的相同 let indentifier = "testIdentifier" // 根据标志词先从复用队列里查找 var cell = tableView.dequeueReusableCellWithIdentifier(indentifier) // 复用队列中没有时再创建 if cell == nil { // 创建新的 cell,默认为主标题模式 cell = UITableViewCell(style: .Default, reuseIdentifier: indentifier) } // 设置每一行显示的文字内容 cell!.textLabel?.text = myDataArray[indexPath.section][indexPath.row] // indexPath.section 分段数,indexPath.row 行数,设置图片内容,图片在 Cell 的左端,图片大小自动压缩 cell!.imageView?.image = UIImage(named: "HQ_0003") return cell! }
2、tableView 的设置
- Objective-C
- 设置数据源初始化方式
// 将数组指向新的空间,可以不提前申请空间(不初始化) myDataArray = @[array1, array2]; // 将数组里的所有数据替换成新的,必须提前申请空间 myDataArray.array = @[array1, array2];
- 设置分段的头标题和脚标题
// 设置分段的头标题和脚标题的类型 /* UITableViewStylePlain, // 简单模式,每个分段之间紧密连接,头脚标题悬浮显示,默认类型 UITableViewStyleGrouped // 分组模式,每个分段之间分开,头脚标题跟随移动,头标题英文自动大写 */ // 头标题和脚标题悬浮显示,默认类型 UITableView *myTableView = [[UITableView alloc] init]; UITableView *myTableView = [[UITableView alloc] initWithFrame:frame]; // 带显示类型的设置 UITableView *myTableView = [[UITableView alloc] initWithFrame:frame style:UITableViewStyleGrouped]; // 设置分段的头标题高度,UITableViewDelegate 协议方法 - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section { return 40; } // 设置分段的脚标题高度,UITableViewDelegate 协议方法 - (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section { return 30; } // 设置分段的头标题内容,UITableViewDataSource 协议方法 - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { if (0 == section) { return @"English Header"; } else { return @"中文 Header"; } } // 设置分段的脚标题内容,UITableViewDataSource 协议方法 - (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section { if (0 == section) { return @"English Footer"; } else { return @"中文 Footer"; } } // 分段头标题视图,UITableViewDelegate 协议方法,返回自定义的标题视图 - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section { if (!section) { label.text = @"English Header"; } else{ label.text = @"中文 Header"; } return label; } // 分段脚标题视图,UITableViewDelegate 协议方法 - (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section { if (!section) { label.text = @"English Footer"; } else{ label.text = @"中文 Footer"; } // 返回自定义的标题视图 return label; }
- 设置表格的表头和表尾视图
// 设置表格的表头视图 /* 只有视图的高度设置起作用 */ myTableView.tableHeaderView = myHeaderView; // 设置表格的表尾视图 /* 只有视图的高度设置起作用 */ myTableView.tableFooterView = myFooterView;
- 设置表格的背景
// 设置表格的背景视图 myTableView.backgroundView = myImageView; // 设置表格的背景颜色 myTableView.backgroundColor = [UIColor blueColor];
- 设置表格的分割线
// 设置表格的分割线颜色 /* 设置为 clearColor 时即可隐藏(不显示)所有分割线 */ myTableView.separatorColor = [UIColor redColor]; // 设置表格的分割线类型 /* UITableViewCellSeparatorStyleNone, // 没有分割线 UITableViewCellSeparatorStyleSingleLine, // 单线型,默认 // 嵌刻线型,This separator style is only supported for grouped style UITableViewCellSeparatorStyleSingleLineEtched */ myTableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine; // 设置表格的分割线边距 /* 上、左、下、右,只有左、右设置有效 设置左边距时会使标题相应的右移 左边距设置为 0 时,分割线不会紧靠左边框 */ myTableView.separatorInset = UIEdgeInsetsMake(0, 10, 0, 10); // 清除表格多余的分割线 /* 表格为 UITableViewStylePlain 类型时,若表格的内容没有占满屏幕时,没有设置内容的部分表格也会有分割线 创建自定义的 view,将该 view 的背景颜色清空(默认为透明),并添加到表格的脚视图上 */ myTableView.tableFooterView = [[UIView alloc] init]; // 设置表格分割线左边距为零 [myTableView setSeparatorInset:UIEdgeInsetsZero]; [myTableView setLayoutMargins:UIEdgeInsetsZero]; - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath{ // UITableViewDelegate 协议方法 [cell setSeparatorInset:UIEdgeInsetsZero]; [cell setLayoutMargins:UIEdgeInsetsZero]; } // 自定义表格分割线 /* 系统分割线的左边无法紧靠表格左边框,隐藏系统分割线,自定义视图,添加到 Cell 的下边实现 同时可以清除表格在 UITableViewStylePlain 类型时的多余分割线 */ myTableView.separatorStyle = UITableViewCellSeparatorStyleNone; if (cell == nil) { // 创建新的 cell cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:indentifier]; // 添加自定义分割线视图 CGRect frame = CGRectMake(0, cell.contentView.bounds.size.height, self.view.bounds.size.width, 1); UIView *mySeparatorView = [[UIView alloc] initWithFrame:frame]; mySeparatorView.backgroundColor = [[UIColor lightGrayColor] colorWithAlphaComponent:0.5]; [cell.contentView addSubview:mySeparatorView]; }
- 设置表格的行高
// 属性设置 /* 设置全部行的高度,默认为 44 */ myTableView.rowHeight = 60; // 协议方法设置 /* 可单独设置每一行的高度,默认为 44 */ - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { return 60; } // 设置估计行高 /* 设置全部行的高度 */ self.tableView.estimatedRowHeight = 80; // 协议方法设置估计行高 /* 可单独设置每一行的估计行高 */ - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath { return 80; } // 设置自动计算行高 self.tableView.rowHeight = UITableViewAutomaticDimension;
- 设置表格的编辑开关状态
// 打开表格的编辑模式 /* default is NO. setting is not animated */ myTableView.editing = YES; // 翻转表格的编辑模式 myTableView.editing = !myTableView.editing; // 翻转表格的编辑模式 [myTableView setEditing:!myTableView.editing animated:YES];
- 设置表格选择状态
// 设置表格普通模式下是否允许单选 /* default is YES. Controls whether rows can be selected when not in editing mode */ myTableView.allowsSelection = YES; // 设置表格在编辑模式下是否允许单选 /* default is NO. Controls whether rows can be selected when in editing mode */ myTableView.allowsSelectionDuringEditing = YES; // 设置表格普通模式下是否允许多选 /* default is NO. Controls whether multiple rows can be selected simultaneously */ myTableView.allowsMultipleSelection = YES; // 设置表格在编辑模式下是否允许多选 /* default is NO. Controls whether multiple rows can be selected simultaneously in editing mode */ myTableView.allowsMultipleSelectionDuringEditing = YES; // 取消表格选择 /* 在表格选中协议方法中设置,表格点击变色后恢复原来颜色,设置后无法实现表格多选 */ [tableView deselectRowAtIndexPath:indexPath animated:YES];
- 重载表格视图
// 重载表格视图 /* 重走所有的表格视图方法,刷新所有的表格 */ [tableView reloadData]; // 重载某一分段 [tableView reloadSections:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation:UITableViewRowAnimationAutomatic]; // 重载某一个行 [tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; // 删除一个 cell /* 只刷新删除的 cell */ [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; // 插入一个 cell /* 只刷新插入的 cell */ [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
- 设置数据源初始化方式
- Swift
- 设置分段的头标题和脚标题
// 设置分段的头标题和脚标题的类型 /* case Plain // 简单模式,每个分段之间紧密连接,头脚标题悬浮显示,默认类型 case Grouped // 分组模式,每个分段之间分开,头脚标题跟随移动,头标题英文自动大写 */ // 头标题和脚标题悬浮显示,默认类型 let myTableView:UITableView = UITableView() let myTableView:UITableView = UITableView(frame: frame) // 带显示类型的设置 let myTableView:UITableView = UITableView(frame: frame, style: .Grouped) // 设置分段的头标题高度,UITableViewDelegate 协议方法 func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { return 40 } // 设置分段的脚标题高度,UITableViewDelegate 协议方法 func tableView(tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat { return 30 } // 设置分段的头标题内容,UITableViewDataSource 协议方法 func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? { if 0 == section { return "English Header" } else { return "中文 Header" } } // 设置分段的脚标题内容,UITableViewDataSource 协议方法 func tableView(tableView: UITableView, titleForFooterInSection section: Int) -> String? { if 0 == section { return "English Footer" } else { return "中文 Footer" } } // 分段头标题视图,UITableViewDelegate 协议方法,返回自定义的标题视图 func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { if 0 == section { label.text = "English Header" } else { label.text = "中文 Header" } return label } // 分段脚标题视图,UITableViewDelegate 协议方法,返回自定义的标题视图 func tableView(tableView: UITableView, viewForFooterInSection section: Int) -> UIView? { if 0 == section { label.text = "English Footer" } else { label.text = "中文 Footer" } return label }
- 设置表格的表头和表尾视图
// 设置表格的表头视图 /* 只有视图的高度设置起作用 */ myTableView.tableHeaderView = myHeaderView // 设置表格的表尾视图 /* 只有视图的高度设置起作用 */ myTableView.tableFooterView = myFooterView
- 设置表格的背景
// 设置表格的背景视图 myTableView.backgroundView = myImageView // 设置表格的背景颜色 myTableView.backgroundColor = UIColor.blueColor()
- 设置表格的分割线
// 设置表格的分割线颜色 /* 设置为 clearColor 时即可隐藏(不显示)所有分割线 */ myTableView.separatorColor = UIColor.redColor() // 设置表格分割线的类型 /* case None // 没有分割线 case SingleLine // 单线型,默认 case SingleLineEtched // 嵌刻线型,This separator style is only supported for grouped style */ myTableView.separatorStyle = .SingleLine // 设置表格的分割线边距 /* 上、左、下、右,只有左、右设置有效 设置左边距时会使标题相应的右移 左边距设置为 0 时,分割线不会紧靠左边框 */ myTableView.separatorInset = UIEdgeInsetsMake(0, 10, 0, 10) // 清除表格多余的分割线 /* 表格为 Plain 类型时,若表格的内容没有占满屏幕时,没有设置内容的部分表格也会有分割线 创建自定义的 view,将该 view 的背景颜色清空(默认为透明),并添加到表格的脚视图上 */ myTableView.tableFooterView = UIView() // 自定义表格分割线 /* 系统分割线的左边无法紧靠表格左边框,隐藏系统分割线,自定义视图,添加到 Cell 的下边实现 同时可以清除表格在 Plain 类型时的多余分割线 */ myTableView.separatorStyle = .None if cell == nil { // 创建新的 cell cell = UITableViewCell(style: .Default, reuseIdentifier: indentifier) // 添加自定义分割线视图 let frame:CGRect = CGRectMake(0, cell!.contentView.bounds.size.height, self.view.bounds.size.width, 1) let mySeparatorView:UIView = UIView(frame: frame) mySeparatorView.backgroundColor = UIColor.lightGrayColor().colorWithAlphaComponent(0.5) cell!.contentView.addSubview(mySeparatorView) }
- 设置表格的行高
// 属性设置 /* 设置全部行的高度,默认为 44 */ myTableView.rowHeight = 60 // 协议方法设置 /* 可单独设置每一行的高度,默认为 44 */ func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { return 60 } // 设置估计行高 /* 设置全部行的高度 */ self.tableView.estimatedRowHeight = 80 // 协议方法设置估计行高 /* 可单独设置每一行的估计行高 */ func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { return 80 } // 设置自动计算行高 self.tableView.rowHeight = UITableViewAutomaticDimension
- 设置表格的编辑开关状态
// 打开表格的编辑模式 myTableView.editing = true // 翻转表格的编辑模式,直接出现 myTableView.editing = !myTableView.editing // 翻转表格的编辑模式,带动画效果 myTableView.setEditing(!myTableView.editing, animated: true)
- 设置表格选择状态
// 设置表格普通模式下是否允许单选 /* default is YES. Controls whether rows can be selected when not in editing mode */ myTableView.allowsSelection = false // 设置表格在编辑模式下是否允许单选 /* default is NO. Controls whether rows can be selected when in editing mode */ myTableView.allowsSelectionDuringEditing = true // 设置表格普通模式下是否允许多选 /* default is NO. Controls whether multiple rows can be selected simultaneously */ myTableView.allowsMultipleSelection = true // 设置表格在编辑模式下是否允许多选 /* default is NO. Controls whether multiple rows can be selected simultaneously in editing mode */ myTableView.allowsMultipleSelectionDuringEditing = true // 取消表格选择 /* 在表格选中协议方法中设置,表格点击变色后恢复原来颜色,设置后无法实现表格多选 */ tableView.deselectRowAtIndexPath(indexPath, animated: true)
- 重载表格视图
// 重载表格视图 /* 重走所有的表格视图方法,刷新所有的表格 */ tableView.reloadData() // 重载某一分段 tableView.reloadSections(NSIndexSet(index: indexPath.section), withRowAnimation: .Automatic) // 重载某一个行 tableView.reloadRowsAtIndexPaths(Array(arrayLiteral: indexPath), withRowAnimation: .Automatic) // 删除一个 cell /* 只刷新删除的 cell */ tableView.deleteRowsAtIndexPaths(NSArray(object: indexPath) as! [NSIndexPath], withRowAnimation: .Automatic) // 插入一个 cell /* 只刷新插入的 cell */ tableView.insertRowsAtIndexPaths(NSArray(object: indexPath) as! [NSIndexPath], withRowAnimation: .Automatic)
- 设置分段的头标题和脚标题
3、Cell 的创建(系统类型 Cell)
- 使用 dequeueReuseableCellWithIdentifier: 可不注册,但是必须对获取回来的 cell 进行判断是否为空,若空则手动创建新的 cell;
- 使用 dequeueReuseableCellWithIdentifier: forIndexPath: 必须注册,但返回的 cell 可省略空值判断的步骤。
- tableViewCell 的复用机制:
- 1、当一个 cell 滑出屏幕的时候,会被放到复用队列里(系统自动操作)。
- 2、当一个 cell 即将出现的时候,我们需要先从复用队列里查找,找到就直接用,找不到就创建。
- 系统 Cell 的创建方式:
- 代码创建 cell。
- 注册 cell。
- 从 storyboard 加载 cell。
3.1 创建 Cell
- 可以设置创建的 Cell 的类型。
- Objective-C
// 设置每一行显示的内容 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { // 创建标识词,标识词随意设置,但不能和其它 tableView 的相同 static NSString *resumeID = @"testIdentifier"; // 根据标识词先从复用队列里查找 UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:resumeID]; // 复用队列中没有时再创建 if (cell == nil) { // 创建新的 cell,默认为主标题模式 cell = [[UITableViewCell alloc] initWithStyle: UITableViewCellStyleDefault reuseIdentifier:resumeID]; } // 设置每一行显示的文字内容,覆盖数据 cell.textLabel.text = [[myDataArray objectAtIndex:indexPath.section] objectAtIndex:indexPath.row]; cell.imageView.image = [UIImage imageNamed:@"HQ_0003"]; return cell; }
- Swift
// 设置每一行显示的内容 func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { // 创建标识词,标识词随意设置,但不能和其它 tableView 的相同 let resumeID = "testIdentifier" // 根据标识词先从复用队列里查找 var cell = tableView.dequeueReusableCellWithIdentifier(resumeID) // 复用队列中没有时再创建 if cell == nil { // 创建新的 cell,默认为主标题模式 cell = UITableViewCell(style: .Default, reuseIdentifier: resumeID) } // 设置每一行显示的文字内容,覆盖数据 cell!.textLabel?.text = myDataArray[indexPath.section][indexPath.row] cell!.imageView?.image = UIImage(named: "HQ_0003") return cell! }
3.2 注册 Cell
- 在 tableView 创建时,从 iOS7 开始多了一种创建 cell 的方式(注册),让 tableView 注册一种 cell,需要设置复用标志。
- 创建的 Cell 为 UITableViewCellStyleDefault 默认类型,无法修改。
- Objective-C
// 定义重用标识,定义为全局变量 NSString *resumeID = @"testIdentifier"; // 注册 cell - (void)viewDidLoad { [super viewDidLoad]; // 注册某个标识对应的 cell 类型 [myTableView registerClass:[UITableViewCell class] forCellReuseIdentifier:resumeID]; } // 设置每一行显示的内容 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { // 根据标识词先从复用队列里查找,复用队列中没有时根据注册的 cell 自动创建 UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:resumeID forIndexPath:indexPath]; // 设置每一行显示的文字内容,覆盖数据 cell.textLabel.text = [[myDataArray objectAtIndex:indexPath.section] objectAtIndex:indexPath.row]; cell.imageView.image = [UIImage imageNamed:@"HQ_0003"]; return cell; }
- Swift
// 定义重用标识,定义为全局变量 let resumeID = "testIdentifier" // 注册 cell override func viewDidLoad() { super.viewDidLoad() // 注册某个标识对应的 cell 类型 myTableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: resumeID) } // 设置每一行显示的内容 func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { // 根据标识词先从复用队列里查找,复用队列中没有时根据注册的 cell 自动创建 var cell = tableView.dequeueReusableCellWithIdentifier(resumeID, forIndexPath: indexPath) // 设置每一行显示的文字内容,覆盖数据 cell!.textLabel?.text = myDataArray[indexPath.section][indexPath.row] cell!.imageView?.image = UIImage(named: "HQ_0003") return cell! }
3.3 StoryBoard 加载 Cell
- 在 storyboard 中设置 UITableView 的 Dynamic Prototypes Cell。
- 设置 cell 的重用标识。
- 在代码中利用重用标识获取 cell。
- Objective-C
// 设置每一行显示的内容 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { // 创建标识词,标识词随意设置,但不能和其它 tableView 的相同 static NSString *resumeID = @"cell"; // 根据标志词从先复用队列里查找,复用队列中没有时根据 storyboard 自动创建 UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:resumeID forIndexPath:indexPath]; // 设置每一行显示的文字内容,覆盖数据 cell.textLabel.text = [[myDataArray objectAtIndex:indexPath.section] objectAtIndex:indexPath.row]; cell.imageView.image = [UIImage imageNamed:@"HQ_0003"]; return cell; }
- Swift
// 设置每一行显示的内容 func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { // 创建标识词,标识词随意设置,但不能和其它 tableView 的相同 let resumeID = "cell" // 根据标志词从先复用队列里查找,复用队列中没有时根据 storyboard 自动创建 var cell = tableView.dequeueReusableCellWithIdentifier(resumeID, forIndexPath: indexPath) // 设置每一行显示的文字内容,覆盖数据 cell!.textLabel?.text = myDataArray[indexPath.section][indexPath.row] cell!.imageView?.image = UIImage(named: "HQ_0003") return cell! }
- Objective-C
4、Cell 的设置
- UITableView 的每一行都是一个 UITableViewCell,通过 dataSource的tableView:cellForRowAtIndexPath: 方法来初始化每一行。
- UITableViewCell 内部有个默认的子视图 contentView,contentView 是 UITableViewCell 所显示内容的父视图,可显示一些辅助指示视图。辅助指示视图的作用是显示一个表示动作的图标,可以通过设置 UITableViewCell 的 accessoryType 来显示,默认是 UITableViewCellAccessoryNone (不显示辅助指示视图)。
- contentView 下默认有 3 个子视图
- 其中 2 个是 UILabel (通过 UITableViewCell 的 textLabel 和 detailTextLabel 属性访问)。
- 第 3 个是 UIImageView (通过 UITableViewCell 的 imageView 属性访问)。
- UITableViewCell 还有一个 UITableViewCellStyle 属性,用于决定使用 contentView 的哪些子视图,以及这些子视图在 contentView 中的位置。
- UITableViewCell 结构
- Objective-C
- 设置 Cell 的类型
/* UITableViewCellStyleDefault, // 可选图片 + 主标题模式,默认 UITableViewCellStyleValue1, // 可选图片 + 左右主副标题模式,两端对齐 UITableViewCellStyleValue2, // 左右主副标题模式,中间对齐 UITableViewCellStyleSubtitle // 可选图片 + 上下主副标题模式 */ // 主标题模式,默认类型 cell = [[UITableViewCell alloc] init]; // 设置类型 cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"testIdentifier"];
- 设置 Cell 的显示内容
// 主标题模式 // 设置主标题内容 cell.textLabel.text = [[myDataArray objectAtIndex:indexPath.section] objectAtIndex:indexPath.row]; // 设置图片内容,图片在 Cell 的左端,图片大小自动压缩 cell.imageView.image = [UIImage imageNamed:@"HQ_0003"]; // 主副标题模式 // 设置主标题内容 cell.textLabel.text = [[myDataArray objectAtIndex:indexPath.section] objectAtIndex:indexPath.row]; // 设置副标题内容 cell.detailTextLabel.text = [NSString stringWithFormat:@"第 %li 行", indexPath.row]; // 设置图片内容,图片在 Cell 的左端,图片大小自动压缩 cell.imageView.image = [UIImage imageNamed:@"HQ_0003"];
- 往 cell 上添加自定义 view
- 不是直接添加在 cell 上,cell 给我们提供了一个专门用来添加子 view 的东西,当 cell 被复用的时候,不允许创建对象,如果想给系统的 cell 上添加一些子 view,需要在创建 cell 的时候添加,然后在复用的时候修改子 view 显示的内容。
// 添加 cell 自定义 view 视图 UILabel *myCellView = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 300, 44)]; myCellView.tag = 100; // 在创建新的 cell 后添加 [cell.contentView addSubview:myCellView];
// 设置 cell 自定义 view 显示内容,在 cell 复用的时候设置 UILabel *myCellView = (id)[self.view viewWithTag:100]; myCellView.text = [NSString stringWithFormat:@"自定义 Cell View %@", [[myDataArray objectAtIndex:indexPath.section] objectAtIndex:indexPath.row]];
- 设置 Cell 的背景视图
// Cell 的背景视图设置 /* 设置自定义视图为 Cell 背景视图 图片大小自动压缩填充 */ cell.backgroundView = myBackgroundView; // Cell 选中时的背景视图设置 cell.selectedBackgroundView = myBackgroundView;
- 设置 Cell 的颜色
// Cell 背景颜色的设置 cell.backgroundColor = [UIColor yellowColor]; // 设置 cell 被点击时的颜色 /* UITableViewCellSelectionStyleNone, // 无色,表格点击时无颜色变化 UITableViewCellSelectionStyleBlue, // 灰色 UITableViewCellSelectionStyleGray, // 灰色 UITableViewCellSelectionStyleDefault // 灰色,默认 */ cell.selectionStyle = UITableViewCellSelectionStyleDefault; // 取消表格选择变色 /* 在表格选中协议方法中设置,表格点击变色后恢复原来颜色,设置后无法实现表格多选 */ [tableView deselectRowAtIndexPath:indexPath animated:YES];
- 设置 Cell 的附属控件
// Cell 附属控件类型的设置 /* 如果附属控件里有 button ,这个 button 会独立出来 UITableViewCellAccessoryNone, // 无附属控件,默认 UITableViewCellAccessoryDisclosureIndicator, // 箭头,不能点击 UITableViewCellAccessoryDetailDisclosureButton, // 详情按钮和箭头,可以点击 UITableViewCellAccessoryCheckmark, // 对号,不能点击 UITableViewCellAccessoryDetailButton // 详情按钮,可以点击 */ cell.accessoryType = UITableViewCellAccessoryCheckmark; // Cell 附属控件视图的设置 /* 设置自定义视图为 Cell 的附属控件,需设置 view 的大小 */ cell.accessoryView = myAccessoryView;
- 获取 cell
// 获取指定行的 cell UITableViewCell *cell = [tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:3 inSection:0]]; // 获取所有被选中的行 NSArray *indexPaths = [tableView indexPathsForSelectedRows];
- 设置 Cell 的类型
- Swift
- 设置 Cell 的类型
/* case Default // 可选图片 + 主标题模式 case Value1 // 可选图片 + 左右主副标题模式,两端对齐 case Value2 // 左右主副标题模式,中间对齐 case Subtitle // 可选图片 + 上下主副标题模式 */ // 主标题模式,默认类型 cell = UITableViewCell() // 设置类型 cell = UITableViewCell(style: .Subtitle, reuseIdentifier: "testIdentifier")
- 设置 Cell 的显示内容
// 主标题模式 // 设置主标题内容 cell!.textLabel?.text = myDataArray[indexPath.section][indexPath.row] // 设置图片内容,图片在 Cell 的左端,图片大小自动压缩 cell!.imageView?.image = UIImage(named: "HQ_0003") // 主副标题模式 // 设置主标题内容 cell!.textLabel?.text = myDataArray[indexPath.section][indexPath.row] // 设置副标题内容 cell!.detailTextLabel?.text = "第 \(indexPath.row) 行" // 设置图片内容,图片在 Cell 的左端,图片大小自动压缩 cell!.imageView?.image = UIImage(named: "HQ_0003")
- 往 cell 上添加自定义 view
- 不是直接添加在 cell 上,cell 给我们提供了一个专门用来添加子 view 的东西,当 cell 被复用的时候,不允许创建对象,如果想给系统的 cell 上添加一些子 view,需要在创建 cell 的时候添加,然后在复用的时候修改子 view 显示的内容。
// 添加 cell 自定义 view 视图 let myCellView:UILabel = UILabel(frame: CGRectMake(0, 0, 300, 44)) myCellView.tag = 100 // 在创建新的 cell 后添加 cell!.contentView.addSubview(myCellView) // 设置 cell 自定义 view 显示内容,在 cell 复用的时候设置 if (self.view.viewWithTag(100) != nil) { let myCellView = self.view.viewWithTag(100) as! UILabel myCellView.text = String("自定义 Cell View \(myDataArray[indexPath.section][indexPath.row])") }
- 设置 Cell 的背景视图
// Cell 的背景视图设置 /* 设置自定义视图为 Cell 背景视图 图片大小自动压缩填充 */ cell!.backgroundView = myBackgroundView // Cell 选中时的背景视图设置 cell!.selectedBackgroundView = myBackgroundView
- 设置 Cell 的颜色
// Cell 背景颜色的设置 cell!.backgroundColor = UIColor.yellowColor() // 设置 cell 被点击时的颜色 /* case None // 无色,表格点击时无颜色变化 case Blue // 灰色 case Gray // 灰色 case Default // 灰色,默认 */ cell!.selectionStyle = UITableViewCellSelectionStyle.Default // 取消表格选择变色 /* 在表格选中协议方法中设置,表格点击变色后恢复原来颜色,设置后无法实现表格多选 */ tableView.deselectRowAtIndexPath(indexPath, animated: true)
- 设置 Cell 的附属控件
// Cell 附属控件类型的设置 /* 如果附属控件里有 button ,这个 button 会独立出来 case None // 无附属控件,默认 case DisclosureIndicator // 箭头,不能点击 case DetailDisclosureButton // 详情按钮和箭头,可以点击 case Checkmark // 对号,不能点击 case DetailButton // 详情按钮,可以点击 */ cell!.accessoryType = UITableViewCellAccessoryType.DetailButton // Cell 附属控件视图的设置 /* 设置自定义视图为 Cell 的附属控件,需设置 view 的大小 */ cell!.accessoryView = myAccessoryView
- 获取 cell
// 获取指定行的 cell let cell:UITableViewCell = tableView.cellForRowAtIndexPath(NSIndexPath(forItem: 3, inSection: 0))! // 获取所有被选中的行 let indexPaths:[NSIndexPath] = tableView.indexPathsForVisibleRows!
- 设置 Cell 的类型
5、自定义数据模型的创建与引用
- Objective-C
- BookModel.h
@interface BookModel : NSObject // 根据需要使用的数据创建数据模型属性变量 @property(nonatomic, copy)NSString *title; @property(nonatomic, copy)NSString *detail; @property(nonatomic, copy)NSString *icon; @property(nonatomic, copy)NSString *price; + (instancetype)bookModelWithDict:(NSDictionary *)dict; @end
- BookModel.m
+ (instancetype)bookModelWithDict:(NSDictionary *)dict { BookModel *model = [[self alloc] init]; // KVC - Key Value Coding [model setValuesForKeysWithDictionary:dict]; return model; }
- ViewController.m
// 向数据源中添加数据 // 定义数据源 @property(nonatomic, retain)NSArray *myDataArray; // 懒加载 - (NSArray *)myDataArray { if (_myDataArray == nil) { // 加载 plist 中的字典数组 NSString *filePath = [[NSBundle mainBundle] pathForResource:@"bookData" ofType:@"plist"]; NSArray *bookDataArray = [NSArray arrayWithContentsOfFile:filePath]; // 字典数组 -> 模型数组 NSMutableArray *dataArrayM = [NSMutableArray arrayWithCapacity:bookDataArray.count]; for (NSDictionary *bookInfoDic in bookDataArray) { BookModel *bookModel = [BookModel bookModelWithDict:bookInfoDic]; [dataArrayM addObject:bookModel]; } // 将从文件中取出的数据添加到数据源数组中 _myDataArray = [dataArrayM copy]; } return _myDataArray; } // 从数据源中取出数据 // UITableViewDataSource 协议方法 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { // 从数据源数组中取出数据 BookModel *bookModel = [self.myDataArray objectAtIndex:indexPath.row]; // 配置自定义 Cell 子视图上显示的内容 cell.book = bookModel; }
- BookModel.h
- Swift
- BookModel.swift
class BookModel: NSObject { // 根据需要使用的数据创建数据模型属性变量 var title:String? var detail:String? var icon:String? var price:String? class func bookModelWithDict(dict:[String : AnyObject]) -> AnyObject { let model = BookModel() // KVC - Key Value Coding model.setValuesForKeysWithDictionary(dict) return model } }
- ViewController.swift
// 向数据源中添加数据 // 定义数据源,懒加载 lazy var myDataArray:[BookModel] = { // 加载 plist 中的字典数组 let filePath:String = NSBundle.mainBundle().pathForResource("bookData", ofType: "plist")! let bookDataArray:NSArray = NSArray(contentsOfFile: filePath)! // 字典数组 -> 模型数组 var dataArrayM:NSMutableArray = NSMutableArray(capacity: bookDataArray.count) for bookInfoDic in bookDataArray { let bookModel:BookModel = BookModel.bookModelWithDict(bookInfoDic as! NSDictionary) dataArrayM.addObject(bookModel) } // 将从文件中取出的数据添加到数据源数组中 return dataArrayM.copy() as! [BookModel] }() // 从数据源中取出数据 // UITableViewDataSource 协议方法 func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { // 从数据源数组中取出数据 let bookModel:BookModel = self.myDataArray[indexPath.row] // 配置自定义 Cell 子视图上显示的内容 cell!.configWithModel(bookModel) }
- BookModel.swift
6、自定义等高 Cell
6.1 StoryBoard 自定义 cell
- 创建一个继承自 UITableViewCell 的子类,比如 XMGDealCell。
- 在 storyboard 中
- 往 cell 里面增加需要用到的子控件。
- 设置 cell 的重用标识 。
- 设置 cell 的 class 为 XMGDealCell。
- 在 XMGDealCell 中
- 将 storyboard 中的子控件连线到类扩展中。
- 需要提供一个模型属性,重写模型的 set 方法,在这个方法中设置模型数据到子控件上。
- 在控制器中
- 利用重用标识找到 cell。
- 给 cell 传递模型数据。
- Objective-C
- XMGDeal.h
@interface XMGDeal : NSObject @property (strong, nonatomic) NSString *buyCount; @property (strong, nonatomic) NSString *price; @property (strong, nonatomic) NSString *title; @property (strong, nonatomic) NSString *icon; + (instancetype)dealWithDict:(NSDictionary *)dict; @end
- XMGDeal.m
@implementation XMGDeal + (instancetype)dealWithDict:(NSDictionary *)dict { XMGDeal *deal = [[self alloc] init]; [deal setValuesForKeysWithDictionary:dict]; return deal; } @end
- XMGDealCell.h
@class XMGDeal; @interface XMGDealCell : UITableViewCell /** 模型数据 */ @property (nonatomic, strong) XMGDeal *deal; @end
- XMGDealCell.m
@interface XMGDealCell() @property (weak, nonatomic) IBOutlet UIImageView *iconView; @property (weak, nonatomic) IBOutlet UILabel *titleLabel; @property (weak, nonatomic) IBOutlet UILabel *priceLabel; @property (weak, nonatomic) IBOutlet UILabel *buyCountLabel; @end @implementation XMGDealCell - (void)setDeal:(XMGDeal *)deal { _deal = deal; // 设置数据 self.iconView.image = [UIImage imageNamed:deal.icon]; self.titleLabel.text = deal.title; self.priceLabel.text = [NSString stringWithFormat:@"¥%@", deal.price]; self.buyCountLabel.text = [NSString stringWithFormat:@"%@人已购买", deal.buyCount]; } @end
- XMGDealsViewController.m
@interface XMGDealsViewController () /** 所有的团购数据 */ @property (nonatomic, strong) NSArray *deals; @end @implementation XMGDealsViewController - (NSArray *)deals { if (_deals == nil) { // 加载plist中的字典数组 NSString *path = [[NSBundle mainBundle] pathForResource:@"deals.plist" ofType:nil]; NSArray *dictArray = [NSArray arrayWithContentsOfFile:path]; // 字典数组 -> 模型数组 NSMutableArray *dealArray = [NSMutableArray array]; for (NSDictionary *dict in dictArray) { XMGDeal *deal = [XMGDeal dealWithDict:dict]; [dealArray addObject:deal]; } _deals = dealArray; } return _deals; } #pragma mark - Table view data source - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return self.deals.count; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *ID = @"deal"; XMGDealCell *cell = [tableView dequeueReusableCellWithIdentifier:ID]; // 取出模型数据 cell.deal = self.deals[indexPath.row]; return cell; } @end
- XMGDeal.h
6.2 xib 自定义 cell
- 创建一个继承自 UITableViewCell 的子类,比如 BookCell2。
- 创建一个 xib 文件(文件名建议跟 cell 的类名一样),比如 BookCell2.xib。
- 拖拽一个 UITableViewCell 出来。
- 修改 cell 的 class 为 BookCell2。
- 设置 cell 的重用标识。
- 往 cell 中添加需要用到的子控件。
- 在 BookCell2 中
- 将 xib 中的子控件连线到类扩展中。
- 需要提供一个模型属性,重写模型的 set 方法,在这个方法中设置模型数据到子控件上。
- 也可以将创建获得 cell 的代码封装起来(比如 cellWithTableView: 方法)。
- 在控制器中
- 手动加载 xib 文件,或者利用 registerNib... 方法注册 xib 文件。
- 利用重用标识找到 cell。
- 给 cell 传递模型数据。
6.2.1 xib 创建 cell
- 在 xib 文件中必须设置 Identifier 属性,否则 cell 不会被复用,会一直创建新的 cell,占用大量的内存。
- Objective-C
- BookCell2.xib
- xib 的 Identifier 属性设置为 Book2ID
- BookCell2.h
@class BookModel; @interface BookCell2 : UITableViewCell // 定义 Cell 的数据模型 @property (nonatomic, retain)BookModel *book; @end
- BookCell2.m
#import "BookCell2.h" #import "BookModel.h" @interface BookCell2 () // 创建自定义 Cell 视图包含的内容 // 按住 control 键拖动 或右键拖动过来生成 @property (weak, nonatomic) IBOutlet UIImageView *iconView; @property (weak, nonatomic) IBOutlet UILabel *titleLabel; @property (weak, nonatomic) IBOutlet UILabel *detailLabel; @property (weak, nonatomic) IBOutlet UILabel *priceLabel; @end @implementation BookCell2 // 设置显示的数据 - (void)setBook:(BookModel *)book { _book = book; // 设置数据,设置 cell 视图上显示的内容 内容 self.iconView.image = [UIImage imageNamed:book.icon]; self.titleLabel.text = book.title; self.detailLabel.text = book.detail; self.priceLabel.text = book.price; } @end
- ViewController.m
// 使用 xib 定义的 Cell 创建,UITableViewDataSource 协议方法 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { // 使用 xib 定义的 Cell 定义 BookCell2 *cell = [tableView dequeueReusableCellWithIdentifier:@"Book2ID"]; if (cell == nil) { // 通过 xib 文件创建新的 cell cell = [[[NSBundle mainBundle] loadNibNamed:@"BookCell2" owner:self options: nil] lastObject]; } BookModel *bookModel = [self.myDataArray objectAtIndex:indexPath.row]; cell.book = bookModel; return cell; }
- BookCell2.xib
- Swift
- BookCell2.xib
- xib 的 Identifier 属性设置为 Book2ID 。
- BookCell2.swift
class BookCell2: UITableViewCell { // 创建自定义 Cell 视图包含的内容 @IBOutlet weak var iconView: UIImageView! @IBOutlet weak var titleLabel: UILabel! @IBOutlet weak var detailLabel: UILabel! @IBOutlet weak var priceLabel: UILabel! // 设置显示的数据 func configWithModel(bookModel:BookModel){ // 设置数据,设置 cell 视图上显示的内容 内容 iconView!.image = UIImage(named: bookModel.icon!) titleLabel!.text = bookModel.title detailLabel!.text = bookModel.detail priceLabel!.text = bookModel.price } }
- ViewController.swift
// 使用 xib 定义的 Cell 创建,UITableViewDataSource 协议方法 func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { // 使用 xib 定义的 Cell 定义 var cell:BookCell2? = tableView.dequeueReusableCellWithIdentifier("BookCell2") as? BookCell2 if cell == nil { // 通过 xib 文件创建新的 cell cell = NSBundle.mainBundle().loadNibNamed("BookCell2", owner: self, options: nil).last as? BookCell2 } let bookModel:BookModel = self.myDataArray[indexPath.row] cell!.configWithModel(bookModel) return cell! }
- BookCell2.xib
6.2.2 xib 注册 cell
- 如果 cell 使用 xib 注册的方式,xib 中可以不指定复用标识 Identifier 属性,如果 xib 中指定了,那么所有地方都要同步。但无论如何代码中必须设置 Identifier 属性。
- 在 tableView 创建时,从 iOS7 开始多了一种创建 cell 的方式(注册),让 tableView 注册一种 cell,需要设置复用标志。
- 用注册方式创建 cell,如果 tableView 已经注册了某一种 cell,从复用队列里查找,如果找不到,系统会自动通过注册的 cell 类来创建 cell 对象。
- Objective-C
- BookCell2.xib
- BookCell2.h
- BookCell2.m
- xib 自定义 Cell 部分同上。
- ViewController.m
// 注册 cell [myTableView registerNib:[UINib nibWithNibName:NSStringFromClass([BookCell2 class]) bundle:nil] forCellReuseIdentifier:@"Book2ID"]; // 使用注册的 xib cell 创建,UITableViewDataSource 协议方法 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { // 使用 xib 定义的 Cell 定义 BookCell2 *cell = [tableView dequeueReusableCellWithIdentifier:@"Book2ID" forIndexPath:indexPath]; BookModel *bookModel = [self.myDataArray objectAtIndex:indexPath.row]; cell.book = bookModel; return cell; }
- Swift
- BookCell2.xib
- BookCell2.swift
- xib 自定义 Cell 部分同上。
- ViewController.m
// 注册 cell myTableView.registerNib(UINib(nibName: "BookCell2", bundle: nil), forCellReuseIdentifier: "Book2ID") // 使用注册的 xib cell 创建,UITableViewDataSource 协议方法 func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { // 使用 xib 定义的 Cell 定义 let cell:BookCell2? = tableView.dequeueReusableCellWithIdentifier("Book2ID", forIndexPath: indexPath) as? BookCell2 let bookModel:BookModel = self.myDataArray[indexPath.row] cell!.configWithModel(bookModel) return cell! }
6.3 代码自定义 Cell
- 代码自定义 cell(使用 frame)
- 创建一个继承自 UITableViewCell 的子类,比如 BookCell1。
- 在 initWithStyle:reuseIdentifier: 方法中。
- 添加子控件。
- 设置子控件的初始化属性(比如文字颜色、字体)。
- 在 layoutSubviews 方法中设置子控件的 frame。
- 需要提供一个模型属性,重写模型的 set 方法,在这个方法中设置模型数据到子控件。
- 在 initWithStyle:reuseIdentifier: 方法中。
- 在控制器中
- 利用 registerClass... 方法注册 BookCell1 类。
- 利用重用标识找到 cell(如果没有注册类,就需要手动创建 cell)。
- 给 cell 传递模型数据。
- 也可以将创建获得 cell 的代码封装起来(比如 cellWithTableView: 方法)。
- 创建一个继承自 UITableViewCell 的子类,比如 BookCell1。
- 代码自定义 cell(使用 autolayout)
- 创建一个继承自 UITableViewCell 的子类,比如 BookCell1。
- 在 initWithStyle:reuseIdentifier: 方法中。
- 添加子控件。
- 添加子控件的约束(建议使用 Masonry)。
- 设置子控件的初始化属性(比如文字颜色、字体)。
- 需要提供一个模型属性,重写模型的 set 方法,在这个方法中设置模型数据到子控件。
- 在 initWithStyle:reuseIdentifier: 方法中。
- 在控制器中
- 利用 registerClass... 方法注册 BookCell1 类。
- 利用重用标识找到 cell(如果没有注册类,就需要手动创建 cell)。
- 给 cell 传递模型数据。
- 也可以将创建获得 cell 的代码封装起来(比如 cellWithTableView: 方法)。
- 创建一个继承自 UITableViewCell 的子类,比如 BookCell1。
6.3.1 代码创建 cell - frame
- Objective-C
- BookCell1.h
@class BookModel; @interface BookCell1 : UITableViewCell // 定义 Cell 的数据模型 @property (nonatomic, retain)BookModel *book; @end
- BookCell1.m
#import "BookCell1.h" #import "BookModel.h" @interface BookCell1() // 创建自定义 Cell 视图包含的内容 @property(nonatomic, retain)UIImageView *iconView; @property(nonatomic, retain)UILabel *titleLabel; @property(nonatomic, retain)UILabel *detailLabel; @property(nonatomic, retain)UILabel *priceLabel; @end @implementation BookCell1 // 重写初 Cell 始化方法,创建自定义 Cell - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) { // 创建子视图 // 创建 iconView 视图,并添加到自定义 Cell 上 self.iconView = [[UIImageView alloc] init]; self.iconView.layer.borderColor = [[UIColor greenColor] CGColor]; self.iconView.layer.borderWidth = 2; [self.contentView addSubview:self.iconView]; // 创建 titleLabel 视图 self.titleLabel = [[UILabel alloc] init]; self.titleLabel.font = [UIFont boldSystemFontOfSize:14]; self.titleLabel.textColor = [UIColor redColor]; [self.contentView addSubview:self.titleLabel]; // 创建 detailLabel 视图 self.detailLabel = [[UILabel alloc] init]; self.detailLabel.font = [UIFont systemFontOfSize:12]; [self.contentView addSubview:self.detailLabel]; // 创建 priceLabel 视图 self.priceLabel = [[UILabel alloc] init]; self.priceLabel.font = [UIFont systemFontOfSize:12]; [self.contentView addSubview:self.priceLabel]; } return self; } // 布局子视图 - (void)layoutSubviews { [super layoutSubviews]; // 布局子视图 self.iconView.frame = CGRectMake(10, 10, 60, 60); self.titleLabel.frame = CGRectMake(90, 5, 200, 25); self.detailLabel.frame = CGRectMake(90, 30, 200, 25); self.priceLabel.frame = CGRectMake(90, 55, 200, 25); } // 设置显示的数据 - (void)setBook:(BookModel *)book { _book = book; // 设置数据,设置 cell 视图上显示的内容 内容 self.iconView.image = [UIImage imageNamed:book.icon]; self.titleLabel.text = book.title; self.detailLabel.text = book.detail; self.priceLabel.text = book.price; } @end
- ViewController.m
// 使用自定义 Cell 创建,UITableViewDataSource 协议方法 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { // 使用自定义的 Cell 定义 BookCell1 *cell = [tableView dequeueReusableCellWithIdentifier:@"testIdentifier"]; if (cell == nil) { // 使用自定义的 Cell 创建 cell = [[BookCell1 alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"testIdentifier"]; } BookModel *bookModel = [self.myDataArray objectAtIndex:indexPath.row]; cell.book = bookModel; return cell; }
- BookCell1.h
- Swift
- BookCell1.swift
class BookCell1: UITableViewCell { // 创建自定义 Cell 视图包含的内容 var iconView:UIImageView? var titleLabel:UILabel? var detailLabel:UILabel? var priceLabel:UILabel? // 重写初 Cell 始化方法,创建自定义 Cell override init(style: UITableViewCellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) // 创建子视图 // 创建 _iconView 视图,并添加到自定义 Cell 上 iconView = UIImageView() iconView!.layer.borderColor = UIColor.greenColor().CGColor iconView!.layer.borderWidth = 2 self.contentView.addSubview(iconView!) // 创建 _titleLabel 视图 titleLabel = UILabel() titleLabel!.font = UIFont.boldSystemFontOfSize(14) titleLabel!.textColor = UIColor.redColor() self.contentView.addSubview(titleLabel!) // 创建 _detailLabel 视图 detailLabel = UILabel() detailLabel!.font = UIFont.systemFontOfSize(12) self.contentView.addSubview(detailLabel!) // 创建 _priceLabel 视图 priceLabel = UILabel() priceLabel!.font = UIFont.systemFontOfSize(12) self.contentView.addSubview(priceLabel!) } // 布局子视图 override func layoutSubviews() { super.layoutSubviews() // 布局子视图 iconView?.frame = CGRectMake(10, 10, 60, 60) titleLabel?.frame = CGRectMake(90, 5, 200, 25) detailLabel?.frame = CGRectMake(90, 30, 200, 25) priceLabel?.frame = CGRectMake(90, 55, 200, 25) } // 设置显示的数据 func configWithModel(bookModel:BookModel){ // 设置数据,设置 cell 视图上显示的内容 内容 iconView?.image = UIImage(named: bookModel.icon!) titleLabel?.text = bookModel.title detailLabel?.text = bookModel.detail priceLabel?.text = bookModel.price } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } }
- ViewController.swift
// 使用自定义 Cell 创建,UITableViewDataSource 协议方法 func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { // 使用自定义的 Cell 定义 var cell:BookCell1? = tableView.dequeueReusableCellWithIdentifier("testIdentifier") as? BookCell1 if cell == nil { // 使用自定义的 Cell 创建 cell = BookCell1(style: UITableViewCellStyle.Default, reuseIdentifier: "testIdentifier") } let bookModel:BookModel = self.myDataArray[indexPath.row] cell!.configWithModel(bookModel) return cell! }
- BookCell1.swift
6.3.2 代码注册 cell - frame
- 在 tableView 创建时,从 iOS7 开始多了一种创建 cell 的方式(注册),让 tableView 注册一种 cell,需要设置复用标志。
- 用注册方式创建 cell,如果 tableView 已经注册了某一种 cell,从复用队列里查找,如果找不到,系统会自动通过注册的 cell 类来创建 cell 对象。
- Objective-C
- BookCell1.h
- BookCell1.m
- 自定义 Cell 部分同上。
- ViewController.m
// 注册 cell [myTableView registerClass:[BookCell1 class] forCellReuseIdentifier:@"testIdentifier"]; // 使用注册的 Cell 创建,UITableViewDataSource 协议方法 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { BookCell1 *cell = [tableView dequeueReusableCellWithIdentifier:@"testIdentifier" forIndexPath:indexPath]; BookModel *bookModel = [self.myDataArray objectAtIndex:indexPath.row]; cell.book = bookModel; return cell; }
- Swift
- BookCell1.swift
- 自定义 Cell 部分同上。
- ViewController.swift
// 注册 cell myTableView.registerClass(BookCell1.self, forCellReuseIdentifier: "testIdentifier") // 使用注册的 Cell 创建,UITableViewDataSource 协议方法 func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let cell:BookCell1? = tableView.dequeueReusableCellWithIdentifier("testIdentifier", forIndexPath: indexPath) as? BookCell1 let bookModel:BookModel = self.myDataArray[indexPath.row] cell!.configWithModel(bookModel) return cell! }
- BookCell1.swift
6.3.3 代码创建 cell - autolayout
- 第三方框架 Masonry Github 网址:https://github.com/SnapKit/Masonry
- StoryBoard
- XMGDeal.h
@interface XMGDeal : NSObject @property (strong, nonatomic) NSString *buyCount; @property (strong, nonatomic) NSString *price; @property (strong, nonatomic) NSString *title; @property (strong, nonatomic) NSString *icon; + (instancetype)dealWithDict:(NSDictionary *)dict; @end
- XMGDeal.m
@implementation XMGDeal + (instancetype)dealWithDict:(NSDictionary *)dict { XMGDeal *deal = [[self alloc] init]; // KVC - Key Value Coding [deal setValuesForKeysWithDictionary:dict]; return deal; } @end
- XMGDealCell.h
@class XMGDeal; @interface XMGDealCell : UITableViewCell /** 模型数据 */ @property (nonatomic, strong) XMGDeal *deal; + (instancetype)cellWithTableView:(UITableView *)tableView; @end
- XMGDealCell.m
#define MAS_SHORTHAND #define MAS_SHORTHAND_GLOBALS #import "Masonry.h" @interface XMGDealCell() @property (weak, nonatomic) UIImageView *iconView; @property (weak, nonatomic) UILabel *titleLabel; @property (weak, nonatomic) UILabel *priceLabel; @property (weak, nonatomic) UILabel *buyCountLabel; @end @implementation XMGDealCell + (instancetype)cellWithTableView:(UITableView *)tableView { static NSString *ID = @"deal"; // 创建cell XMGDealCell *cell = [tableView dequeueReusableCellWithIdentifier:ID]; if (cell == nil) { cell = [[XMGDealCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID]; } return cell; } // 1.在 initWithStyle:reuseIdentifier: 方法中添加子控件 - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) { UIImageView *iconView = [[UIImageView alloc] init]; [self.contentView addSubview:iconView]; self.iconView = iconView; UILabel *titleLabel = [[UILabel alloc] init]; [self.contentView addSubview:titleLabel]; self.titleLabel = titleLabel; UILabel *priceLabel = [[UILabel alloc] init]; priceLabel.textColor = [UIColor orangeColor]; [self.contentView addSubview:priceLabel]; self.priceLabel = priceLabel; UILabel *buyCountLabel = [[UILabel alloc] init]; buyCountLabel.textAlignment = NSTextAlignmentRight; buyCountLabel.font = [UIFont systemFontOfSize:14]; buyCountLabel.textColor = [UIColor lightGrayColor]; [self.contentView addSubview:buyCountLabel]; self.buyCountLabel = buyCountLabel; } return self; } // 2.在 layoutSubviews 方法中设置子控件的 约束 - (void)layoutSubviews { [super layoutSubviews]; CGFloat margin = 10; [self.iconView makeConstraints:^(MASConstraintMaker *make) { make.width.equalTo(100); make.left.top.offset(margin); make.bottom.offset(-margin); }]; [self.titleLabel makeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(self.iconView); make.left.equalTo(self.iconView.right).offset(margin); make.right.offset(-margin); }]; [self.priceLabel makeConstraints:^(MASConstraintMaker *make) { make.left.equalTo(self.titleLabel); make.bottom.equalTo(self.iconView); make.width.equalTo(70); }]; [self.buyCountLabel makeConstraints:^(MASConstraintMaker *make) { make.bottom.equalTo(self.priceLabel); make.right.equalTo(self.titleLabel); make.left.equalTo(self.priceLabel.right).offset(margin); }]; } // 3.重写模型的 set 方法 - (void)setDeal:(XMGDeal *)deal { _deal = deal; // 设置数据 self.iconView.image = [UIImage imageNamed:deal.icon]; self.titleLabel.text = deal.title; self.priceLabel.text = [NSString stringWithFormat:@"¥%@", deal.price]; self.buyCountLabel.text = [NSString stringWithFormat:@"%@人已购买", deal.buyCount]; } @end
- XMGDealsViewController.m
@interface XMGDealsViewController () /** 所有的团购数据 */ @property (nonatomic, strong) NSArray *deals; @end @implementation XMGDealsViewController - (NSArray *)deals { if (_deals == nil) { // 加载plist中的字典数组 NSString *path = [[NSBundle mainBundle] pathForResource:@"deals.plist" ofType:nil]; NSArray *dictArray = [NSArray arrayWithContentsOfFile:path]; // 字典数组 -> 模型数组 NSMutableArray *dealArray = [NSMutableArray array]; for (NSDictionary *dict in dictArray) { XMGDeal *deal = [XMGDeal dealWithDict:dict]; [dealArray addObject:deal]; } _deals = dealArray; } return _deals; } - (void)viewDidLoad { [super viewDidLoad]; // [self.tableView registerClass:[XMGDealCell class] forCellReuseIdentifier:@"deal"]; } #pragma mark - Table view data source - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return self.deals.count; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { //创建cell XMGDealCell *cell = [XMGDealCell cellWithTableView:tableView]; // 取出模型数据 cell.deal = self.deals[indexPath.row]; return cell; } @end
- XMGDeal.h
7、自定义非等高 Cell
7.1 StoryBoard / Xib 自定义 cell
- 在模型中增加一个 cellHeight 属性,用来存放对应 cell 的高度。
- 在 cell 的模型属性 set 方法中调用 [self layoutIfNeed] 方法强制布局,然后计算出模型的 cellheight 属性值。
- 在控制器中实现 tableView:estimatedHeightForRowAtIndexPath: 方法,返回一个估计高度,比如 200。
- 在控制器中实现 tableView:heightForRowAtIndexPath: 方法,返回 cell 的真实高度(模型中的 cellHeight 属性)。
- 注意:StoryBoard 中 label 的约束不要设置右侧约束值,否则编译时会打印出一大堆提示信息。
- StoryBoard
- Main.storyboard
- 在 cell 上添加子控件,并设置约束。
- Main.storyboard
- Xib
- XMGStatusCell.xib
- 在 cell 上添加子控件,并设置约束。
- XMGStatusCell.xib
- Objective-C
- XMGStatus.h
@interface XMGStatus : NSObject @property (strong, nonatomic) NSString *name; @property (strong, nonatomic) NSString *text; @property (strong, nonatomic) NSString *icon; @property (strong, nonatomic) NSString *picture; @property (assign, nonatomic, getter=isVip) BOOL vip; /** cell 的高度 */ @property (assign, nonatomic) CGFloat cellHeight; + (instancetype)statusWithDict:(NSDictionary *)dict; @end
- XMGStatus.m
@implementation XMGStatus + (instancetype)statusWithDict:(NSDictionary *)dict { XMGStatus *status = [[self alloc] init]; [status setValuesForKeysWithDictionary:dict]; return status; } @end
- XMGStatusCell.h
@class XMGStatus; @interface XMGStatusCell : UITableViewCell + (instancetype)cellWithTableView:(UITableView *)tableView; /** 模型数据 */ @property (nonatomic, strong) XMGStatus *status; @end
- XMGStatusCell.m
@interface XMGStatusCell() @property (weak, nonatomic) IBOutlet UIImageView *iconView; @property (weak, nonatomic) IBOutlet UILabel *nameLabel; @property (weak, nonatomic) IBOutlet UIImageView *vipView; @property (weak, nonatomic) IBOutlet UILabel *contentLabel; @property (weak, nonatomic) IBOutlet UIImageView *pictureView; @end @implementation XMGStatusCell + (instancetype)cellWithTableView:(UITableView *)tableView { return [tableView dequeueReusableCellWithIdentifier:@"status"]; } - (void)awakeFromNib { [super awakeFromNib]; // 设置label每一行文字的最大宽度 /* 为了保证计算出来的数值 跟 真正显示出来的效果 一致 */ self.contentLabel.preferredMaxLayoutWidth = [UIScreen mainScreen].bounds.size.width - 20; } - (void)setStatus:(XMGStatus *)status { _status = status; // 设置显示的数据 self.iconView.image = [UIImage imageNamed:status.icon]; self.nameLabel.text = status.name; if (status.isVip) { self.nameLabel.textColor = [UIColor orangeColor]; self.vipView.hidden = NO; } else { self.nameLabel.textColor = [UIColor blackColor]; self.vipView.hidden = YES; } self.contentLabel.text = status.text; if (status.picture) { self.pictureView.hidden = NO; self.pictureView.image = [UIImage imageNamed:status.picture]; } else { self.pictureView.hidden = YES; } // 计算 cell 高度 // 强制布局 [self layoutIfNeeded]; // 计算 cell 的高度 if (self.pictureView.hidden) { // 没有配图 _status.cellHeight = CGRectGetMaxY(self.contentLabel.frame) + 10; } else { // 有配图 _status.cellHeight = CGRectGetMaxY(self.pictureView.frame) + 10; } } @end
- XMGStatusesViewController.m
@interface XMGStatusesViewController () @property (strong, nonatomic) NSArray *statuses; @end @implementation XMGStatusesViewController - (NSArray *)statuses { if (_statuses == nil) { // 加载plist中的字典数组 NSString *path = [[NSBundle mainBundle] pathForResource:@"statuses.plist" ofType:nil]; NSArray *dictArray = [NSArray arrayWithContentsOfFile:path]; // 字典数组 -> 模型数组 NSMutableArray *statusArray = [NSMutableArray array]; for (NSDictionary *dict in dictArray) { XMGStatus *status = [XMGStatus statusWithDict:dict]; [statusArray addObject:status]; } _statuses = statusArray; } return _statuses; } #pragma mark - Table view data source - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return self.statuses.count; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { XMGStatusCell *cell = [XMGStatusCell cellWithTableView:tableView]; cell.status = self.statuses[indexPath.row]; return cell; } #pragma mark - 代理方法 // 返回每一行的高度 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { XMGStatus *staus = self.statuses[indexPath.row]; return staus.cellHeight; } /** * 返回每一行的估计高度 * 只要返回了估计高度,那么就会先调用 tableView:cellForRowAtIndexPath: 方法创建 cell, * 再调用 tableView:heightForRowAtIndexPath: 方法获取 cell 的真实高度 */ - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath { return 200; } @end
- XMGStatus.h
- 运行效果
7.2 代码自定义 cell
7.2.1 代码自定义(frame)
- 新建一个继承自 UITableViewCell 的类。
- 重写 initWithStyle:reuseIdentifier: 方法。
- 添加所有需要显示的子控件(不需要设置子控件的数据和 frame, 子控件要添加到 contentView 中)。
- 进行子控件一次性的属性设置(有些属性只需要设置一次, 比如字体\固定的图片)。
- 提供 2 个模型。
- 数据模型: 存放文字数据\图片数据。
- frame 模型: 存放数据模型\所有子控件的 frame\cell 的高度。
- cell 拥有一个 frame 模型(不要直接拥有数据模型)。
- 重写 cell frame 模型属性的 setter 方法: 在这个方法中设置子控件的显示数据和 frame。
- frame 模型数据的初始化已经采取懒加载的方式(每一个 cell 对应的 frame 模型数据只加载一次)。
7.2.2 代码自定义(Autolayout)
- 新建一个继承自 UITableViewCell 的类。
- 重写 initWithStyle:reuseIdentifier: 方法。
- 添加所有需要显示的子控件(不需要设置子控件的数据和 frame, 子控件要添加到 contentView 中)。
- 进行子控件一次性的属性设置(有些属性只需要设置一次, 比如字体\固定的图片)。
- 设置 cell 上子控件的约束。
- 在模型中增加一个 cellHeight 属性,用来存放对应 cell 的高度。
- 在 cell 的模型属性 set 方法中调用 [self layoutIfNeed] 方法强制布局,然后计算出模型的 cellheight 属性值。
- 在控制器中实现 tableView:estimatedHeightForRowAtIndexPath: 方法,返回一个估计高度,比如 200。
- 在控制器中实现 tableView:heightForRowAtIndexPath: 方法,返回 cell 的真实高度(模型中的 cellHeight 属性)。
- Objective-C
- XMGStatus.h
@interface XMGStatus : NSObject @property (strong, nonatomic) NSString *name; @property (strong, nonatomic) NSString *text; @property (strong, nonatomic) NSString *icon; @property (strong, nonatomic) NSString *picture; @property (assign, nonatomic, getter=isVip) BOOL vip; /** cell 的高度 */ @property (assign, nonatomic) CGFloat cellHeight; + (instancetype)statusWithDict:(NSDictionary *)dict; @end
- XMGStatus.m
@implementation XMGStatus + (instancetype)statusWithDict:(NSDictionary *)dict { XMGStatus *status = [[self alloc] init]; [status setValuesForKeysWithDictionary:dict]; return status; } @end
- XMGStatusCell.h
@class XMGStatus; @interface XMGStatusCell : UITableViewCell + (instancetype)cellWithTableView:(UITableView *)tableView; /** 模型数据 */ @property (nonatomic, strong) XMGStatus *status; @end
- XMGStatusCell.m
#define MAS_SHORTHAND #define MAS_SHORTHAND_GLOBALS #import "Masonry.h" @interface XMGStatusCell() @property (weak, nonatomic) UIImageView *iconView; @property (weak, nonatomic) UILabel *nameLabel; @property (weak, nonatomic) UIImageView *vipView; @property (weak, nonatomic) UILabel *contentLabel; @property (weak, nonatomic) UIImageView *pictureView; @end @implementation XMGStatusCell + (instancetype)cellWithTableView:(UITableView *)tableView { static NSString *ID = @"status"; XMGStatusCell *cell = [tableView dequeueReusableCellWithIdentifier:ID]; if (cell == nil) { cell = [[XMGStatusCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID]; } return cell; } - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) { UIImageView *iconView = [[UIImageView alloc] init]; [self.contentView addSubview:iconView]; self.iconView = iconView; UILabel *nameLabel = [[UILabel alloc] init]; [self.contentView addSubview:nameLabel]; self.nameLabel = nameLabel; UIImageView *vipView = [[UIImageView alloc] init]; [self.contentView addSubview:vipView]; self.vipView = vipView; UILabel *contentLabel = [[UILabel alloc] init]; contentLabel.numberOfLines = 0; // 设置 label 每一行文字的最大宽度 contentLabel.preferredMaxLayoutWidth = [UIScreen mainScreen].bounds.size.width - 20; [self.contentView addSubview:contentLabel]; self.contentLabel = contentLabel; UIImageView *pictureView = [[UIImageView alloc] init]; [self.contentView addSubview:pictureView]; self.pictureView = pictureView; } return self; } - (void)layoutSubviews { [super layoutSubviews]; CGFloat margin = 10; [self.iconView makeConstraints:^(MASConstraintMaker *make) { make.size.equalTo(30); make.left.top.offset(margin); }]; [self.nameLabel makeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(self.iconView); make.left.equalTo(self.iconView.right).offset(margin); }]; [self.vipView makeConstraints:^(MASConstraintMaker *make) { make.size.equalTo(14); make.left.equalTo(self.nameLabel.right).offset(margin); make.centerY.equalTo(self.nameLabel.centerY); }]; [self.contentLabel makeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(self.iconView.bottom).offset(margin); make.left.offset(margin); // make.right.offset(-margin); // 可加可不加 }]; [self.pictureView makeConstraints:^(MASConstraintMaker *make) { make.size.equalTo(100); make.top.equalTo(self.contentLabel.bottom).offset(margin); make.left.offset(margin); }]; } - (void)setStatus:(XMGStatus *)status { _status = status; // 设置显示的数据 self.iconView.image = [UIImage imageNamed:status.icon]; self.nameLabel.text = status.name; if (status.isVip) { self.nameLabel.textColor = [UIColor orangeColor]; self.vipView.hidden = NO; } else { self.nameLabel.textColor = [UIColor blackColor]; self.vipView.hidden = YES; } self.contentLabel.text = status.text; if (status.picture) { self.pictureView.hidden = NO; self.pictureView.image = [UIImage imageNamed:status.picture]; } else { self.pictureView.hidden = YES; } // 计算 cell 高度 // 强制布局 [self layoutIfNeeded]; // 计算 cell 的高度 if (self.pictureView.hidden) { // 没有配图 _status.cellHeight = CGRectGetMaxY(self.contentLabel.frame) + 10; } else { // 有配图 _status.cellHeight = CGRectGetMaxY(self.pictureView.frame) + 10; } } @end
- XMGStatusesViewController.m
@interface XMGStatusesViewController () @property (strong, nonatomic) NSArray *statuses; @end @implementation XMGStatusesViewController - (NSArray *)statuses { if (_statuses == nil) { // 加载plist中的字典数组 NSString *path = [[NSBundle mainBundle] pathForResource:@"statuses.plist" ofType:nil]; NSArray *dictArray = [NSArray arrayWithContentsOfFile:path]; // 字典数组 -> 模型数组 NSMutableArray *statusArray = [NSMutableArray array]; for (NSDictionary *dict in dictArray) { XMGStatus *status = [XMGStatus statusWithDict:dict]; [statusArray addObject:status]; } _statuses = statusArray; } return _statuses; } #pragma mark - Table view data source - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return self.statuses.count; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { XMGStatusCell *cell = [XMGStatusCell cellWithTableView:tableView]; cell.status = self.statuses[indexPath.row]; return cell; } #pragma mark - 代理方法 // 返回每一行的高度 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { XMGStatus *staus = self.statuses[indexPath.row]; return staus.cellHeight; } /** * 返回每一行的估计高度 * 只要返回了估计高度,那么就会先调用 tableView:cellForRowAtIndexPath: 方法创建 cell, * 再调用 tableView:heightForRowAtIndexPath: 方法获取 cell 的真实高度 */ - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath { return 200; } @end
- XMGStatus.h
- 运行效果
7.3 其它设置方式
7.3.1 计算方式
- Objective-C
- BookModel.h
@property(nonatomic, copy)NSString *title; @property(nonatomic, copy)NSString *detail; @property(nonatomic, copy)NSString *icon; @property(nonatomic, copy)NSString *price;
- BookCell.h
@property(nonatomic, retain)UILabel *titleLabel; @property(nonatomic, retain)UILabel *detailLabel; @property(nonatomic, retain)UIImageView *iconView; @property(nonatomic, retain)UILabel *priceLabel;
- 设置行高
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { // 从数据源数组中取出数据 BookModel *bookModel = [myDataArray objectAtIndex:indexPath.row]; // 计算 detailLabel 占用的高度 CGFloat detialHeight = [bookModel.detail boundingRectWithSize:CGSizeMake(self.view.bounds.size.width - 40, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName: [UIFont systemFontOfSize:14]} context:nil].size.height; // 判断是否有图片 if (bookModel.icon.length) { // 60 为图片的高度 return 30 + detialHeight + 60 + 30; } else { return 30 + detialHeight + 30; } }
- 设置每一行显示的内容
// 设置每一行显示的内容 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { BookCell3 *cell = [tableView dequeueReusableCellWithIdentifier:@"test" forIndexPath:indexPath]; BookModel *bookModel = [myDataArray objectAtIndex:indexPath.row]; // 设置 titleLabel cell.titleLabel.text = bookModel.title; // 设置 detailLabel // 计算 detailLabel 的高度 CGSize detialSize = [bookModel.detail boundingRectWithSize:CGSizeMake(self.view.bounds.size.width - 40, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName: [UIFont systemFontOfSize:14]} context:nil].size; CGRect detialFrame = cell.detailLabel.frame; detialFrame.size.height = detialSize.height + 5; // 加偏移量 5,适应标点无法换行 detialFrame.size.width = detialSize.width + 5; cell.detailLabel.frame = detialFrame; // 设置 detailLabel 的 frame cell.detailLabel.text = bookModel.detail; // 判断是否有图片 if (bookModel.icon.length) { // 设置 iconView CGRect iconFrame = cell.iconView.frame; iconFrame.origin.y = detialFrame.origin.y + detialFrame.size.height; cell.iconView.frame = iconFrame; cell.iconView.image = [UIImage imageNamed: bookModel.icon]; // 设置 priceLabel CGRect priceFrame = cell.priceLabel.frame; priceFrame.origin.y = iconFrame.origin.y + iconFrame.size.height; cell.priceLabel.frame = priceFrame; cell.priceLabel.text = bookModel.price; } else { // 设置 priceLabel CGRect priceFrame = cell.priceLabel.frame; priceFrame.origin.y = detialFrame.origin.y + detialFrame.size.height; cell.priceLabel.frame = priceFrame; cell.priceLabel.text = bookModel.price; } return cell; }
- BookModel.h
- Swift
- BookModel.swift
var title:String? var detail:String? var icon:String? var price:String?
- BookCell.swift
var titleLabel:UILabel? var detailLabel:UILabel? var iconView:UIImageView? var priceLabel:UILabel?
- 设置行高
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { // 从数据源数组中取出数据 let bookModel:BookModel = myDataArray.objectAtIndex(indexPath.row) as! BookModel // 计算 detailLabel 占用的高度 let detialHeight:CGFloat = NSString(string: bookModel.detail!) .boundingRectWithSize(CGSizeMake(self.view.bounds.size.width - 40, CGFloat.max), options: .UsesLineFragmentOrigin, attributes: [NSFontAttributeName : (UIFont.systemFontOfSize(14) as AnyObject)], context: nil).size.height // 判断是否有图片 if bookModel.icon?.characters.count != 0 { return 30 + detialHeight + 60 + 30 // 60 为图片的高度 } else { return 30 + detialHeight + 30 } }
- 设置每一行显示的内容
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCellWithIdentifier("test", forIndexPath: indexPath) as! BookCell3 let bookModel:BookModel = myDataArray.objectAtIndex(indexPath.row) as! BookModel // 设置 titleLabel cell.titleLabel!.text = bookModel.title // 设置 detailLabel // 计算 detailLabel 的高度 let detialSize:CGSize = NSString(string: bookModel.detail!) .boundingRectWithSize(CGSizeMake(self.view.bounds.size.width - 40, CGFloat.max), options: .UsesLineFragmentOrigin, attributes: [NSFontAttributeName : (UIFont.systemFontOfSize(14) as AnyObject)], context: nil).size var detialFrame:CGRect = cell.detailLabel!.frame detialFrame.size.height = detialSize.height + 5 // 加偏移量 5,适应标点无法换行 detialFrame.size.width = detialSize.width + 5 cell.detailLabel!.frame = detialFrame // 设置 detailLabel 的 frame cell.detailLabel!.text = bookModel.detail // 判断是否有图片 if bookModel.icon?.characters.count != 0 { // 设置 iconView var iconFrame:CGRect = cell.iconView!.frame iconFrame.origin.y = detialFrame.origin.y + detialFrame.size.height cell.iconView!.frame = iconFrame cell.iconView!.image = UIImage(named: bookModel.icon!) // 设置 priceLabel var priceFrame:CGRect = cell.priceLabel!.frame priceFrame.origin.y = iconFrame.origin.y + iconFrame.size.height cell.priceLabel!.frame = priceFrame cell.priceLabel!.text = bookModel.price } else { // 设置 priceLabel var priceFrame:CGRect = cell.priceLabel!.frame priceFrame.origin.y = detialFrame.origin.y + detialFrame.size.height cell.priceLabel!.frame = priceFrame cell.priceLabel!.text = bookModel.price } return cell; }
- BookModel.swift
7.3.2 系统自动布局方式
- 自适应 cell 中较高的一个视图的高度。
- ImageView 与 Label 同行显示,且都设置了上下边缘约束,ImageView 的图片填充模式为 Aspect Fit,否则图片将会被拉长。
- Objective-C
- 协议方法 方式设置
// 动态设置行高 - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath { /* 行高自适应 Label 高度 */ secondTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"secondTableViewCell" forIndexPath:indexPath]; cell.secondLabel.text = [_labelArray objectAtIndex:indexPath.row]; return [cell.contentView systemLayoutSizeFittingSize:(UILayoutFittingCompressedSize)].height + 1; } // 属性变量 方式设置 self.tableView.estimatedRowHeight = 80; self.tableView.rowHeight = UITableViewAutomaticDimension;
- 协议方法 方式设置
- Swift
- 协议方法 方式设置
// 动态设置行高 override func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { /* 行高自适应 Label 高度 */ var cell = tableView.dequeueReusableCellWithIdentifier("secondTableViewCell", forIndexPath: indexPath) as! secondTableViewCell cell.secondLabel.text = labelArray[indexPath.row] return cell.contentView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height + 1 } // 属性变量 方式设置 self.tableView.estimatedRowHeight = 80 self.tableView.rowHeight = UITableViewAutomaticDimension
- 协议方法 方式设置
8、分段索引条的创建
- Objective-C
- 创建索引条
// UITableViewDataSource 协议方法 - (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView { // 索引条数据源数组初始化,实例化索引条上的字符存放的数组对象 NSMutableArray *titleIndexArray = [NSMutableArray array]; // 向数组中添加系统自带放大镜图标,会被处理成一个放大镜 [titleIndexArray addObject:UITableViewIndexSearch]; // 向数据源中添加数据 for (int i = 'A'; i<='Z'; i++) { // 点击索引条上第几个图标,tableView 就会跳到第几段 [titleIndexArray addObject:[NSString stringWithFormat:@"%c 组 ", i]]; } // 索引条上字符颜色,默认为蓝色 tableView.sectionIndexColor = [UIColor redColor]; // 索引条上常规时背景颜色,默认为白色 tableView.sectionIndexBackgroundColor = [UIColor blackColor]; // 索引条上点击时背景颜色,默认为白色 tableView.sectionIndexTrackingBackgroundColor = [UIColor grayColor]; return titleIndexArray; }
- 设置索引条偏移量
// UITableViewDataSource 协议方法 /* 默认索引条与分段一一对应时,可以不写该方法。如果索引条的前面加了个搜索小图标等,需要重写这个方法。A 所在的分段在 tableView 中为第 0 段 */ - (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index { return index - 1; }
- 创建索引条
- Swift
- 创建索引条
// UITableViewDataSource 协议方法 func sectionIndexTitlesForTableView(tableView: UITableView) -> [String]? { // 索引条数据源数组初始化,实例化索引条上的字符存放的数组对象 var titleIndexArray:[String] = Array() // 向数组中添加系统自带放大镜图标,会被处理成一个放大镜 titleIndexArray.append(UITableViewIndexSearch) // 向数据源中添加数据 for i in 65...90 { // 点击索引条上第几个图标,tableView 就会跳到第几段 titleIndexArray.append("\(Character(UnicodeScalar(i))) 组 ") } // 索引条上索引条上字符颜色,默认为蓝色 tableView.sectionIndexColor = UIColor.redColor() // 索引条上常规时背景颜色,默认为白色 tableView.sectionIndexBackgroundColor = UIColor.blackColor() // 索引条上点击时背景颜色,默认为白色 tableView.sectionIndexTrackingBackgroundColor = UIColor.grayColor() return titleIndexArray }
- 设置索引条偏移量
// UITableViewDataSource 协议方法 /* 默认索引条与分段一一对应时,可以不写该方法。如果索引条的前面加了个搜索小图标等,需要重写这个方法。A 所在的分段在 tableView 中为第 0 段 */ func tableView(tableView: UITableView, sectionForSectionIndexTitle title: String, atIndex index: Int) -> Int { return index - 1 }
- 创建索引条
- 运行效果
9、搜索框的创建
- 在 iOS 8.0 以上版本中, 我们可以使用 UISearchController 来非常方便地在 UITableView 中添加搜索框. 而在之前版本中, 我们还是必须使用 UISearchDisplayController + UISearchBar 的组合方式。
- 我们创建的 tableView 和搜索控制器创建的 tableView 都会走代理方法,需要在代理方法中判断响应代理方法的 tableView 是哪一个,如果响应代理方法的 tableView 不是我创建的,说明一定是搜索控制器创建的。在 iOS 8.0 以下版本中需使用 tableView == myTableView 判断,在 iOS 8.0 以上版本中需使用 mySearchController.active 判断。
9.1 在 iOS 8.0 以下版本中
- Objective-C
- 遵守协议 UISearchDisplayDelegate
- 搜索结果数组初始化
// 声明搜索结果存放数组 @property(nonatomic, retain)NSMutableArray *mySearchResultArray; // 初始化搜索结果存放数组 mySearchResultArray = [[NSMutableArray alloc] init];
- searchDisplayController 初始化
// 声明搜索控制器,自带一个表格视图,用来展示搜索结果,必须设置为全局变量 @property(nonatomic, retain)UISearchDisplayController *mySearchDisplayController; // 实例化搜索条 UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 44)]; // 实例化搜索控制器对象 mySearchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar:searchBar contentsController:self]; // 设置搜索控制器的代理 mySearchDisplayController.delegate = self; // 为搜索控制器自带 tableView 指定代理 mySearchDisplayController.searchResultsDelegate = self; mySearchDisplayController.searchResultsDataSource = self; // 将搜索条设置为 tableView 的表头 myTableView.tableHeaderView = searchBar;
- UISearchDisplayDelegate 协议方法
// 更新搜索结果 /* 只要搜索框的文字发生了改变,这个方法就会触发。searchString 为搜索框内输入的内容。 */ - (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString { // 清空上一次搜索的内容 [mySearchResultArray removeAllObjects]; for (NSArray *subArray in myDataArray) { // 将搜索的结果存放到数组中 for (NSString *str in subArray) { NSRange range = [str rangeOfString:searchString]; if (range.length) { [mySearchResultArray addObject:str]; } } } return YES; }
- UITableView 协议方法
// 设置分段头标题 - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { if (tableView == myTableView) { return [NSString stringWithFormat:@"%c", (char)('A' + section)]; } return @"搜索结果"; } // 设置分段数 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { if (tableView == myTableView) { return myDataArray.count; } return 1; } // 设置行数 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { if (tableView == myTableView) { return [[myDataArray objectAtIndex:section] count]; } return mySearchResultArray.count; } // 设置每段显示的内容 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"testIdentifier"]; if (!cell) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"testIdentifier"]; } if (tableView == myTableView) { cell.textLabel.text = [[myDataArray objectAtIndex:indexPath.section] objectAtIndex:indexPath.row]; } else { cell.textLabel.text = [mySearchResultArray objectAtIndex:indexPath.row]; } return cell; }
- Swift
- 遵守协议 UISearchDisplayDelegate
- 搜索结果数组初始化
// 初始化搜索结果存放数组 var mySearchResultArray:[String] = Array()
- searchDisplayController 初始化
// 声明搜索控制器,自带一个表格视图,用来展示搜索结果,必须设置为全局变量 var mySearchDisplayController:UISearchDisplayController! // 实例化搜索条 let searchBar:UISearchBar = UISearchBar(frame: CGRectMake(0, 0, self.view.frame.size.width, 44)) // 实例化搜索控制器对象 mySearchDisplayController = UISearchDisplayController(searchBar: searchBar, contentsController: self) // 设置搜索控制器的代理 mySearchDisplayController.delegate = self // 为搜索控制器自带 tableView 指定代理 mySearchDisplayController.searchResultsDelegate = self mySearchDisplayController.searchResultsDataSource = self // 将搜索条设置为 tableView 的表头 myTableView.tableHeaderView = searchBar
- UISearchDisplayDelegate 协议方法
// 更新搜索结果 /* 只要搜索框的文字发生了改变,这个方法就会触发。searchString 为搜索框内输入的内容 */ func searchDisplayController(controller: UISearchDisplayController, shouldReloadTableForSearchString searchString: String?) -> Bool { // 清空上一次搜索的内容 mySearchResultArray.removeAll() // 将搜索的结果存放到数组中 for subArray in myDataArray { for str in subArray { let range:NSRange = (str as NSString).rangeOfString(searchString!) if range.length != 0 { mySearchResultArray.append(str) } } } return true }
- UITableView 协议方法
// 设置分段头标题 func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? { if tableView == myTableView { return "\(Character(UnicodeScalar(65 + section)))" } return "搜索结果" } // 设置分段数 func numberOfSectionsInTableView(tableView: UITableView) -> Int { if tableView == myTableView { return myDataArray.count } return 1 } // 设置行数 func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { if tableView == myTableView { return myDataArray[section].count } return mySearchResultArray.count } // 设置每段显示的内容 func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { var cell = tableView.dequeueReusableCellWithIdentifier("testIdentifier") if cell == nil { cell = UITableViewCell(style: .Default, reuseIdentifier: "testIdentifier") } if tableView == myTableView { cell!.textLabel?.text = myDataArray[indexPath.section][indexPath.row] } else { cell!.textLabel?.text = mySearchResultArray[indexPath.row] } return cell! }
9.2 在 iOS 8.0 及以上版本中
- Objective-C
- 遵守协议 UISearchResultsUpdating
- 搜索结果数组初始化
// 声明搜索结果存放数组 @property(nonatomic, retain)NSMutableArray *mySearchResultArray; // 初始化搜索结果存放数组 mySearchResultArray = [[NSMutableArray alloc] init];
- searchController 初始化
// 声明搜索控制器,自带一个表格视图控制器,用来展示搜索结果,必须设置为全局变量 @property(nonatomic, retain)UISearchController *mySearchController; // 实例化搜索控制器 mySearchController = [[UISearchController alloc] initWithSearchResultsController:nil]; // 设置搜索代理 mySearchController.searchResultsUpdater = self; // 设置搜索条大小 [mySearchController.searchBar sizeToFit]; // 设置搜索期间背景视图是否取消操作,default is YES mySearchController.dimsBackgroundDuringPresentation = NO; // 设置搜索期间是否隐藏导航条,default is YES mySearchController.hidesNavigationBarDuringPresentation = NO; // 将 searchBar 添加到表格的开头 myTableView.tableHeaderView = mySearchController.searchBar;
- UISearchResultsUpdating 协议方法
// 更新搜索结果 /* 只要搜索框的文字发生了改变,这个方法就会触发。searchController.searchBar.text 为搜索框内输入的内容 */ - (void)updateSearchResultsForSearchController:(UISearchController *)searchController { // 清除上一次的搜索结果 [mySearchResultArray removeAllObjects]; // 将搜索的结果存放到数组中 for (NSArray *subArray in myDataArray) { for (NSString *str in subArray) { NSRange range = [str rangeOfString:searchController.searchBar.text]; if (range.length) { [mySearchResultArray addObject:str]; } } } // 重新加载表格视图,不加载的话将不会显示搜索结果 [myTableView reloadData]; }
- UITableView 协议方法
// 设置分段头标题 - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { if (mySearchController.active) { return @"搜索结果"; } return [NSString stringWithFormat:@"%c", (char)('A' + section)]; } // 设置分段数 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { if (mySearchController.active) { return 1; } return myDataArray.count; } // 设置行数 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { if (mySearchController.active) { return mySearchResultArray.count; } return [[myDataArray objectAtIndex:section] count]; } // 设置每段显示的内容 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"testIdentifier"]; if (!cell) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"testIdentifier"]; } if (mySearchController.active) { cell.textLabel.text = [mySearchResultArray objectAtIndex:indexPath.row]; } else { cell.textLabel.text = [[myDataArray objectAtIndex:indexPath.section] objectAtIndex:indexPath.row]; } return cell; }
- Swift
- 遵守协议 UISearchResultsUpdating
- 搜索结果数组初始化
// 初始化搜索结果存放数组 var searchResultArray:[String] = Array()
- searchController 初始化
// 声明搜索控制器,自带一个表格视图控制器,用来展示搜索结果,必须设置为全局变量 var mySearchController:UISearchController! // 实例化搜索控制器 mySearchController = UISearchController(searchResultsController: nil) // 设置搜索代理 mySearchController.searchResultsUpdater = self // 设置搜索条大小 mySearchController.searchBar.sizeToFit() // 设置搜索期间背景视图是否取消操作,default is YES mySearchController.dimsBackgroundDuringPresentation = false // 设置搜索期间是否隐藏导航条,default is YES mySearchController.hidesNavigationBarDuringPresentation = false // 将 searchBar 添加到表格的开头 myTableView.tableHeaderView = mySearchController.searchBar
- UISearchResultsUpdating 协议方法
// 更新搜索结果 /* 只要搜索框的文字发生了改变,这个方法就会触发。searchController.searchBar.text 为搜索框内输入的内容 */ func updateSearchResultsForSearchController(searchController: UISearchController) { // 清除上一次的搜索结果 searchResultArray.removeAll() // 将搜索的结果存放到数组中 for subArray in myDataArray { for str in subArray { let range:NSRange = (str as NSString).rangeOfString(searchController.searchBar.text!) if range.length != 0 { searchResultArray.append(str) } } } // 重新加载表格视图,不加载的话将不会显示搜索结果 myTableView.reloadData() }
- UITableView 协议方法
// 设置分段头标题 func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? { if mySearchController.active { return "搜索结果" } return "\(Character(UnicodeScalar(65 + section)))" } // 设置分段数 func numberOfSectionsInTableView(tableView: UITableView) -> Int { if mySearchController.active { return 1 } return myDataArray.count } // 设置行数 func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { if mySearchController.active { return searchResultArray.count } return myDataArray[section].count } // 设置每段显示的内容 func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { var cell = tableView.dequeueReusableCellWithIdentifier("testIdentifier") if cell == nil { cell = UITableViewCell(style: .Default, reuseIdentifier: "testIdentifier") } if mySearchController.active { cell!.textLabel?.text = searchResultArray[indexPath.row] } else { cell!.textLabel?.text = myDataArray[indexPath.section][indexPath.row] } return cell! }
- 运行效果
10、表格折叠
- 通过改变分段的行数实现分段的折叠与打开。分段处于折叠状态时,设置分段的行数为 0。
- Objective-C
- 分段折叠状态数组初始化
// 声明记录折叠状态数组 @property(nonatomic, retain)NSMutableArray *foldStatusArray; // 初始化记录折叠状态数组 foldStatusArray = [[NSMutableArray alloc] init]; // 给分段折叠状态数组赋初值,状态值为 1 时,分段折叠 for (int i = 0; i < myDataArray.count; i++) { [foldStatusArray addObject:[NSNumber numberWithBool:YES]]; }
- UITableView 协议方法
// 设置行数,UITableViewDataSource 协议方法 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { // 获取分段的折叠状态,foldStatusArray 存放的是 NSNumber 类型的值 BOOL isFold = [[foldStatusArray objectAtIndex:section] boolValue]; if (isFold) { // 分段处于折叠状态时,设置分段的行数为 0 return 0; } return [[myDataArray objectAtIndex:section] count]; } // 设置分段头标题高度,UITableViewDelegate 协议方法 - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section { return 30; } // 设置分段头标题视图,UITableViewDelegate 协议方法 - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section { UIButton *headerButton = [UIButton buttonWithType:UIButtonTypeCustom]; headerButton.frame = CGRectMake(0, 0, tableView.frame.size.width, 30); headerButton.backgroundColor = [UIColor orangeColor]; headerButton.titleLabel.font = [UIFont boldSystemFontOfSize:20]; headerButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft; headerButton.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter; headerButton.layer.borderColor = [[UIColor lightGrayColor] CGColor]; headerButton.layer.borderWidth = 1; // 设置分段头标题显示内容 [headerButton setTitle:[NSString stringWithFormat:@" %c", (char)('A' + section)] forState:UIControlStateNormal]; // 设置分段的 tag 值 headerButton.tag = 100 + section; // 添加分段头标题点击响应事件 [headerButton addTarget:self action:@selector(headerButtonClick:) forControlEvents:UIControlEventTouchUpInside]; return headerButton; }
- 头标题点击响应事件
- (void)headerButtonClick:(UIButton *)button { // 获取分段的折叠状态,foldStatusArray 存放的是 NSNumber 类型的值 BOOL isFold = [[foldStatusArray objectAtIndex:button.tag - 100] boolValue]; // 改变分段的折叠状态 foldStatusArray[button.tag - 100] = [NSNumber numberWithInt: isFold ? NO : YES]; // 重载分段 [myTableView reloadSections:[NSIndexSet indexSetWithIndex:button.tag - 100] withRowAnimation:UITableViewRowAnimationAutomatic]; }
- 分段折叠状态数组初始化
- Swift
- 分段折叠状态数组初始化
// 初始化分段折叠状态数组 var foldStatusArray:[NSNumber] = Array() // 分段折叠状态数组赋值,状态值为 1 时,分段折叠 for _ in 0 ..< myDataArray.count { foldStatusArray.append(NSNumber(bool: true)) }
- UITableView 协议方法
// 设置行数 func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { // 获取分段的折叠状态,foldStatusArray 存放的是 NSNumber 类型的值 let isFold:Bool = foldStatusArray[section].boolValue if isFold { // 分段处于折叠状态时,设置分段的行数为 0 return 0 } return myDataArray[section].count } // 设置分段头标题的高度 func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { return 30 } // 设置分段头标题视图 func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { let headerButton:UIButton = UIButton(type: .Custom) headerButton.frame = CGRectMake(0, 0, tableView.frame.size.width, 30) headerButton.backgroundColor = UIColor.orangeColor() headerButton.titleLabel!.font = UIFont.boldSystemFontOfSize(20) headerButton.contentHorizontalAlignment = UIControlContentHorizontalAlignment.Left headerButton.contentVerticalAlignment = UIControlContentVerticalAlignment.Center headerButton.layer.borderColor = UIColor.lightGrayColor().CGColor headerButton.layer.borderWidth = 1 // 设置分段头标题显示内容 headerButton.setTitle(" \(Character(UnicodeScalar(65 + section)))", forState: UIControlState.Normal) // 设置分段的 tag 值 headerButton.tag = 100 + section // 添加分段头标题点击响应事件 headerButton.addTarget(self, action: #selector(UiTableViewController10.headerButtonClick(_:)), forControlEvents: UIControlEvents.TouchUpInside) return headerButton }
- 头标题点击响应事件
func headerButtonClick(button:UIButton) { // 获取分段的折叠状态,foldStatusArray 存放的是 NSNumber 类型的值 let isFold:Bool = foldStatusArray[button.tag - 100].boolValue // 改变分段的折叠状态 foldStatusArray[button.tag - 100] = NSNumber(bool: isFold ? false : true) // 重载分段 myTableView.reloadSections(NSIndexSet(index: button.tag - 100), withRowAnimation: .Automatic) }
- 分段折叠状态数组初始化
- 运行效果
11、表格编辑
- Objective-C
- 设置表格编辑开关状态
// 设置表格的编辑状态 myTableView.editing = YES; // 翻转表格的编辑状态 myTableView.editing = !myTableView.editing; // 带动画翻转表格的编辑状态 [myTableView setEditing:!myTableView.editing animated:YES];
- 修改左滑删除按钮的内容
// UITableViewDelegate 协议方法 /* 默认为 Delete */ - (NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath { return @"删除"; }
- 设置左滑多按钮
- (NSArray *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewRowAction *action0 = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleNormal title:@"关注" handler:^(UITableViewRowAction *action, NSIndexPath *indexPath) { NSLog(@"点击了关注"); // 收回左滑出现的按钮(退出编辑模式) tableView.editing = NO; }]; UITableViewRowAction *action1 = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDefault title:@"删除" handler:^(UITableViewRowAction *action, NSIndexPath *indexPath) { [[myDataArray objectAtIndex:indexPath.section] removeObjectAtIndex:indexPath.row]; [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; }]; // 按钮从右向左的顺序排列 return @[action1, action0]; }
- 设置编辑模式
/* UITableViewCellEditingStyleNone; // 无 UITableViewCellEditingStyleDelete; // 删除模式,默认 UITableViewCellEditingStyleInsert; // 插入模式 UITableViewCellEditingStyleDelete | UITableViewCellEditingStyleInsert; // 多选模式 */ // UITableViewDelegate 协议方法 - (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath { // 删除、插入、多选删除,不设置默认时为删除 if (0 == indexPath.section) { return UITableViewCellEditingStyleDelete; } else { return UITableViewCellEditingStyleInsert; } }
- 表格删除、插入
表格删除: 1. 先将数据从数据源里删除, 2. 再从 tableView 里删除 cell: [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; 或者再直接重载整个表格: [tableView reloadData]; 或者在直接重载分段: [tableView reloadSections:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation:UITableViewRowAnimationAutomatic]; 表格插入: 1. 先将数据插入到数据源中, 2. 然后再插入一个 cell: [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; 或者再直接重载整个表格: [tableView reloadData]; 或者在直接重载分段: [tableView reloadSections:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation:UITableViewRowAnimationAutomatic];
// 表格删除或插入,默认为删除模式,写入该方法即表示允许删除 // UITableViewDataSource 协议方法 - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { // 判断编辑风格,默认是删除 if (editingStyle == UITableViewCellEditingStyleDelete) { // 表格删除 // 从数据源里删除 [[myDataArray objectAtIndex:indexPath.section] removeObjectAtIndex:indexPath.row]; // 从 tableView 里删除 cell [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; } else if (editingStyle == UITableViewCellEditingStyleInsert) { Person *person = [[Person alloc] init]; person.name = @"xiao bai"; person.age = 18; // 表格插入 // 插入到数据源中 [[myDataArray objectAtIndex:indexPath.section] insertObject:person atIndex:indexPath.row]; // 插入一个 cell [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; } }
- 表格移动
表格移动: 1. 先在数据源中找到需要移动的对象。 2. 然后在数据源数组中从原始位置删掉。 3. 再在数据源数组中插入到新位置。 4. 最后重新加载表格: [tableView reloadData]; 或者在直接重载分段: [tableView reloadSections:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation:UITableViewRowAnimationAutomatic];
// 写入该方法即表示允许移动 // UITableViewDataSource 协议方法 - (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath { // 找到需要移动的对象 Person *person = [[myDataArray objectAtIndex:sourceIndexPath.section] objectAtIndex:sourceIndexPath.row]; // 从原始位置删掉 [[myDataArray objectAtIndex:sourceIndexPath.section] removeObjectAtIndex:sourceIndexPath.row]; // 插入到新位置 [[myDataArray objectAtIndex:destinationIndexPath.section] insertObject:person atIndex:destinationIndexPath.row]; // 刷新 tableView [tableView reloadData]; }
- 设置表格编辑开关状态
- Swift
- 设置表格编辑开关状态
// 设置表格的编辑状态 myTableView.editing = true // 翻转表格的编辑状态 myTableView.editing = !myTableView.editing // 带动画翻转表格的编辑状态 myTableView.setEditing(!myTableView.editing, animated: true)
- 修改左滑删除按钮的内容
// UITableViewDelegate 协议方法 func tableView(tableView: UITableView, titleForDeleteConfirmationButtonForRowAtIndexPath indexPath: NSIndexPath) -> String? { // 默认为 Delete return "删除" }
- 设置左滑多按钮
func tableView(tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? { let action0:UITableViewRowAction = UITableViewRowAction(style: .normal, title: "关注") { (action:UITableViewRowAction, indexPath:IndexPath) in print("点击了关注") // 收回左滑出现的按钮(退出编辑模式) tableView.isEditing = false } let action1:UITableViewRowAction = UITableViewRowAction(style: .normal, title: "删除") { (action:UITableViewRowAction, indexPath:IndexPath) in myDataArray[indexPath.section].remove(at: indexPath.row) tableView.deleteRows(at: NSArray(object: indexPath) as! [IndexPath], with: .automatic) } // 按钮从右向左的顺序排列 return [action1, action0] }
- 设置编辑模式
/* 删除、插入,不设置默认时为删除,不能设置多选删除模式, 若要实现多选删除,需设置 myTableView.allowsMultipleSelectionDuringEditing = true UITableViewCellEditingStyle.None // 无 UITableViewCellEditingStyle.Delete // 删除模式,默认 UITableViewCellEditingStyle.Insert // 插入模式 */ // UITableViewDelegate 协议方法 func tableView(tableView: UITableView, editingStyleForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCellEditingStyle { if 0 == indexPath.section { return .Delete } else { return .Insert } }
- 表格删除、插入
表格删除: 1. 先将数据从数据源里删除, 2. 再从 tableView 里删除 cell: tableView.deleteRowsAtIndexPaths(NSArray(object: indexPath) as! [NSIndexPath], withRowAnimation: .Automatic) 或者再直接重载整个表格: tableView.reloadData() 或者在直接重载分段: tableView.reloadSections(NSIndexSet(index: indexPath.section) , withRowAnimation: .Automatic) 表格插入: 1. 先将数据插入到数据源中, 2. 然后再插入一个 cell: tableView.insertRowsAtIndexPaths(NSArray(object: indexPath) as! [NSIndexPath], withRowAnimation: .Automatic) 或者再直接重载整个表格: tableView.reloadData() 或者在直接重载分段: tableView.reloadSections(NSIndexSet(index: indexPath.section) , withRowAnimation: .Automatic)
// 表格删除或插入,默认为删除模式,写入该方法即表示允许删除 // UITableViewDataSource 协议方法 func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) { // 判断编辑风格,默认是删除 if editingStyle == UITableViewCellEditingStyle.Delete { // 表格删除 // 从数据源里删除 myDataArray[indexPath.section].removeAtIndex(indexPath.row) // 从 tableView 里删除 cell tableView.deleteRowsAtIndexPaths(NSArray(object: indexPath) as! [NSIndexPath], withRowAnimation: .Automatic) } else if editingStyle == UITableViewCellEditingStyle.Insert { let person:Person = Person() person.name = "xiao bai” person.age = 18 // 表格插入 // 插入到数据源中 myDataArray[indexPath.section].insert(person, atIndex: indexPath.row) // 插入一个 cell tableView.insertRowsAtIndexPaths(NSArray(object: indexPath) as! [NSIndexPath], withRowAnimation: .Automatic) } }
- 表格移动
表格移动: 1. 先在数据源中找到需要移动的对象。 2. 然后在数据源数组中从原始位置删掉。 3. 再在数据源数组中插入到新位置。 4. 最后重新加载表格: tableView.reloadData() 或者在直接重载分段: tableView.reloadSections(NSIndexSet(index: indexPath.section) , withRowAnimation: .Automatic)
// 写入该方法即表示允许移动 // UITableViewDataSource 协议方法 func tableView(tableView: UITableView, moveRowAtIndexPath sourceIndexPath: NSIndexPath, toIndexPath destinationIndexPath: NSIndexPath) { // 找到需要移动的对象 let person:Person = myDataArray[sourceIndexPath.section][sourceIndexPath.row] // 从原始位置删掉 myDataArray[sourceIndexPath.section].removeAtIndex(sourceIndexPath.row) // 插入到新位置 myDataArray[destinationIndexPath.section].insert(person, atIndex: destinationIndexPath.row) tableView.reloadData() }
- 设置表格编辑开关状态
- 运行效果
12、表格多选删除
12.1 系统方式
- 将要删除的数据添加到待删数组中,从数据源中删除待删数组中包含的数据,刷新表格。
- OC 中可设置编辑模式为 UITableViewCellEditingStyleDelete | UITableViewCellEditingStyleInsert; 或者设置 myTableView.allowsMultipleSelectionDuringEditing = YES; 进入多选模式。
- Swift 需设置 myTableView.allowsMultipleSelectionDuringEditing = true 进入多选模式。
- Objective-C
- 待删数据数组初始化
// 声明待删数据数组 @property(nonatomic, retain)NSMutableArray *tempDeleteArray; // 初始化待删数据数组 tempDeleteArray = [[NSMutableArray alloc] init];
- 自定义方法
// 编辑按钮点击响应事件 - (void)editClick:(UIButton *)button { // 改变编辑开关状态 [myTableView setEditing:!myTableView.editing animated:YES]; // 设置编辑模式,允许编辑时多选,或者在协议方法中设置 myTableView.allowsMultipleSelectionDuringEditing = YES; // 当编辑状态发生改变的时候,清空待删数组 [tempDeleteArray removeAllObjects]; [myTableView reloadData]; } // 删除按钮点击响应事件 - (void)deleteClick:(UIButton *)button { // 从数据源中删除待选数组中包含的数据 [myDataArray removeObjectsInArray:tempDeleteArray]; // 清空待删数组 [tempDeleteArray removeAllObjects]; [myTableView reloadData]; }
- UITableView 协议方法
// 设置编辑模式 /* 删除、插入、多选删除,不设置默认时为删除, 或者在编辑按钮点击事件中直接设置 myTableView.allowsMultipleSelectionDuringEditing = YES; */ - (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath { // 多选删除 return UITableViewCellEditingStyleDelete | UITableViewCellEditingStyleInsert; } // 表格选中点击响应事件 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { // 判断 tableView 的编辑状态,表格处于编辑状态 if (tableView.isEditing) { // 选中 cell 的时候,将对应的数据源模型添加到待删除数组中 [tempDeleteArray addObject:[myDataArray objectAtIndex:indexPath.row]]; } else { // 恢复未选中状态时的颜色 [tableView deselectRowAtIndexPath:indexPath animated:YES]; } } // 表格取消选中点击响应事件 - (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath { // 判断 tableView 的编辑状态,表格处于编辑状态 if (tableView.isEditing) { // 将对应的数据模型从待删除数组中移除 [tempDeleteArray removeObject:[myDataArray objectAtIndex:indexPath.row]]; } }
- 待删数据数组初始化
- Swift
- 待删数据数组初始化
// 初始化待删数据数组 var tempDeleteArray:[Dog] = Array()
- 自定义方法
// 编辑按钮点击响应事件 func editClick(button:UIButton){ // 改变编辑开关状态 myTableView.setEditing(!myTableView.editing, animated: true) // 设置编辑模式,允许编辑时多选 myTableView.allowsMultipleSelectionDuringEditing = true // 当编辑状态发生改变的时候,清空待删数组 tempDeleteArray.removeAll() myTableView.reloadData() } // 删除按钮点击响应事件 func deleteClick(button:UIButton){ // 从数据源中删除待选数组中包含的数据 myDataArray.removeObjectsInArray(tempDeleteArray) // 清空待删数组 tempDeleteArray.removeAll() myTableView.reloadData() }
- UITableView 协议方法
// 表格被选中 func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { // 判断 tableView 的编辑状态,表格处于编辑状态 if tableView.editing { // 选中 cell 的时候,将对应的数据源模型添加到待删除数组中 tempDeleteArray.append(myDataArray.objectAtIndex(indexPath.row) as! Dog) } else { // 恢复未选中状态时的颜色 tableView.deselectRowAtIndexPath(indexPath, animated: true) } } // 表格被取消选中 func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) { // 判断 tableView 的编辑状态,表格处于编辑状态 if tableView.editing { // 将对应的数据模型从待删除数组中移除 tempDeleteArray.append(myDataArray.objectAtIndex(indexPath.row) as! Dog) } }
- 待删数据数组初始化
- 运行效果
12.2 自定义方式 1
- Objective-C
- XMGDeal.h
#import <Foundation/Foundation.h> @interface XMGDeal : NSObject @property (strong, nonatomic) NSString *buyCount; @property (strong, nonatomic) NSString *price; @property (strong, nonatomic) NSString *title; @property (strong, nonatomic) NSString *icon; /** 状态量标识有无被打钩 */ @property (assign, nonatomic, getter=isChecked) BOOL checked; + (instancetype)dealWithDict:(NSDictionary *)dict; @end
- XMGDeal.m
#import "XMGDeal.h" @implementation XMGDeal + (instancetype)dealWithDict:(NSDictionary *)dict { XMGDeal *deal = [[self alloc] init]; [deal setValuesForKeysWithDictionary:dict]; return deal; } @end
- XMGDealCell.xib
- XMGDealCell.h
#import <UIKit/UIKit.h> @class XMGDeal; @interface XMGDealCell : UITableViewCell /** 团购模型数据 */ @property (nonatomic, strong) XMGDeal *deal; + (instancetype)cellWithTableView:(UITableView *)tableView; @end
- XMGDealCell.m
#import "XMGDealCell.h" #import "XMGDeal.h" @interface XMGDealCell() @property (weak, nonatomic) IBOutlet UIImageView *iconView; @property (weak, nonatomic) IBOutlet UILabel *titleLabel; @property (weak, nonatomic) IBOutlet UILabel *buyCountLabel; @property (weak, nonatomic) IBOutlet UIImageView *checkView; @property (weak, nonatomic) IBOutlet UILabel *priceLabel; @end @implementation XMGDealCell + (instancetype)cellWithTableView:(UITableView *)tableView { static NSString *ID = @"deal"; XMGDealCell *cell = [tableView dequeueReusableCellWithIdentifier:ID]; if (cell == nil) { cell = [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([XMGDealCell class]) owner:nil options:nil] lastObject]; } return cell; } - (void)setDeal:(XMGDeal *)deal { _deal = deal; // 设置数据 self.iconView.image = [UIImage imageNamed:deal.icon]; self.titleLabel.text = deal.title; self.priceLabel.text = [NSString stringWithFormat:@"¥%@", deal.price]; self.buyCountLabel.text = [NSString stringWithFormat:@"%@人已购买", deal.buyCount]; // 设置打钩控件的显示和隐藏 self.checkView.hidden = !deal.isChecked; } @end
- XMGDealsViewController.m
#import "XMGDealsViewController.h" #import "XMGDeal.h" #import "XMGDealCell.h" @interface XMGDealsViewController () <UITableViewDataSource, UITableViewDelegate> @property (weak, nonatomic) IBOutlet UITableView *tableView; /** 所有的团购数据 */ @property (nonatomic, strong) NSMutableArray *deals; @end @implementation XMGDealsViewController - (NSMutableArray *)deals { if (_deals == nil) { // 加载plist中的字典数组 NSString *path = [[NSBundle mainBundle] pathForResource:@"deals.plist" ofType:nil]; NSArray *dictArray = [NSArray arrayWithContentsOfFile:path]; // 字典数组 -> 模型数组 NSMutableArray *dealArray = [NSMutableArray array]; for (NSDictionary *dict in dictArray) { XMGDeal *deal = [XMGDeal dealWithDict:dict]; [dealArray addObject:deal]; } _deals = dealArray; } return _deals; } - (IBAction)remove { // 临时数组:存放即将需要删除的团购数据 NSMutableArray *deletedDeals = [NSMutableArray array]; for (XMGDeal *deal in self.deals) { if (deal.isChecked) [deletedDeals addObject:deal]; } // 删除模型数据 [self.deals removeObjectsInArray:deletedDeals]; // 刷新表格 [self.tableView reloadData]; } #pragma mark - Table view data source - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return self.deals.count; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { XMGDealCell *cell = [XMGDealCell cellWithTableView:tableView]; // 取出模型数据 cell.deal = self.deals[indexPath.row]; return cell; } #pragma mark - TableView 代理方法 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { // 取消选中这一行 [tableView deselectRowAtIndexPath:indexPath animated:YES]; // 模型的打钩属性取反 XMGDeal *deal = self.deals[indexPath.row]; deal.checked = !deal.isChecked; // 刷新表格 [tableView reloadData]; } @end
- XMGDeal.h
- 运行效果
12.3 自定义方式 2
- Objective-C
- XMGDeal.h
#import <Foundation/Foundation.h> @interface XMGDeal : NSObject @property (strong, nonatomic) NSString *buyCount; @property (strong, nonatomic) NSString *price; @property (strong, nonatomic) NSString *title; @property (strong, nonatomic) NSString *icon; + (instancetype)dealWithDict:(NSDictionary *)dict; @end
- XMGDeal.m
#import "XMGDeal.h" @implementation XMGDeal + (instancetype)dealWithDict:(NSDictionary *)dict { XMGDeal *deal = [[self alloc] init]; [deal setValuesForKeysWithDictionary:dict]; return deal; } @end
- XMGDealCell.xib
- XMGDealCell.h
#import <UIKit/UIKit.h> @class XMGDeal; @interface XMGDealCell : UITableViewCell /** 团购模型数据 */ @property (nonatomic, strong) XMGDeal *deal; + (instancetype)cellWithTableView:(UITableView *)tableView; @end
- XMGDealCell.m
#import "XMGDealCell.h" #import "XMGDeal.h" @interface XMGDealCell() @property (weak, nonatomic) IBOutlet UIImageView *iconView; @property (weak, nonatomic) IBOutlet UILabel *titleLabel; @property (weak, nonatomic) IBOutlet UILabel *buyCountLabel; @property (weak, nonatomic) IBOutlet UIImageView *checkView; @property (weak, nonatomic) IBOutlet UILabel *priceLabel; @end @implementation XMGDealCell + (instancetype)cellWithTableView:(UITableView *)tableView { static NSString *ID = @"deal"; XMGDealCell *cell = [tableView dequeueReusableCellWithIdentifier:ID]; if (cell == nil) { cell = [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([XMGDealCell class]) owner:nil options:nil] lastObject]; } return cell; } - (void)setDeal:(XMGDeal *)deal { _deal = deal; // 设置数据 self.iconView.image = [UIImage imageNamed:deal.icon]; self.titleLabel.text = deal.title; self.priceLabel.text = [NSString stringWithFormat:@"¥%@", deal.price]; self.buyCountLabel.text = [NSString stringWithFormat:@"%@人已购买", deal.buyCount]; } @end
- XMGDealsViewController.m
#import "XMGDealsViewController.h" #import "XMGDeal.h" #import "XMGDealCell.h" @interface XMGDealsViewController () <UITableViewDataSource, UITableViewDelegate> @property (weak, nonatomic) IBOutlet UITableView *tableView; /** 所有的团购数据 */ @property (nonatomic, strong) NSMutableArray *deals; /** 即将要删除的团购 */ @property (nonatomic, strong) NSMutableArray *deletedDeals; @end @implementation XMGDealsViewController - (NSMutableArray *)deletedDeals { if (!_deletedDeals) { _deletedDeals = [NSMutableArray array]; } return _deletedDeals; } - (NSMutableArray *)deals { if (_deals == nil) { // 加载plist中的字典数组 NSString *path = [[NSBundle mainBundle] pathForResource:@"deals.plist" ofType:nil]; NSArray *dictArray = [NSArray arrayWithContentsOfFile:path]; // 字典数组 -> 模型数组 NSMutableArray *dealArray = [NSMutableArray array]; for (NSDictionary *dict in dictArray) { XMGDeal *deal = [XMGDeal dealWithDict:dict]; [dealArray addObject:deal]; } _deals = dealArray; } return _deals; } - (IBAction)remove { // 删除模型数据 [self.deals removeObjectsInArray:self.deletedDeals]; // 刷新表格 [self.tableView reloadData]; // 清空数组 [self.deletedDeals removeAllObjects]; } #pragma mark - Table view data source - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return self.deals.count; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { XMGDealCell *cell = [XMGDealCell cellWithTableView:tableView]; // 取出模型数据 cell.deal = self.deals[indexPath.row]; cell.checkView.hidden = ![self.deletedDeals containsObject:cell.deal]; return cell; } #pragma mark - TableView代理方法 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { // 取消选中这一行 [tableView deselectRowAtIndexPath:indexPath animated:YES]; // 取出模型 XMGDeal *deal = self.deals[indexPath.row]; if ([self.deletedDeals containsObject:deal]) { [self.deletedDeals removeObject:deal]; } else { [self.deletedDeals addObject:deal]; } // 刷新表格 [tableView reloadData]; } @end
- XMGDeal.h
- 运行效果
13、聊天布局
- Objective-C
- XMGMessage.h
#import <UIKit/UIKit.h> typedef enum { XMGMessageTypeMe = 0, XMGMessageTypeOther = 1 } XMGMessageType; @interface XMGMessage : NSObject @property (nonatomic, strong) NSString *text; @property (nonatomic, strong) NSString *time; @property (nonatomic, assign) XMGMessageType type; /** cell 的高度 */ @property (nonatomic, assign) CGFloat cellHeight; /** 是否隐藏时间 */ @property (nonatomic, assign, getter=isHideTime) BOOL hideTime; + (instancetype)messageWithDict:(NSDictionary *)dict; @end
- XMGMessage.m
#import "XMGMessage.h" @implementation XMGMessage + (instancetype)messageWithDict:(NSDictionary *)dict { XMGMessage *message = [[self alloc] init]; [message setValuesForKeysWithDictionary:dict]; return message; } @end
- Main.storyboard
- 设置 cell
- 单 Cell 布局
- 多 Cell 布局
- 设置气泡图片拉伸
- 设置按钮边距
- 设置 cell
- XMGMessageCell.h
#import <UIKit/UIKit.h> @class XMGMessage; @interface XMGMessageCell : UITableViewCell @property (nonatomic, strong) XMGMessage *message; @end
- XMGMessageCell.m
- 单 Cell 布局
#import "XMGMessageCell.h" #import "XMGMessage.h" #define MAS_SHORTHAND #define MAS_SHORTHAND_GLOBALS #import "Masonry.h" @interface XMGMessageCell() @property (weak, nonatomic) IBOutlet UILabel *timeLabel; @property (weak, nonatomic) IBOutlet UIButton *textButton; @property (weak, nonatomic) IBOutlet UIImageView *iconView; @property (weak, nonatomic) IBOutlet UIButton *otherTextButton; @property (weak, nonatomic) IBOutlet UIImageView *otherIconView; @end @implementation XMGMessageCell - (void)awakeFromNib { self.textButton.titleLabel.numberOfLines = 0; self.otherTextButton.titleLabel.numberOfLines = 0; } - (void)setMessage:(XMGMessage *)message { _message = message; if (message.hideTime) { // 隐藏时间 self.timeLabel.hidden = YES; [self.timeLabel updateConstraints:^(MASConstraintMaker *make) { make.height.equalTo(0); }]; } else { // 显示时间 self.timeLabel.text = message.time; self.timeLabel.hidden = NO; [self.timeLabel updateConstraints:^(MASConstraintMaker *make) { make.height.equalTo(21); }]; } // 强制更新 [self layoutIfNeeded]; if (message.type == XMGMessageTypeMe) { // 右边 [self settingShowTextButton:self.textButton showIconView:self.iconView hideTextButton:self.otherTextButton hideIconView:self.otherIconView]; } else { // 左边 [self settingShowTextButton:self.otherTextButton showIconView:self.otherIconView hideTextButton:self.textButton hideIconView:self.iconView]; } } /** * 处理左右按钮、头像 */ - (void)settingShowTextButton:(UIButton *)showTextButton showIconView:(UIImageView *)showIconView hideTextButton:(UIButton *)hideTextButton hideIconView:(UIImageView *)hideIconView { hideTextButton.hidden = YES; hideIconView.hidden = YES; showTextButton.hidden = NO; showIconView.hidden = NO; // 设置按钮的文字 [showTextButton setTitle:self.message.text forState:UIControlStateNormal]; // 强制更新 [showTextButton layoutIfNeeded]; // 设置按钮的高度就是titleLabel的高度 [showTextButton updateConstraints:^(MASConstraintMaker *make) { CGFloat buttonH = showTextButton.titleLabel.frame.size.height + 30; make.height.equalTo(buttonH); }]; // 强制更新 [showTextButton layoutIfNeeded]; // 计算当前 cell 的高度 CGFloat buttonMaxY = CGRectGetMaxY(showTextButton.frame); CGFloat iconMaxY = CGRectGetMaxY(showIconView.frame); self.message.cellHeight = MAX(buttonMaxY, iconMaxY) + 10; } @end
- 多 Cell 布局
#import "XMGMessageCell.h" #import "XMGMessage.h" #define MAS_SHORTHAND #define MAS_SHORTHAND_GLOBALS #import "Masonry.h" @interface XMGMessageCell() @property (weak, nonatomic) IBOutlet UILabel *timeLabel; @property (weak, nonatomic) IBOutlet UIButton *textButton; @property (weak, nonatomic) IBOutlet UIImageView *iconView; @end @implementation XMGMessageCell - (void)awakeFromNib { self.textButton.titleLabel.numberOfLines = 0; } - (void)setMessage:(XMGMessage *)message { _message = message; // 时间处理 if (message.hideTime) { // 隐藏时间 self.timeLabel.hidden = YES; [self.timeLabel updateConstraints:^(MASConstraintMaker *make) { make.height.equalTo(0); }]; } else { // 显示时间 self.timeLabel.text = message.time; self.timeLabel.hidden = NO; [self.timeLabel updateConstraints:^(MASConstraintMaker *make) { make.height.equalTo(21); }]; } // 处理显示的消息文字 // 设置按钮的文字 [self.textButton setTitle:self.message.text forState:UIControlStateNormal]; // 强制更新 [self layoutIfNeeded]; // 设置按钮的高度就是titleLabel的高度 [self.textButton updateConstraints:^(MASConstraintMaker *make) { CGFloat buttonH = self.textButton.titleLabel.frame.size.height + 30; make.height.equalTo(buttonH); }]; // 强制更新 [self layoutIfNeeded]; // 计算当前cell的高度 CGFloat buttonMaxY = CGRectGetMaxY(self.textButton.frame); CGFloat iconMaxY = CGRectGetMaxY(self.iconView.frame); self.message.cellHeight = MAX(buttonMaxY, iconMaxY) + 10; } @end
- 单 Cell 布局
- XMGChatingViewController.m
#import "XMGChatingViewController.h" #import "XMGMessage.h" #import "XMGMessageCell.h" @interface XMGChatingViewController () <UITableViewDataSource, UITableViewDelegate> @property (nonatomic, strong) NSArray *messages; @end @implementation XMGChatingViewController - (NSArray *)messages { if (_messages == nil) { // 加载plist中的字典数组 NSString *path = [[NSBundle mainBundle] pathForResource:@"messages.plist" ofType:nil]; NSArray *dictArray = [NSArray arrayWithContentsOfFile:path]; // 字典数组 -> 模型数组 NSMutableArray *messageArray = [NSMutableArray array]; // 用来记录上一条消息模型 XMGMessage *lastMessage = nil; for (NSDictionary *dict in dictArray) { XMGMessage *message = [XMGMessage messageWithDict:dict]; //埋下伏笔,加载数据时,判断哪个时间值相等。 message.hideTime = [message.time isEqualToString:lastMessage.time]; [messageArray addObject:message]; lastMessage = message; } _messages = messageArray; } return _messages; } #pragma mark - <UITableViewDataSource> - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return self.messages.count; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { XMGMessageCell *cell = [tableView dequeueReusableCellWithIdentifier:@"message"]; cell.message = self.messages[indexPath.row]; return cell; } #pragma mark - <UITableViewDelegate> - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath { return 200; } - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { XMGMessage *message = self.messages[indexPath.row]; return message.cellHeight; } @end
- 效果
- XMGMessage.h
14、tableView 的协议方法
- 需遵守协议 UITableViewDataSource, UITableViewDelegate,并设置代理
- UITableViewDelegate 继承自 UIScrollViewDelegate
@protocol UITableViewDelegate<NSObject, UIScrollViewDelegate>
14.1 UITableViewDataSource 和 UITableViewDelegate 协议方法
- Objective-C
- 分段、行 设置
// 设置分段数,设置 tableView 有多少个分段 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return myDataArray.count; } // 设置行数,设置 tableView 中每段中有多少行,section 就是第几分段 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return [[myDataArray objectAtIndex:section] count]; } // 设置行高 ,默认为 44 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { return 60; } // 设置估计行高 - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath { /* 只要返回了估计高度,那么就会先调用 tableView:cellForRowAtIndexPath: 方法创建 cell, 再调用 tableView:heightForRowAtIndexPath: 方法获取 cell 的真实高度, 并且显示一个 cell,调用一次 tableView:heightForRowAtIndexPath: 方法。 如果不返回估计高度,会先调用 tableView:heightForRowAtIndexPath: 方法, 再调用 tableView:heightForRowAtIndexPath: 方法, 并且一次性全部调用总 cell 数量次 tableView:heightForRowAtIndexPath: 方法。 */ return 60; } // 设置每一行显示的内容,每当有一个 cell 进入视野范围内就会调用 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { return cell; }
- 分段的头、脚标题 设置
// 设置分段的头标题高度 - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section { return 40; } // 设置分段的脚标题高度 - (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section { return 30; } // 设置分段的头标题估计高度 - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForHeaderInSection:(NSInteger)section { return 40; } // 设置分段的脚标题估计高度 - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForFooterInSection:(NSInteger)section { return 30; } // 设置分段的头标题内容 - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { if (0 == section) { return @"1 Header"; } else{ return @"2 rHeader"; } } // 设置分段的脚标题内容 - (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section { if (0 == section) { return @"2 Footer"; } else{ return @"2 Footer"; } } // 设置分段头标题视图,返回自定义的标题视图 - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section { return myView; } // 设置分段脚标题视图,返回自定义的标题视图 - (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section { return myView; }
- 分段索引条 设置
// 创建索引条 - (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView { return array; } // 设置索引条偏移量,默认索引条与分段一一对应时,可以不写该方法 - (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index { /* 点击索引条上字符串的时候 tableView 会跳转到对应的分段,是根据位置计算的,点击索引条上第几个,tableView 就会跳到第几段。 如果索引条的前面加了个搜索小图标等,需要重写这个方法。 */ }
- 表格点击 设置
// 表格选中点击响应事件,表格被选中 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { } // 表格取消选中点击响应事件,表格被取消选中 - (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath { } // 附属控件 button 点击响应事件 - (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath { // 如果系统自带的附属控件里有 button ,附属控件的点击事件会独立出来 }
- 表格编辑 设置
// 表格删除、插入 - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { // 表格删除或插入,默认为删除模式,写入该方法即表示允许删除。 } // 设置编辑模式 - (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath { // 删除、插入、多选删除,不设置默认时为删除 } // 修改左滑删除按钮的内容 - (NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath { return @"删除"; } // 设置左滑多按钮 - (NSArray *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath { // 按钮从右向左的顺序排列 return @[action1, action0]; } // 表格移动 - (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath { }
- 分段、行 设置
- Swift
- 分段、行 设置
// 设置分段数,设置 tableView 有多少个分段 func numberOfSectionsInTableView(tableView: UITableView) -> Int { return myDataArray.count } // 设置行数,设置 tableView 中每段中有多少行,section 就是第几分段 func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return myDataArray[section].count } // 设置行高,默认为 44 func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { return 60 } // 设置估计行高 func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { /* 只要返回了估计高度,那么就会先调用 tableView:cellForRowAtIndexPath: 方法创建 cell, 再调用 tableView:heightForRowAtIndexPath: 方法获取 cell 的真实高度, 并且显示一个 cell,调用一次 tableView:heightForRowAtIndexPath: 方法。 如果不返回估计高度,会先调用 tableView:heightForRowAtIndexPath: 方法, 再调用 tableView:heightForRowAtIndexPath: 方法, 并且一次性全部调用总 cell 数量次 tableView:heightForRowAtIndexPath: 方法。 */ return 60 } // 设置每一行显示的内容,每当有一个 cell 进入视野范围内就会调用 func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { return cell }
- 分段的头、脚标题 设置
// 设置分段的头标题高度 func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { return 40 } // 设置分段的脚标题高度 func tableView(tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat { return 30 } // 设置分段的头标题估计高度 func tableView(tableView: UITableView, estimatedHeightForHeaderInSection section: Int) -> CGFloat { return 40 } // 设置分段的脚标题估计高度 func tableView(tableView: UITableView, estimatedHeightForFooterInSection section: Int) -> CGFloat { return 30 } // 设置分段的头标题内容 func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? { if 0 == section { return "1 Header" } else { return "2 Header" } } // 设置分段的脚标题内容 func tableView(tableView: UITableView, titleForFooterInSection section: Int) -> String? { if 0 == section { return "1 Footer" } else { return "2 Footer" } } // 设置分段头标题视图,返回自定义的标题视图 func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { return myView } // 设置分段脚标题视图,返回自定义的标题视图 func tableView(tableView: UITableView, viewForFooterInSection section: Int) -> UIView? { return myView }
- 分段索引条 设置
// 创建索引条 func sectionIndexTitlesForTableView(tableView: UITableView) -> [String]? { return array } // 设置索引条偏移量,默认索引条与分段一一对应时,可以不写该方法 func tableView(tableView: UITableView, sectionForSectionIndexTitle title: String, atIndex index: Int) -> Int { /* 点击索引条上字符串的时候 tableView 会跳转到对应的分段,是根据位置计算的,点击索引条上第几个,tableView 就会跳到第几段。 如果索引条的前面加了个搜索小图标等,需要重写这个方法。 */ }
- 表格点击 设置
// 表格选中点击响应事件,表格被选中 func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { } // 表格取消选中点击响应事件,表格被取消选中 func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) { } // 附属控件 button 点击响应事件 func tableView(tableView: UITableView, accessoryButtonTappedForRowWithIndexPath indexPath: NSIndexPath) { // 如果系统自带的附属控件里有 button ,附属控件的点击事件会独立出来 }
- 表格编辑 设置
// 表格删除、插入 func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) { // 表格删除或插入,默认为删除模式,写入该方法即表示允许删除。 } // 设置编辑模式 func tableView(tableView: UITableView, editingStyleForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCellEditingStyle { // 删除、插入、多选删除,不设置默认时为删除 } // 修改左滑删除按钮的内容 func tableView(tableView: UITableView, titleForDeleteConfirmationButtonForRowAtIndexPath indexPath: NSIndexPath) -> String? { return "删除" } // 设置左滑多按钮 func tableView(tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? { // 按钮从右向左的顺序排列 return [action1, action0] } // 表格移动 func tableView(tableView: UITableView, moveRowAtIndexPath sourceIndexPath: NSIndexPath, toIndexPath destinationIndexPath: NSIndexPath) { }
- 分段、行 设置
14.2 UIScrollViewDelegate 协议方法
- Objective-C
- 拖拽
// 将要开始拖拽 - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { } // 将要结束拖拽 - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset { } // 已经结束拖拽,decelerate 松手后 是否有惯性滚动 0:没有,1:有 - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate { }
- 滚动
// 滚动过程中,只要滚动就会触发 - (void)scrollViewDidScroll:(UIScrollView *)scrollView { } // 已经结束滚动,滚动动画停止时执行,代码改变时触发,也就是 setContentOffset 改变时 - (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView { }
- 惯性滚动
// 将要开始惯性滚动 - (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView { } // 已经结束惯性滚动 - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { }
- 滚到顶端
// 设置点击状态栏时是否滚到顶端 - (BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView { return YES; } // 已经滚到顶端,点击状态栏时调用 - (void)scrollViewDidScrollToTop:(UIScrollView *)scrollView { }
- 缩放
// 设置被缩放的空间,一个 scrollView 中只能有一个子控件被缩放,如果有很多个子控件缩放时会引起错乱 - (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView { return [scrollView.subviews[0] viewWithTag:100]; } // 将要开始缩放 - (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view { } // 已经结束缩放 - (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale { } // 缩放过程中,只要缩放就会触发 - (void)scrollViewDidZoom:(UIScrollView *)scrollView { }
- 拖拽
- Swift
- 拖拽
// 将要开始拖拽 func scrollViewWillBeginDragging(scrollView: UIScrollView) { } // 将要结束拖拽 func scrollViewWillEndDragging(scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) { } // 已经结束拖拽,decelerate 松手后 是否有惯性滚动 0:没有,1:有 func scrollViewDidEndDragging(scrollView: UIScrollView, willDecelerate decelerate: Bool) { }
- 滚动
// 滚动过程中,只要滚动就会触发,只要滚动就会触发 func scrollViewDidScroll(scrollView: UIScrollView) { } // 已经结束滚动,滚动动画停止时执行,代码改变时触发,也就是 setContentOffset 改变时 func scrollViewDidEndScrollingAnimation(scrollView: UIScrollView) { }
- 惯性滚动
// 将要开始惯性滚动 func scrollViewWillBeginDecelerating(scrollView: UIScrollView) { } // 已经结束惯性滚动 func scrollViewDidEndDecelerating(scrollView: UIScrollView) { }
- 滚到顶端
// 设置点击状态栏时是否滚到顶端 func scrollViewShouldScrollToTop(scrollView: UIScrollView) -> Bool { return true } // 已经滚到顶端,点击状态栏时调用 func scrollViewDidScrollToTop(scrollView: UIScrollView) { }
- 缩放
// 设置被缩放的空间,一个 scrollView 中只能有一个子控件被缩放,如果有很多个子控件缩放时会引起错乱 func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? { return scrollView.subviews[0].viewWithTag(100) } // 将要开始缩放 func scrollViewWillBeginZooming(scrollView: UIScrollView, withView view: UIView?) { } // 已经结束缩放 func scrollViewDidEndZooming(scrollView: UIScrollView, withView view: UIView?, atScale scale: CGFloat) { } // 缩放过程中,只要缩放就会触发 func scrollViewDidZoom(scrollView: UIScrollView) { }
- 拖拽
15、Storyboard 中设置
- 在 Storyboard 场景中设置
- Table View Controller
Selection 选项 -- Clear on Appearance Refreshing UIRefreshControl 刷新设置 - Table View
Content 设置表格 Cell 类型 -- Dynamic Prototypes 动态 Cell,可以设置自定义 Cell -- Static Cells 静态 Cell,cell 的数量和内容无需(或大部分不需要)做动态的变化 Sections 设为静态 Cell 时,设置分段数 Prototype Cells 设为动态 Cell 时,设置不同类型表格的数量,需设置不同的 Identifier Style 设置表格类型 Plain 悬浮显示 Grouped 跟随移动,多余的表格将自动清除 Separator 设置分割线类型/颜色 Separator Insets 设置分割线边距 Selection 设置表格选择类型,不允许选择/单选/多选 Editing 设置编辑状态时表格的选择类型 Show Selection on Touch 显示选择 Index Row Limit Text Background - Table View Cell
Style 设置 Cell 的类型 Image 设置 Cell 显示的图片 Identifier 设置 Cell 的复用 ID Selection 设置 Cell 点击时的颜色 Accessory 设置 Cell 附属控件类型 Editing Acc. Indentation Separator 设置分割线边距 - 在 Storyboard 场景绑定的 Controller 中设置
@interface TableViewController : UITableViewController self.tableView.backgroundColor = [UIColor greenColor];
- Table View Controller
时间: 2024-10-05 00:44:26