在iOS中,要实现表格数据展示,最常用的做法就是使用UITableView。UITableView继承自UIScrollView,因此支持垂直滚动,而且性能极佳。
UITableView有两种样式:
- 一列显示:UITableViewStylePlain
- 分组显示:UITableViewStyleGrouped
tableView展示数据的过程
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView;
2.调用数据源的下面方法得知每一组有多少行数据
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
3.调用数据源的下面方法得知每一行显示什么内容
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
常见的属性
// 表格行线条的颜色 self.tableView.separatorColor = [UIColor colorWithRed:255/255.0 green:255/255.0 blue:0 alpha:255/255.0]; // 显示表格行线条的样式,UITableViewCellSeparatorStyleNone为没有线条 self.tableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine; // 表格的头部控件(直接显示表格的最顶部),一般用来显示广告 self.tableView.tableHeaderView = [UIButton buttonWithType:UIButtonTypeContactAdd]; // 表格的底部控件,一般用来加载更多数据 self.tableView.tableFooterView = [[UISwitch alloc] init];
// 行选中为透明
cell.selectionStyle=UITableViewCellSeparatorStyleNone;
Cell的重用代码
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { // 1.定义一个cell的标识。static修饰局部变量:可以保证局部变量只分配一次存储空间(只初始化一次) static NSString *ID = @"mjcell"; // 2.从缓存池中取出cell UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID]; // 3.如果缓存池中没有cell if (cell == nil) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID]; } // 4.设置cell的属性... return cell; }
右边索引条
已经有相应的数据源,实现即可
/** * 返回右边索引条显示的字符串数据 */ - (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView { return [self.groups valueForKeyPath:@"title"]; }
效果:
刷新表格
// 全局刷新(每一行都会重新刷新) - (void)reloadData; // 局部刷新(使用前提: 刷新前后, 模型数据的个数不变) - (void)reloadRows:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation; // 局部删除(使用前提: 模型数据减少的个数 == indexPaths的长度) - (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation;
实例:
// 全部刷新 [self.tableView reloadData]; // 局部刷新,row指的是第几行,inSection指的是第几组 NSIndexPath *path = [NSIndexPath indexPathForRow:row inSection:0]; [self.tableView reloadRowsAtIndexPaths:@[path] withRowAnimation:UITableViewRowAnimationBottom];
滑动删除与新增
只需要实现tableview代理即可
/** * 当tableView进入编辑状态的时候会调用,询问每一行进行怎样的操作(添加\删除) */ - (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath { return UITableViewCellEditingStyleDelete; //return indexPath.row %2 ? UITableViewCellEditingStyleDelete : UITableViewCellEditingStyleInsert; }
如果显示的是英文Delete,可以通过设置模拟器的语言,同时增加中文语言
选中InfoPlist.strings和InfoPlist.strings
监听点击滑动删除按钮
/** * 如果实现了这个方法,就自动实现了滑动删除的功能 * 点击了删除按钮就会调用 * 提交了一个编辑操作就会调用(操作:删除\添加) * @param editingStyle 编辑的行为 * @param indexPath 操作的行号 */ - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { if (editingStyle == UITableViewCellEditingStyleDelete) { // 提交的是删除操作 // 1.删除模型数据 [self.contacts removeObjectAtIndex:indexPath.row]; // 2.刷新表格 // 局部刷新某些行(使用前提:模型数据的行数不变) [self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationTop]; // 3.归档 [NSKeyedArchiver archiveRootObject:self.contacts toFile:MJContactsFilepath]; } else if (editingStyle == UITableViewCellEditingStyleInsert) { // 1.修改模型数据 MJContact *contact = [[MJContact alloc] init]; contact.name = @"jack"; contact.phone = @"10086"; [self.contacts insertObject:contact atIndex:indexPath.row + 1]; // 2.刷新表格,使用局部刷新 NSIndexPath *nextPath = [NSIndexPath indexPathForRow:indexPath.row + 1 inSection:0]; [self.tableView insertRowsAtIndexPaths:@[nextPath] withRowAnimation:UITableViewRowAnimationBottom]; // [self.tableView reloadData]; } }
其它属性
// 2.设置每一组头部尾部的高度
self.tableView.sectionFooterHeight = 0;
self.tableView.sectionHeaderHeight = 10;
// 3.设置背景色
self.tableView.backgroundView = nil;
self.tableView.backgroundColor = IWGlobalBgColor;
常用实例:
#pragma mark - 初始化界面 - (void)setupTableView { _tableView= [[UITableView alloc] initWithFrame:CGRectMake(0, 0, kScreenWidth, kScreenHeight) style:UITableViewStyleGrouped];; _tableView.dataSource = self; _tableView.delegate=self; _tableView.separatorStyle = UITableViewCellSeparatorStyleNone; _tableView.tableHeaderView = [self tableHeaderView]; _tableView.tableFooterView = [UIView new]; [self.view addSubview:_tableView]; } // 初始化头部 - (UIView *)tableHeaderView { } #pragma mark - UITableViewDataSource数据源方法 // 返回分组数 -(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{ NSLog(@"计算分组数"); return 1; } // 返回每组行数 -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ NSLog(@"计算每组(组%i)行数",section); return group1.contacts.count; } // 返回每行的单元格 -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ static NSString *ID = @"RFMeTableViewCell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID]; if (cell == nil) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID]; } cell.textLabel.text = @"文字"; cell.imageView.image = [UIImage imageNamed:@"me_phone_icon"]; return cell; } // 返回每组头标题名称 -(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{ NSLog(@"生成组(组%i)名称",section); return group.name; } // 返回每组尾部说明 -(NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section{ NSLog(@"生成尾部(组%i)详情",section); return group.detail; } #pragma mark - UITableViewDelegate 代理方法 // 设置分组标题内容高度 -(CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section{ if(section==0){ return 50; } return 40; } // 设置每行高度(每行高度可以不一样) -(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{ return 45; } // 设置尾部说明内容高度 -(CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section{ return 40; } // 点击行 -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ }
点击效果
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath*)indexPath { //当手指离开某行时,就让某行的选中状态消失 [tableView deselectRowAtIndexPath:indexPath animated:YES]; }
TableView样式
如果带有图标,我们使用系统自带的tableviewCell.ImageView.image来设置的话,会发现,底下的这条线没有包括图标,类型QQ的效果,如:
现在有一个需求,是改成类型微信的显示效果,底部是线包括图标,如:
我们会发现,当tableviewcell.imageView.image 为空的时候,横线是到图标的这个位置的。因而,简单的做法是,不使用系统的,自定义一个imageview,并设置tableviewcell.textLabel的X值。
// 1.设置基本数据 if (item.icon) { _iconImage.image = [UIImage imageNamed:item.icon]; } else { _iconImage.frame = CGRectZero; } //self.imageView.image = [UIImage imageNamed:item.icon]; self.textLabel.text = item.title; self.detailTextLabel.text = item.subtitle;
改为textLable的X值
- (void)layoutSubviews { [super layoutSubviews]; // 调整子标题的x self.detailTextLabel.x = CGRectGetMaxX(self.textLabel.frame) + 5; if (_item.icon) { self.textLabel.x = 40; } }
参考完成代码:
// // MeCommonCell.m // 99SVR // // Created by jiangys on 16/6/2. // Copyright © 2016年 xia zhonglin . All rights reserved. // #import "MeCommonCell.h" #import "CommonCellLabelItem.h" #import "CommonCellArrowItem.h" @interface MeCommonCell() /** 箭头 */ @property (nonatomic, strong) UIImageView *rightArrow; /** 标签 */ @property (nonatomic, strong) UILabel *rightLabel; /** 图标 这里不使用自带的self.imageView.image,因为底部横线不会包括自带的图标*/ @property(nonatomic, strong) UIImageView *iconImage; @end @implementation MeCommonCell #pragma mark - 懒加载右边的view - (UIImageView *)rightArrow { if (_rightArrow == nil) { UIImageView *arrowImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"arrow"]]; arrowImageView.size = (CGSize){13,13}; self.rightArrow = arrowImageView; } return _rightArrow; } - (UILabel *)rightLabel { if (_rightLabel == nil) { self.rightLabel = [[UILabel alloc] init]; self.rightLabel.textColor = COLOR_Text_Gay; self.rightLabel.font = Font_14; } return _rightLabel; } #pragma mark - 初始化 + (instancetype)cellWithTableView:(UITableView *)tableView { static NSString *ID = @"MeCommonCell"; MeCommonCell *cell = [tableView dequeueReusableCellWithIdentifier:ID]; if (cell == nil) { cell = [[MeCommonCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:ID]; } return cell; } - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; if (self) { _iconImage = [[UIImageView alloc] initWithFrame:CGRectMake(15,0, 17, self.height)]; _iconImage.contentMode = UIViewContentModeScaleAspectFit; [self.contentView addSubview:_iconImage]; // 设置标题的字体 self.textLabel.font = Font_15; self.textLabel.textColor = COLOR_Text_Black; self.detailTextLabel.font = Font_14; // 设置cell的默认背景色 self.backgroundColor = [UIColor whiteColor]; } return self; } #pragma mark - 调整子控件的位置 - (void)layoutSubviews { [super layoutSubviews]; // 调整子标题的x self.detailTextLabel.x = CGRectGetMaxX(self.textLabel.frame) + 5; if (_item.icon) { self.textLabel.x = 40; } } - (void)setItem:(CommonCellItem *)item { _item = item; // 1.设置基本数据 if (item.icon) { _iconImage.image = [UIImage imageNamed:item.icon]; } else { _iconImage.frame = CGRectZero; } //self.imageView.image = [UIImage imageNamed:item.icon]; self.textLabel.text = item.title; self.detailTextLabel.text = item.subtitle; // 2.设置右边的内容 if ([item isKindOfClass:[CommonCellArrowItem class]]) { self.accessoryView = self.rightArrow; } else if ([item isKindOfClass:[CommonCellLabelItem class]]) { CommonCellLabelItem *labelItem = (CommonCellLabelItem *)item; // 设置文字 self.rightLabel.text = labelItem.text; // 根据文字计算尺寸 self.rightLabel.size = [labelItem.text sizeMakeWithFont:self.rightLabel.font]; self.accessoryView = self.rightLabel; } else { // 取消右边的内容 self.accessoryView = nil; } } @end
网摘一些小总结:
//这句话不显示多余的单元格 self.tableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero]; // 这句话不显示单元格之间的分割线 _tableView.separatorStyle = UITableViewCellSeparatorStyleNone; // 这句话在单元格的最后显示一个箭头 cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; //--- 点击cell的时候 cell不会产生高亮状态 --- cell.selectionStyle = UITableViewCellSelectionStyleNone; // --- 写在viewdidload里面cell自适应高度 ---- tableView.rowHeight = UITableViewAutomaticDimension; // 自适应单元格高度 tableView.estimatedRowHeight = 50; //先估计一个高度 // 去掉UItableview headerview黏性(sticky) - (void)scrollViewDidScroll:(UIScrollView *)scrollView { CGFloat sectionHeaderHeight = 40; if (scrollView.contentOffset.y<=sectionHeaderHeight&&scrollView.contentOffset.y>=0) { scrollView.contentInset = UIEdgeInsetsMake(-scrollView.contentOffset.y, 0, 0, 0); } else if (scrollView.contentOffset.y>=sectionHeaderHeight) { scrollView.contentInset = UIEdgeInsetsMake(-sectionHeaderHeight, 0, 0, 0); } } // 获取手势点击的是哪一个cell的坐标 NSIndexPath *indexPath = [self.tableView indexPathForCell:((UITableViewCell *)longPress.view)]; // 局部刷新 //一个section刷新 NSIndexSet *indexSet=[[NSIndexSet alloc]initWithIndex:2]; [tableview reloadSections:indexSet withRowAnimation:UITableViewRowAnimationAutomatic]; //一个cell刷新 NSIndexPath *indexPath=[NSIndexPath indexPathForRow:3 inSection:0]; [tableView reloadRowsAtIndexPaths:[NSArray arrayWithObjects:indexPath,nil] withRowAnimation:UITableViewRowAnimationNone]; // 关于UITableView如何跳转到最后一行或者任意指定行。 其实现如下: [self.chatTableView scrollToRowAtIndexPath: [NSIndexPath indexPathForRow:[self.chatArray count]-1 inSection:0] atScrollPosition: UITableViewScrollPositionBottom animated:NO]; // 点击button时获取button所在的cell的indexpath UIButton *button = sender; HeroDermaTableViewCell *cell = (HeroDermaTableViewCell *)[[button superview] superview]; NSIndexPath *indexPath = [_tableView indexPathForCell:cell];
分组状态下,顶部留白,解决办法
table.tableHeaderView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, CGFLOAT_MIN)];