UITableView
基本介绍
UITableView有两种风格:UITableViewStylePlain和UITableViewStyleGrouped。这两者操作起来其实并没有本质区别,只是后者按分组样式显示前者按照普通样式显示而已。
在UITableView中数据只有行的概念,并没有列的概念,因为在手机操作系统中显示多列是不利于操作的。UITableView中每行数据都是一个UITableViewCell,在这个控件中为了显示更多的信息,iOS已经在其内部设置好了多个子控件以供开发者使用。如果我们查看UITableViewCell的声明文件可以发现在内部有一个UIView控件(contentView,作为其他元素的父控件)、两个UILable控件(textLabel、detailTextLabel)、一个UIImage控件(imageView),分别用于容器、显示内容、详情和图片。使用效果类似于微信、QQ信息列表。
当然,这些子控件并不一定要全部使用,具体操作时可以通过UITableViewCellStyle进行设置,具体每个枚举表示的意思已经在代码中进行了注释:
typedef NS_ENUM(NSInteger, UITableViewCellStyle) {
UITableViewCellStyleDefault, // 左侧显示textLabel(不显示detailTextLabel),imageView可选(显示在最左边)
UITableViewCellStyleValue1, // 左侧显示textLabel、右侧显示detailTextLabel(默认蓝色),imageView可选(显示在最左边)
UITableViewCellStyleValue2, // 左侧依次显示textLabel(默认蓝色)和detailTextLabel,imageView可选(显示在最左边)
UITableViewCellStyleSubtitle // 左上方显示textLabel,左下方显示detailTextLabel(默认灰色),imageView可选(显示在最左边)
};
数据源
由于iOS是遵循MVC模式设计的,很多操作都是通过代理和外界沟通的,但对于数据源控件除了代理还有一个数据源属性,通过它和外界进行数据交互。 对于UITableView设置完dataSource后需要实现UITableViewDataSource协议,在这个协议中定义了多种 数据操作方法,下面通过创建一个简单的联系人管理进行演示:
首先我们需要创建一个联系人模型KCContact(通讯录)
KCContact.h
#import <Foundation/Foundation.h>
//联系人模型
@interface KCContact : NSObject
#pragma mark 姓
@property (nonatomic,copy) NSString *firstName;
#pragma mark 名
@property (nonatomic,copy) NSString *lastName;
#pragma mark 手机号码
@property (nonatomic,copy) NSString *phoneNumber;
#pragma mark 带参数的构造函数
-(KCContact *)initWithFirstName:(NSString *)firstName andLastName:(NSString *)laseName andPhoneNumber:(NSString *)phoneNumber;
#pragma mark 取得姓名
-(NSString *)getName;
#pragma mark 带参数的静态对象初始化方法
+(KCContact *)initWithFirstName:(NSString *)firstName andLastName:(NSString *)laseName andPhoneNumber:(NSString *)phoneNumber;
@end
KCContact.m
#import "KCContact.h"
@implementation KCContact
//得到数据源的姓和名的方法
-(KCContact *)initWithFirstName:(NSString *)firstName andLastName:(NSString *)laseName andPhoneNumber:(NSString *)phoneNumber{
if (self = [super init]) {
self.firstName = firstName;
self.lastName = laseName;
self.phoneNumber = phoneNumber;
}
return self;
}
//拼接 姓+名 的方法
-(NSString *)getName{
return [NSString stringWithFormat:@"%@%@", _firstName,_lastName];
}
+(KCContact *)initWithFirstName:(NSString *)firstName andLastName:(NSString *)laseName andPhoneNumber:(NSString *)phoneNumber{
KCContact *contact1 = [[KCContact alloc]initWithFirstName:firstName andLastName:laseName andPhoneNumber:phoneNumber];
return contact1;
}
@end
联系人分组模型(比如姓刘的,姓张的都各自是一组)KCContactGroup
KCContactGroup.h
#import <Foundation/Foundation.h>
#import "KCContact.h"
//联系人分组模型
@interface KCContactGroup : NSObject
#pragma mark 组名
@property (nonatomic,copy) NSString *name;
#pragma mark 分组描述
@property (nonatomic,copy) NSString *detail;
#pragma mark 联系人
@property (nonatomic,strong) NSMutableArray *contacts;
#pragma mark 带参数的构造函数
-(KCContactGroup *)initWithName:(NSString *)name andDetail:(NSString *)detail andContacts:(NSMutableArray *)contacts;
#pragma mark 静态初始化方法
+(KCContactGroup *)initWithName:(NSString *)name andDetail:(NSString *)detail andContacts:(NSMutableArray *)contacts;
@end
KCContactGroup.m
#import "KCContactGroup.h"
@implementation KCContactGroup
//拿到父类的分组
-(KCContactGroup *)initWithName:(NSString *)name andDetail:(NSString *)detail andContacts:(NSMutableArray *)contacts{
if (self = [super init]) {
self.name = name;
self.detail = detail;
self.contacts = contacts;
}
return self;
}
+(KCContactGroup *)initWithName:(NSString *)name andDetail:(NSString *)detail andContacts:(NSMutableArray *)contacts{
KCContactGroup *group1 = [[KCContactGroup alloc]initWithName:name andDetail:detail andContacts:contacts];
return group1;
}
@end
然后在viewDidLoad方法中创建一些模拟数据同时实现数据源协议方法:
ViewController.h
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController<UIAlertViewDelegate,UITableViewDelegate,UITableViewDataSource>
@end
ViewController.m
#import "ViewController.h"
#import "KCContact.h"
#import "KCContactGroup.h"
@interface ViewController (){
UITableView *myTableView;//新建一个UITableView
NSMutableArray *contacts;//联系人模型
NSIndexPath *selectedIndexPath;//当前选中的组和行
UIToolbar *myToolbar;
BOOL isInsert;//记录是点击了插入还是删除按钮
}
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//初始化数据(!!!很重要)
[self initData];
//创建一个分组样式的UITableView
myTableView = [[UITableView alloc]initWithFrame:self.view.bounds style:UITableViewStyleGrouped];
//设置数据源,注意必须实现对应的UITableViewDataSource协议
myTableView.dataSource = self;
myTableView.delegate = self;
[self.view addSubview:myTableView];
/**
UITableView和UITableViewCell提供了强大的操作功能,这一节中会重点讨论删除、增加、排序等操作。为了方便演示我们还是在之前的通讯录的基础上演示,在此之前先来给视图控制器添加一个工具条,在工具条左侧放一个删除按钮,右侧放一个添加按钮:
*/
myToolbar = [[UIToolbar alloc]initWithFrame:CGRectMake(0, 0, [[UIScreen mainScreen]bounds].size.width, 64)];
myToolbar.backgroundColor = [UIColor redColor];
// [self.view addSubview:myToolbar];
//添加删除按钮
UIBarButtonItem *removeButton = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemTrash target:self action:@selector(remove:)];
//间距按钮
UIBarButtonItem *flexibleButton = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
//添加按钮
UIBarButtonItem *addButton = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(add:)];
NSArray *ButtonArray = [NSArray arrayWithObjects:removeButton, flexibleButton, addButton, nil];
// myToolbar.items = ButtonArray;
}
#pragma mark 加载数据
-(void)initData{
contacts = [[NSMutableArray alloc]init];
KCContact *contact1 = [KCContact initWithFirstName:@"liu" andLastName:@"bo" andPhoneNumber:@"18220480259"];
KCContact *contact2 = [KCContact initWithFirstName:@"wang" andLastName:@"zhaohua" andPhoneNumber:@"18792487043"];
KCContactGroup *group1 = [KCContactGroup initWithName:@"jiaren" andDetail:@"womenshiyijiaren" andContacts:[NSMutableArray arrayWithObjects:contact1, contact2, nil]];
[contacts addObject:group1];
KCContact *contact3 = [KCContact initWithFirstName:@"zhang" andLastName:@"bing" andPhoneNumber:@"12345678900"];
KCContact *contact4 = [KCContact initWithFirstName:@"wu" andLastName:@"wei" andPhoneNumber:@"98765432100"];
KCContact *contact5 = [KCContact initWithFirstName:@"zhang" andLastName:@"san" andPhoneNumber:@"12345612345"];
KCContactGroup *group2 = [KCContactGroup initWithName:@"pengyou" andDetail:@"womenshipengyou" andContacts:[NSMutableArray arrayWithObjects:contact3, contact4, contact5, nil]];
[contacts addObject:group2];
KCContact *contact6 = [KCContact initWithFirstName:@"li" andLastName:@"si" andPhoneNumber:@"12345555555"];
KCContact *contact7 = [KCContact initWithFirstName:@"wang" andLastName:@"wu" andPhoneNumber:@"88888888888"];
KCContactGroup *group3 = [KCContactGroup initWithName:@"tongxue" andDetail:@"womenshitongxue" andContacts:[NSMutableArray arrayWithObjects:contact6, contact7, nil]];
[contacts addObject:group3];
KCContact *contact8 = [KCContact initWithFirstName:@"hi" andLastName:@"pi" andPhoneNumber:@"12345555555"];
KCContact *contact9 = [KCContact initWithFirstName:@"kang" andLastName:@"gu" andPhoneNumber:@"88888888888"];
KCContact *contact10 = [KCContact initWithFirstName:@"roi" andLastName:@"sfi" andPhoneNumber:@"12345555555"];
KCContact *contact11 = [KCContact initWithFirstName:@"fang" andLastName:@"ii" andPhoneNumber:@"88888888888"];
KCContact *contact12 = [KCContact initWithFirstName:@"tyng" andLastName:@"rd" andPhoneNumber:@"88888888888"];
KCContactGroup *group4 = [KCContactGroup initWithName:@"qinqi" andDetail:@"womenshiqinqi" andContacts:[NSMutableArray arrayWithObjects:contact8, contact9, contact10, contact11, contact12, nil]];
[contacts addObject:group4];
KCContact *contact13 = [KCContact initWithFirstName:@"roi" andLastName:@"sfi" andPhoneNumber:@"12345555555"];
KCContact *contact14 = [KCContact initWithFirstName:@"fang" andLastName:@"ii" andPhoneNumber:@"88888888888"];
KCContact *contact15 = [KCContact initWithFirstName:@"tyng" andLastName:@"rd" andPhoneNumber:@"88888888888"];
KCContactGroup *group5 = [KCContactGroup initWithName:@"tongshi" andDetail:@"womenshitongshi" andContacts:[NSMutableArray arrayWithObjects:contact13, contact14, contact15, nil]];
[contacts addObject:group5];
}
#pragma mark - 数据源代理方法
#pragma mark 返回分组数
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
NSLog(@"计算分组数");
return contacts.count;
}
#pragma mark 返回每组行数
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
NSLog(@"计算每组(组%li)行数",(long)section);
KCContactGroup * group1 = contacts[section];
return group1.contacts.count;
}
#pragma mark 返回每行的单元格
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
//NSIndexPath是一个结构体,记录了组和行的信息
NSLog(@"生成单元格(组:%li 行:%li)",(long)indexPath.section,(long)indexPath.row);
KCContactGroup *group = contacts[indexPath.section];
KCContact *contact = group.contacts[indexPath.row];
// //每行cell上面的内容(----------------普通方法创建cell---------------------)
// UITableViewCell *cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:nil];
// /**
// (----------------优化方法创建cell---------------------)
// * 优化UITableView,这时我们不是在滚动到指定位置后更改滚动的位置而是要将当前没有显示的Cell重新显示在将要显示的Cell的位置然后更新其内容。原因就是UITableView中的Cell结构布局可能是不同的,通过重新定位是不可取的,而是需要重用已经不再界面显示的已创建过的Cell。
//
// UITableView已经为我们实现了这种机制。在UITableView内部有一个缓存池,初始化时使用initWithStyle:(UITableViewCellStyle) reuseIdentifier:(NSString *)方法指定一个可重用标识,就可以将这个cell放到缓存池。然后在使用时使用指定的标识去缓存池中取得对应的cell然后修改cell内容即可。
// */
// //由于此方法调用十分频繁,cell的 标示 声明成 静态变量 有助于性能优化
// static NSString *cellIdentifier = @"UITanleViewCellIdentifierKey1";
// //首先根据标识去缓存池取
// UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
// //如果缓存池没有则重新创建,并放到缓存池中
// if (!cell) {
// cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:cellIdentifier];
// }
/**
* UITableViewCell右侧可以显示不同的图标,在iOS中称之为访问器,点击可以触发不同的事件,例如通讯录右边的 联系人详情按钮 ,或者 声音,Wifi等设置 右边的开关!!!!!!!!!!!!!!!
在这里示例将通讯录第一行右边设置为 开关 , 其余的右边设置为 联系人详情按钮
*/
//由于此方法调用十分频繁,cell的 标示 声明成 静态变量 有助于性能优化
//注意: 由于此时我们需要两种UITableViewCell样式,考虑到性能我们需要在缓存池缓存两种Cell。
static NSString *cellIdentifier = @"UITableViewCellIdentifierKey1";
static NSString *cellIdentifierForFiestRow = @"UITableViewCellIdentifierKeyWithSwitch";
//首先根据标识去缓存池取
UITableViewCell *cell;
if (indexPath.row == 0) {
cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifierForFiestRow];
}else{
cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
}
//如果缓存池没有则重新创建,并放到缓存池中
if (!cell) {
if (indexPath.row == 0) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:cellIdentifierForFiestRow];
//在第一行右边创建开关
UISwitch *sw = [[UISwitch alloc]init];
[sw addTarget:self action:@selector(switchValueChange:) forControlEvents:UIControlEventValueChanged];
//UISwitch继承于UIControl而不是UIView(当然UIControl最终也是继承于UIView),继承于UIControl的控件使用addTarget添加对应事件而不是代理,同时有“是否可用”、“是否高亮”、“是否选中”等属性;
cell.accessoryView = sw;
}else{
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:cellIdentifier];
cell.accessoryType = UITableViewCellAccessoryDetailButton;
}
}
if (indexPath.row == 0) {
((UISwitch *)cell.accessoryView).tag = indexPath.section;
}
//cell上的内容(联系人 和 电话号码)
cell.textLabel.text = [contact getName];
cell.detailTextLabel.text = contact.phoneNumber;
return cell;
}
#pragma mark 返回每组 头标题 名称
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{
NSLog(@"生成组(组%li)名称",(long)section);
KCContactGroup *group = contacts[section];
return group.name;
}
#pragma mark 返回每组 尾部 说明
- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section{
NSLog(@"生成尾部(组%li)详情",(long)section);
KCContactGroup *group = contacts[section];
return group.detail;
}
#pragma mark 返回每组标题 索引
/**
* 大家在使用iPhone通讯录时会发现右侧可以按字母检索,使用起来很方便,其实这个功能使用UITableView实现很简单,只要实现数据源协议的一个方法,构建一个分组标题的数组即可实现。数组元素的内容和组标题内容未必完全一致,UITableView是按照数组元素的索引和每组数据索引顺序来定位的而不是按内容查找。
*/
- (nullable NSArray<NSString *> *)sectionIndexTitlesForTableView:(UITableView *)tableView {
NSLog(@"生成组索引");
NSMutableArray *indexs = [[NSMutableArray alloc]init];
for (KCContactGroup *group in contacts) {
[indexs addObject:group.name];
}
return indexs;
}
#pragma mark - 代理方法
#pragma mark 设置每行高度(每行高度可以不一样)
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
return 45;
}
#pragma mark 设置分组标题内容高度
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section{
if (section == 0) {
return 50;
}
return 40;
}
#pragma mark 设置尾部说明内容高度
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section{
return 40;
}
#pragma mark 监听事件(点击会跳转,改值等)
//在iOS中点击某联系个人就可以呼叫这个联系人,这时就需要监听点击操作,这里就不演示呼叫联系人操作了,我们演示一下修改人员信息的操作。
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
selectedIndexPath = indexPath;
KCContactGroup *group = contacts[indexPath.section];
KCContact *contact = group.contacts[indexPath.row];
NSLog(@"当前选中的组和行%@", selectedIndexPath);
//创建弹出窗口
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"提示" message:[contact getName] delegate:self cancelButtonTitle:@"cancel" otherButtonTitles:@"Ok", nil];
alert.alertViewStyle = UIAlertViewStylePlainTextInput;//设置窗口内容样式
UITextField *textField = [alert textFieldAtIndex:0];//取得文本框
textField.text = contact.phoneNumber;//设置文本框内容
[alert show];//展示窗口
}
#pragma mark 弹出窗口的代理方法,用户保存数据
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
//当点击了第二个按钮(Ok)
if (buttonIndex == 1) {
UITextField *textField = [alertView textFieldAtIndex:0];
//修改模型数据
KCContactGroup *group = contacts[selectedIndexPath.section];
KCContact *contact = group.contacts[selectedIndexPath.row];
contact.phoneNumber = textField.text;
// //刷新数据(---------------------------全部刷新--------------------)
// [myTableView reloadData];
/**
* 在上面的代码中我们通过修改模型来改变UI显示,这种方式是经典的MVC应用,在后面的代码中会经常看到。当然UI的刷新使用了UITableView的reloadData方法,该方法会重新调用数据源方法,包括计算分组、计算每个分组的行数,生成单元格等刷新整个UITableView。当然这种方式在实际开发中是不可取的,我们不可能因为修改了一个人的信息就刷新整个UITableViewView,此时我们需要采用局部刷新。局部刷新使用起来很简单,只需要调用UITableView的另外一个方法
*/
//刷新表格(----------------------局部刷新-------------------------)
NSArray *indexPaths=@[selectedIndexPath];//需要局部刷新的单元格的组、行
[myTableView reloadRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationLeft];//后面的参数代表更新时的动画
}
}
#pragma mark 重写状态样式方法
-(UIStatusBarStyle)preferredStatusBarStyle{
return UIStatusBarStyleLightContent;
}
#pragma mark 切换开关转化事件
-(void)switchValueChange:(UISwitch *)sw{
//打印点击的组 和 开关状态
NSLog(@"section:%li, switch:%i",(long)sw.tag,sw.on);
}
#pragma mark 删除按钮的方法
-(void)remove:(id)sender{
/**
在UITableView中无论是删除操作还是添加操作都是通过修改UITableView的编辑状态来改变的(除非你不用UITableView自带的删除功能)。在删除按钮中我们设置UITableView的编辑状态:
*/
//直接通过下面的方法设置编辑状态没有动画
//myTableView.editing=!myTableView.isEditing;
//只要一句代码
//如果判断为假
isInsert=false;
[myTableView setEditing:!myTableView.isEditing animated:true];
}
#pragma mark 添加按钮的方法
-(void)add:(id)sender{
//只要一句代码
//如果判断为真
isInsert=true;
[myTableView setEditing:!myTableView.isEditing animated:true];
}
/**
* 添加和删除操作都是设置UITableView的编辑状态,具体是添加还是删除需要根据代理方法-(UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath;的返回值来确定。因此这里我们定义一个变量来记录点击了哪个按钮,根据点击按钮的不同在这个方法中返回不同的值。
*/
#pragma mark 取得当前操作状态,根据不同的状态左侧出现不同的操作按钮
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath{
if (isInsert) {
return UITableViewCellEditingStyleInsert;
}
return UITableViewCellEditingStyleDelete;
}
#pragma mark 编辑操作(删除和添加)
/**
* 用过iOS的朋友都知道,一般这种Cell如果向左滑动右侧就会出现删除按钮直接删除就可以了。其实实现这个功能只要实现代理-(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath;方法,只要实现了此方法向左滑动就会显示删除按钮。只要点击删除按钮这个方法就会调用,但是需要注意的是无论是删除还是添加都是执行这个方法,只是第二个参数类型不同。下面看一下具体的删除实现:
*/
//实现了此方法向左滑动就会显示删除(或添加)图标
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath{
KCContactGroup *group = contacts[indexPath.section];
KCContact *contact = group.contacts[indexPath.row];
/**
* MVC的思想,要修改UI先修改数据。而且我们看到了另一个刷新表格的方法- (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation;,使用这个方法可以再删除之后刷新对应的单元格。
*/
if (editingStyle == UITableViewCellEditingStyleDelete) {
[group.contacts removeObject:contact];
//考虑到性能这里不建议使用reloadData
// [myTableView reloadData];
//使用下面的方法,既可以局部刷新又有动画效果
[myTableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationBottom];
//如果当前组中没有数据,则移除刷新整个表格
if (group.contacts.count == 0) {
[contacts removeObject:group];
[myTableView reloadData];
}
}else if (editingStyle == UITableViewCellEditingStyleInsert){
KCContact *newContact = [[KCContact alloc]init];
newContact.firstName = @"xiao";
newContact.lastName = @"ming";
newContact.phoneNumber = @"888888888888";
[group.contacts insertObject:newContact atIndex:indexPath.row];
[myTableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationBottom];//注意这里没有使用reloadData刷新
}
}
#pragma mark 排序
//只要实现这个方法,在编辑状态右侧就有排序图标
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath{
KCContactGroup *sourceGroup = contacts[sourceIndexPath.section];
KCContact *sourceContact = sourceGroup.contacts[sourceIndexPath.row];
KCContactGroup *destinationGroup = contacts[destinationIndexPath.section];
[sourceGroup.contacts removeObject:sourceContact];
if (sourceGroup.contacts.count == 0) {
[contacts removeObject:sourceGroup];
[myTableView reloadData];
}
[destinationGroup.contacts insertObject:sourceContact atIndex:destinationIndexPath.row];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
需要注意的是上面几个重点方法的执行顺序,请看下图:
使用UITableViewController检索
很多时候一个UIViewController中只有一个UITableView,因此苹果官方为了方便大家开发直接提供了一个UITableViewController,这个控制器 UITableViewController实现了UITableView数据源和代理协议,内部定义了一个tableView属性供外部访问,同时自动铺满整个屏幕、自动伸缩以方便我们的开发。当然UITableViewController也并不是简单的帮我们定义完UITableView并且设置了数据源、代理而已,它还有其他强大的功能,例如刷新控件、滚动过程中固定分组标题等。
有时候一个表格中的数据特别多,检索起来就显得麻烦,这个时候可以实现一个搜索功能帮助用户查找数据,其实搜索的原理很简单:修改模型、刷新表格。下面使用UITableViewController简单演示一下这个功能:
KCContactTableViewController.h
#import <UIKit/UIKit.h>
@interface KCContactTableViewController : UITableViewController
@end
KCContactTableViewController.m
#import "KCContactTableViewController.h"
#import "KCContact.h"
#import "KCContactGroup.h"
#define kSearchbarHeight 44
@interface KCContactTableViewController ()<UISearchBarDelegate>{
UITableView *tableView;
UISearchBar *mysearchBar;
NSMutableArray *contacts;//联系人模型
NSMutableArray *searchContacts;//符合条件的搜索联系人
BOOL isSearching;
}
@end
@implementation KCContactTableViewController
- (void)viewDidLoad {
[super viewDidLoad];
//初始化数据
[self initData];
//添加搜索框
[self addSearchBar];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
#pragma mark 数据源方法
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
if (isSearching) {
return 1;
}
return contacts.count;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (isSearching) {
return searchContacts.count;
}
KCContactGroup *group1 = contacts[section];
return group1.contacts.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
KCContact *contact = nil;
if (isSearching) {
contact = searchContacts[indexPath.row];
}else{
KCContactGroup *group = contacts[indexPath.section];
contact = group.contacts[indexPath.row];
}
static NSString *cellIdentifier = @"UITableViewCellIdentifierKey1";
//首先根据标识去缓存池取
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
//如果缓存池没有取到,则重新创建并放到缓存池中
if (!cell) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:cellIdentifier];
}
cell.textLabel.text = [contact getName];
cell.detailTextLabel.text = contact.phoneNumber;
return cell;
}
#pragma mark - 代理方法
#pragma mark 设置分组标题
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{
KCContactGroup *group = contacts[section];
return group.name;
}
#pragma mark - 搜索框代理
#pragma mark 取消搜索
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar{
isSearching = NO;
mysearchBar.text = @"";
[self.tableView reloadData];
}
#pragma mark 输入搜索关键字
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText{
if ([mysearchBar.text isEqual:@""]) {
isSearching = NO;
[self.tableView reloadData];
return;
}
[self searchDataWithKeyWord:mysearchBar.text];
}
#pragma mark 点击虚拟键盘上的搜索时
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar{
[self searchDataWithKeyWord:searchBar.text];
[mysearchBar resignFirstResponder];//放弃第一响应者对象,关闭虚拟键盘
}
#pragma mark 重写状态样式方法
- (UIStatusBarStyle)preferredStatusBarStyle{
return UIStatusBarStyleLightContent;
}
#pragma mark 加载数据
- (void)initData{
contacts = [[NSMutableArray alloc]init];
KCContact *contact1 = [KCContact initWithFirstName:@"liu" andLastName:@"bo" andPhoneNumber:@"18220480259"];
KCContact *contact2 = [KCContact initWithFirstName:@"wang" andLastName:@"zhaohua" andPhoneNumber:@"18792487043"];
KCContactGroup *group1 = [KCContactGroup initWithName:@"jiaren" andDetail:@"womenshiyijiaren" andContacts:[NSMutableArray arrayWithObjects:contact1, contact2, nil]];
[contacts addObject:group1];
KCContact *contact3 = [KCContact initWithFirstName:@"zhang" andLastName:@"bing" andPhoneNumber:@"12345678900"];
KCContact *contact4 = [KCContact initWithFirstName:@"wu" andLastName:@"wei" andPhoneNumber:@"98765432100"];
KCContact *contact5 = [KCContact initWithFirstName:@"zhang" andLastName:@"san" andPhoneNumber:@"12345612345"];
KCContactGroup *group2 = [KCContactGroup initWithName:@"pengyou" andDetail:@"womenshipengyou" andContacts:[NSMutableArray arrayWithObjects:contact3, contact4, contact5, nil]];
[contacts addObject:group2];
KCContact *contact6 = [KCContact initWithFirstName:@"li" andLastName:@"si" andPhoneNumber:@"12345555555"];
KCContact *contact7 = [KCContact initWithFirstName:@"wang" andLastName:@"wu" andPhoneNumber:@"88888888888"];
KCContactGroup *group3 = [KCContactGroup initWithName:@"tongxue" andDetail:@"womenshitongxue" andContacts:[NSMutableArray arrayWithObjects:contact6, contact7, nil]];
[contacts addObject:group3];
KCContact *contact8 = [KCContact initWithFirstName:@"hi" andLastName:@"pi" andPhoneNumber:@"12345555555"];
KCContact *contact9 = [KCContact initWithFirstName:@"kang" andLastName:@"gu" andPhoneNumber:@"88888888888"];
KCContact *contact10 = [KCContact initWithFirstName:@"roi" andLastName:@"sfi" andPhoneNumber:@"12345555555"];
KCContact *contact11 = [KCContact initWithFirstName:@"fang" andLastName:@"ii" andPhoneNumber:@"88888888888"];
KCContact *contact12 = [KCContact initWithFirstName:@"tyng" andLastName:@"rd" andPhoneNumber:@"88888888888"];
KCContactGroup *group4 = [KCContactGroup initWithName:@"qinqi" andDetail:@"womenshiqinqi" andContacts:[NSMutableArray arrayWithObjects:contact8, contact9, contact10, contact11, contact12, nil]];
[contacts addObject:group4];
KCContact *contact13 = [KCContact initWithFirstName:@"roi" andLastName:@"sfi" andPhoneNumber:@"12345555555"];
KCContact *contact14 = [KCContact initWithFirstName:@"fang" andLastName:@"ii" andPhoneNumber:@"88888888888"];
KCContact *contact15 = [KCContact initWithFirstName:@"tyng" andLastName:@"rd" andPhoneNumber:@"88888888888"];
KCContactGroup *group5 = [KCContactGroup initWithName:@"tongshi" andDetail:@"womenshitongshi" andContacts:[NSMutableArray arrayWithObjects:contact13, contact14, contact15, nil]];
[contacts addObject:group5];
}
#pragma mark 搜索形成新数据
- (void)searchDataWithKeyWord:(NSString *)keyWord{
isSearching = YES;
searchContacts = [NSMutableArray array];
[contacts enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
KCContactGroup *group = obj;
[group.contacts enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
KCContact *contact = obj;
if ([contact.firstName.uppercaseString
containsString:keyWord.uppercaseString] || [contact.lastName.uppercaseString containsString:keyWord.uppercaseString] || [contact.phoneNumber containsString:keyWord]) {
[searchContacts addObject:contact];
}
}];
}];
//刷新表格
[self.tableView reloadData];
}
#pragma mark 添加搜索框
- (void)addSearchBar{
CGRect searchBarRect = CGRectMake(0, 0, self.view.frame.size.width, 44);
mysearchBar = [[UISearchBar alloc]initWithFrame:searchBarRect];
mysearchBar.placeholder = @"请输入姓名";//浅色提示文字
mysearchBar.keyboardType = UIKeyboardTypeAlphabet;//键盘类型
mysearchBar.autocorrectionType = UITextAutocorrectionTypeNo;//自动纠错类型
mysearchBar.autocapitalizationType = UITextAutocapitalizationTypeNone;//哪一次shitf被自动按下
mysearchBar.showsCancelButton = YES;//显示取消按钮
//添加搜索框到页眉位置
mysearchBar.delegate = self;
self.tableView.tableHeaderView = mysearchBar;
}
/*
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:<#@"reuseIdentifier"#> forIndexPath:indexPath];
// Configure the cell...
return cell;
}
*/
/*
// Override to support conditional editing of the table view.
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
// Return NO if you do not want the specified item to be editable.
return YES;
}
*/
/*
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source
[tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
} else if (editingStyle == UITableViewCellEditingStyleInsert) {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
*/
/*
// Override to support rearranging the table view.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath {
}
*/
/*
// Override to support conditional rearranging of the table view.
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
// Return NO if you do not want the item to be re-orderable.
return YES;
}
*/
/*
#pragma mark - Table view delegate
// In a xib-based application, navigation from a table can be handled in -tableView:didSelectRowAtIndexPath:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Navigation logic may go here, for example:
// Create the next view controller.
<#DetailViewController#> *detailViewController = [[<#DetailViewController#> alloc] initWithNibName:<#@"Nib name"#> bundle:nil];
// Pass the selected object to the new view controller.
// Push the view controller.
[self.navigationController pushViewController:detailViewController animated:YES];
}
*/
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
@end
大神链接 - 很重要:
http://www.cnblogs.com/kenshincui/p/3931948.html#introduction
修改点击后cell的颜色
if ([tableView isEqual:leftTableView]) {
static NSString *cellId = @"cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellId];
if (cell == nil) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellId];
}
cell.textLabel.text = @"陕西";
//选中cell后为紫色,对新生成的cell添加一个背景view:(将cell在选择时背景置为紫色)
UIView *aVIew = [[UIView alloc]initWithFrame:cell.frame];
aVIew.backgroundColor = [UIColor purpleColor];
cell.selectedBackgroundView = aVIew;
return cell;
}