表视图控制器(TableViewController)(一)

1 创建一个UITableViewController并展示简单数据

1.1 问题

有很多移动客户端的应用都是采用表的形式来展示数据,因为表视图能使数据看起来更规整、更有调理,比如微信界面就是使用的表视图,如图-1所示:

图-1

在IOS中表视图是非常重要的视图,类型名称为UITabelViewController,是UIViewController的子类,本案例将学习如何使用UITableViewController来展示简单的数据,完成效果如图-2所示:

图-2

1.2 方案

首先创建一个SingleViewApplication项目,创建一个TRMyTableViewController类继承至UITableViewController,在TRAppDelegate的程序入口方法里面创建一个TRMyTableViewController对象做为根视图控制器。

UITableViewController类有一个UITabelView类型的属性tableView,该属性就是表视图控制器管理的表视图,类似UIViewController和属性view的关系。表视图由以下几个部分组成:

表头视图tableHeaderView:表视图最上边的视图,用于展示表视图的信息;

表脚视图tableFooterView:表视图最下边的视图;

单元格cell:是一个UITableViewCell对象,表视图每一行的单位视图;

分区section:由多个单元格cell组成,有分区头和分区脚。

表视图有两个委托协议一个是UITableViewDataSource用于给表视图加载数据,另一个是UITableViewDelegate用于实现其他事件的委托。

另外表视图还有两个属性id <UITableViewDataSource>类型的dataSource和id <UITableViewDelegate>类型的delegate,分别用于指定加载数据的对象和被委托对象。

然后在TRMyTableViewController类里面通过实现的UITableViewDataSource协议的方法给表视图加载简单的数据。

1.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:创建TRMyTableViewController对象做为程序的根视图控制器

首先创建一个单视图应用项目命名为TRMyTableViewController,再创建一个带有xib的TRMyTableViewController类继承至UITableViewController,生成的xib文件如图-3所示:

图-3

然后在TRAppDelegate.m的程序入口方法中创建一个TRMyTableViewController对象做为程序的根视图,代码如下所示:

  1. -(BOOL)application:(UIApplication *)application
  2. didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
  3. {
  4. self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
  5. self.window.backgroundColor = [UIColor whiteColor];
  6. //创建一个TRTableViewController对象做为根视图控制器
  7. TRMyTableViewController *myTVC = [[TRMyTableViewController alloc]initWithNibName:@"TRMyTableViewController" bundle:nil];
  8. self.window.rootViewController = myTVC;
  9. [self.window makeKeyAndVisible];
  10. return YES;
  11. }

在这里myTVC视图控制器对象管理的表视图就是程序展示的第一个界面视图。

步骤二:实现UITableViewDataSource协议的方法给表视图加载数据

UITabelViewController类遵守了两个协议一个是UITableViewDataSource用于给表视图加载数据,另一个是UITableViewDelegate用于实现委托,这里先学习通过实现UITableViewDataSource协议的方法来给表视图加载数据。

系统会自动将TRMyTableViewController指定为它所管理的的表视图的dataSource和delegate,可以在xib中查看,选中tableView点击右键,出现如图-4所示的窗口:

图-4

从图可见dataSource和delegate都是和File’s Owner关联上的,此时的File’s Owner即TRMyTableViewController。

TRMyTableViewController类是UITabelViewController的子类,所以TRMyTableViewController也遵守了以上两个协议,不用再额外写遵守协议代码,直接实现协议里面的方法即可,这里需要实现如下三个协议方法:

numberOfSectionsInTableView:可选方法,用于告诉表视图需要显示多少分区,不实现的时候默认返回1个分区;

tableView:numberOfRowsInSection:必须实现的方法,用于告诉表视图每组需要显示多少行;

tableView:cellForRowAtIndexPath:必须实现的方法,用于告诉表视图每一行显示的内容;

在这里先指定表视图显示1组,每组显示20行,代码如下所示:

  1. -(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
  2. {
  3. return 1;
  4. }
  5. -(NSInteger)tableView:(UITableView *)tableView
  6. numberOfRowsInSection:(NSInteger)section
  7. {
  8. return 20;
  9. }

每一行用于显示内容的是一个UITableViewCell类型的对象,该方法里面需要先创建一个UITableViewCell类型的对象cell,使用初始化方法initWithStyle: reuseIdentifier:进行初始化,style参数是cell的样式,这里选择系统默认的样式即可,reuseIdentifier参数传递一个字符串@”Cell”,本案例先不对该参数的作用进行阐述,后面的案例会进行解释。

UITableViewCell有一个UILabel类型的属性textLabel,该属性用来展示文本内容,这里设置cell的textLabel的显示内容,最后将cell对象返回,代码如下所示:

  1. -(UITableViewCell *)tableView:(UITableView *)tableView
  2. cellForRowAtIndexPath:(NSIndexPath *)indexPath
  3. {
  4. //创建cell对象
  5. UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Cell"];
  6. //设置cell的显示内容
  7. cell.textLabel.text = @"Hello World.";
  8. return cell;
  9. }

1.4 完整代码

本案例中,TRAppDelegate.m文件中的完整代码如下所示:

  1. #import "TRAppDelegate.h"
  2. #import "TRMyTableViewController.h"
  3. @implementation TRAppDelegate
  4. -(BOOL)application:(UIApplication *)application
  5. didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
  6. {
  7. self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
  8. self.window.backgroundColor = [UIColor whiteColor];
  9. TRMyTableViewController *myTVC = [[TRMyTableViewController alloc]initWithNibName:@"TRMyTableViewController" bundle:nil];
  10. self.window.rootViewController = myTVC;
  11. [self.window makeKeyAndVisible];
  12. return YES;
  13. }
  14. @end

本案例中,TRMyFirstViewController.m文件中的完整代码如下所示:

  1. #import "TRMyTableViewController.h"
  2. @implementation TRMyTableViewController
  3. #pragma mark - Table view data source
  4. - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
  5. {
  6. return 1;
  7. }
  8. -(NSInteger)tableView:(UITableView *)tableView
  9. numberOfRowsInSection:(NSInteger)section
  10. {
  11. return 20;
  12. }
  13. -(UITableViewCell *)tableView:(UITableView *)tableView
  14. cellForRowAtIndexPath:(NSIndexPath *)indexPath
  15. {
  16. UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Cell"];
  17. cell.textLabel.text = @"Hello World.";
  18. return cell;
  19. }
  20. @end

2 使用IndexPath中的两个属性区别分区展示数据

2.1 问题

上一个案例已经学习了如何使用表视图控制器展现一组简单的数据,本案例将在上一个案例的基础上让表视图以多个分区的形式展示数据,如图-5所示:

图-5

2.2 方案

首先同上一个案例一样创建一个表视图控制器TRMytableViewController作为本应用的根视图控制器。

其次通过实现UITableViewDataSource协议的方法告诉tableView有多少分区、有多少行以及每一行的显示内容。

2.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:创建TRMyTableViewController对象做为程序的根视图控制器

首先同第一个案例一样创建一个单视图应用项目命名为TRMyTableViewController,再创建一个带有xib的TRMyTableViewController类继承至UITableViewController。

步骤二:实现UITableViewDataSource协议的方法给表视图加载数据

NSIndexPath用来管理存储路径,有两个属性section和row,section用来描述分区,row用来描述分区里面的某一行。

首先通过实现numberOfSectionsInTableView:方法让tabelView分成三个区,该方法不实现默认是返回一个分区,代码如下所示:

  1. - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
  2. {
  3. return 3;
  4. }

然后通过tableView:numberOfRowsInSection:方法确定每个分区的行数,代码如下所示:

  1. -(NSInteger)tableView:(UITableView *)tableView
  2. numberOfRowsInSection:(NSInteger)section
  3. {
  4. if(section == 0) return 2;
  5. else if(section == 1) return 3;
  6. else if(section == 2) return 1500;
  7. return 0;
  8. }

最后通过实现tableView:cellForRowAtIndexPath:确定不同分区的每一行的显示内容,本案例创建cell选择的是UITableViewCellStyleSubtitl类型,该类型除了显示textLabel的显示内容,还可以设置cell的另外两个属性,如下所示:

imageView属性:用来显示一张图片;

detailTextLabel属性:用来显示详细信息。

代码如下所示:

  1. -(UITableViewCell *)tableView:(UITableView *)tableView
  2. cellForRowAtIndexPath:(NSIndexPath *)indexPath
  3. {
  4. UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"Cell"];
  5. if(indexPath.section == 0){
  6. cell.textLabel.text = [NSString stringWithFormat:@"第0区的第%d行",indexPath.row];
  7. }else{
  8. cell.textLabel.text = [NSString stringWithFormat:@"%d区%d行",indexPath.section,indexPath.row];
  9. }
  10. cell.imageView.image = [UIImage imageNamed:@"a.png"];
  11. cell.detailTextLabel.text = @"这是详细信息";
  12. return cell;
  13. }

2.4 完整代码

本案例中,TRAppDelegate.m文件中的完整代码如下所示:

  1. #import "TRAppDelegate.h"
  2. #import "TRMyTableViewController.h"
  3. @implementation TRAppDelegate
  4. -(BOOL)application:(UIApplication *)application
  5. didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
  6. {
  7. self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
  8. self.window.backgroundColor = [UIColor whiteColor];
  9. TRMyTableViewController *myTVC = [[TRMyTableViewController alloc]initWithNibName:@"TRMyTableViewController" bundle:nil];
  10. self.window.rootViewController = myTVC;
  11. [self.window makeKeyAndVisible];
  12. return YES;
  13. }
  14. @end

本案例中,TRMyTableViewController.m文件中的完整代码如下所示:

  1. #import "TRMyTableViewController.h"
  2. @implementation TRMyTableViewController
  3. #pragma mark - Table view data source
  4. //如果不实现此方法,默认为1个分区
  5. - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
  6. {
  7. return 3;
  8. }
  9. -(NSInteger)tableView:(UITableView *)tableView
  10. numberOfRowsInSection:(NSInteger)section
  11. {
  12. if(section == 0) return 2;
  13. else if(section == 1) return 3;
  14. else if(section == 2) return 1500;
  15. return 0;
  16. }
  17. -(UITableViewCell *)tableView:(UITableView *)tableView
  18. cellForRowAtIndexPath:(NSIndexPath *)indexPath
  19. {
  20. UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"Cell"];
  21. if(indexPath.section == 0){
  22. cell.textLabel.text = [NSString stringWithFormat:@"第0区的第%i行",indexPath.row];
  23. }else{
  24. cell.textLabel.text = [NSString stringWithFormat:@"%i区%i行",indexPath.section,indexPath.row];
  25. }
  26. cell.imageView.image = [UIImage imageNamed:@"a.png"];
  27. cell.detailTextLabel.text = @"这是详细信息";
  28. return cell;
  29. }
  30. @end

3 展示Cell重用时的数据处理

3.1 问题

表视图在展现数据的时候,超出界面的单元格cell对象并不会被释放,而是放入了tableView的用于管理单元格的队列中,所以需要在创建cell对象之前先试着从此队列中去拿已经存在的cell对象,如果能拿到就不需要再创建cell对象,而是重用此Cell对象,以减少对内存的占用,本案例将演示cell的重用过程。

3.2 方案

UITableViewCell的重用机制:

1)当tableView在创建每一个cell的时候会对cell做一个重用标记,该标记就是初始化方法initWithStyle:reuseIdentifier:的reuseIdentifier参数,是一个NSString类型;

2)当表视图的单元格超出界面之后会将该单元格加入的tableView的单元格队列中,因此每次创建cell之前都会做一个判断,判断tableView的单元格队列中是否存在cell对象,如果有就直接使用,如果没有就创建一个新的cell对象。

3.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:定义一个重用标记

在tableView:cellForRowAtIndexPath:方法里面首先创建一个NSString类型的对象cellIdentifier用做重用标记,该对象是一个static类型,保证在程序结束之前不会被销毁,代码如下所示:

  1. static NSString *cellIdentifier = @"MyCell";

步骤二:判断tableView的单元格队列中是否存在cell对象

通过tabelView的dequeueReusableCellWithIdentifier:方法获取 cell对象,如果该方法返回的cell对象不为空,说明有可重用的cell,identifier参数就是上一步所创建的重用标记cellIdentifier,代码如下所示:

  1. UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];

步骤三:如果没有可重用的cell则创建一个新的cell对象

通过上一步获取到的cell对象如果为空,说明没有可以重用的cell对象,则需要创建一个新的cell对象以供使用,代码如下所示:

  1. if(cell==nil){
  2. cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellIdentifier];
  3. }

3.4 完整代码

本案例中,TRTableViewController.m文件中的完整代码如下所示:

  1. #import "TRTableViewController.h"
  2. @implementation TRTableViewController
  3. -(UITableViewCell *)tableView:(UITableView *)tableView
  4. cellForRowAtIndexPath:(NSIndexPath *)indexPath
  5. {
  6. static NSString *cellIdentifier = @"MyCell";
  7. UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
  8. if(cell==nil){
  9. cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellIdentifier];
  10. }
  11. return cell;
  12. }
  13. @end
隐藏

4 注册Cell并重用

4.1 问题

本案例学习使用注册cell的方式实现cell的重用。

4.2 方案

首先向tableView注册一个重用的Cell类;

然后向tableView索要队列中的Cell对象时,如果队列中没有Cell对象,tableView会自动创建一个并返回,这样可以确保dequeue方法一定会返回一个Cell对象供使用。

4.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:注册cell

在TRTableViewController里面首先创建一个NSString类型的对象cellIdentifier用做重用标记,该对象是一个static类型,保证在程序结束之前不会被销毁,代码如下所示:

  1. static NSString *cellIdentifier = @"MyCell";

在viewDidLoad方法里面使用registerClass: forCellReuseIdentifier:方法注册cell,代码如下所示:

  1. - (void)viewDidLoad
  2. {
  3. [super viewDidLoad];
  4. //注册一个Cell类型到tableView
  5. [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:cellIdentifier];
  6. }

步骤二:创建cell对象

在tableView: cellForRowAtIndexPath:方法里面创建cell对象,使用dequeueReusableCellWithIdentifier: forIndexPath:方法创建cell对象时,如果没有可重用的cell会自动创建一个,因此能保证返回一个cell对象,代码如下所示:

  1. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
  2. {
  3. //只要注册过Cell类型,此处tableView保证能返回一个Cell对象
  4. UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
  5. return cell;
  6. }

4.4 完整代码

  1. #import "TRTableViewController.h"
  2. @implementation TRTableViewController
  3. //定义cell的重用标识
  4. static NSString *cellIdentifier = @"MyCell";
  5. - (void)viewDidLoad
  6. {
  7. [super viewDidLoad];
  8. //注册一个Cell类型到tableView
  9. [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:cellIdentifier];
  10. }
  11. //cellForRow方法里面的代码
  12. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
  13. {
  14. //只要注册过Cell类型,此处tableView保证能返回一个Cell对象
  15. UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
  16. cell.textLabel.text = self.data[indexPath.row];
  17. return cell;
  18. }
  19. @end

5 使用TableView展示数据列表

5.1 问题

为了使tableView显示的数据更灵活,通常都会有一个NSArray类型的属性来管理表视图的数据,本案例在第三个案例(或者案例四)的基础上让表视图展示一组字符串数据(城市名称)。

5.2 方案

首先定义一个NSArray类型的属性用于存储表视图的数据;

其次在TRAppDelegate的程序入口方法里面初始化数组;

最后在协议方法里面给表视图加载数据。

5.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:定义一个存储数据的NSArray类型的属性

为了使tableView显示的数据更灵活,通常都会有一个NSArray类型的属性来管理表视图的数据,在TRTableViewController类里面定义一个公开的属性NSArray类型的属性data,代码如下所示:

  1. @property (nonatomic, strong) NSArray *data;

步骤二:初始化存储数据的数组

在TRAppdelegate的程序入口方法里面对该属性进行初始化,这里给一个模拟数据,代码如下所示:

  1. -(BOOL)application:(UIApplication *)application
  2. didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
  3. {
  4. self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
  5. self.window.backgroundColor = [UIColor whiteColor];
  6. TRTableViewController *tvc = [[TRTableViewController alloc]initWithNibName:@"TRTableViewController" bundle:nil];
  7. tvc.data = @[@"北京", @"上海", @"广州", @"深圳", @"济南",@"石家庄",@"武汉",@"郑州",@"成都",@"重庆",@"珠海",@"香港",@"澳门"];
  8. UINavigationController *navi = [[UINavigationController alloc]initWithRootViewController:tvc];
  9. self.window.rootViewController = navi;
  10. [self.window makeKeyAndVisible];
  11. return YES;
  12. }

步骤三:给表视图加载数据

首先在tableView:numberOfRowsInSection:方法里面返回表视图的行数,通常都是返回一个数组的count,本案例即self.data.count,代码如下所示:

  1. -(NSInteger)tableView:(UITableView *)tableView
  2. numberOfRowsInSection:(NSInteger)section
  3. {
  4. return self.data.count;
  5. }

然后在TRTableViewController类里面的tableView:cellForRowAtIndexPath:的方法里面给表视图加载数据,代码如下所示:

  1. -(UITableViewCell *)tableView:(UITableView *)tableView
  2. cellForRowAtIndexPath:(NSIndexPath *)indexPath
  3. {
  4. static NSString *cellIdentifier = @"MyCell";
  5. UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
  6. if(cell==nil){
  7. cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellIdentifier];
  8. }
  9. cell.textLabel.text = self.data[indexPath.row];
  10. return cell;
  11. }

运行程序实现效果如图-6所示:

图-6

步骤四:点击某一行单元格

在点击表视图某一行时会调用tableView:didSelectRowAtIndexPath:方法,该方法是UITabelViewDelegate协议里面的方法,因此只需在TRTableViewController.m文件中实现该方法即可,indexPath参数就是被点击的单元格的路径,代码如下所示:

  1. //当用户点击了某一行Cell时被调用
  2. -(void)tableView:(UITableView *)tableView
  3. didSelectRowAtIndexPath:(NSIndexPath *)indexPath
  4. {
  5. NSLog(@"用户点击了%d区的%d行, %@", indexPath.section, indexPath.row, self.data[indexPath.row]);
  6. }

运行程序,点击某一行可见控制台会输出被点击的地区名称。

5.4 完整代码

本案例中,TRAppDeleagte.m文件中的完整代码如下所示:

  1. #import "TRAppDelegate.h"
  2. #import "TRTableViewController.h"
  3. @implementation TRAppDelegate
  4. -(BOOL)application:(UIApplication *)application
  5. didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
  6. {
  7. self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
  8. self.window.backgroundColor = [UIColor whiteColor];
  9. TRTableViewController *tvc = [[TRTableViewController alloc]initWithNibName:@"TRTableViewController" bundle:nil];
  10. tvc.data = @[@"北京", @"上海", @"广州", @"深圳", @"济南",@"石家庄",@"武汉",@"郑州",@"成都",@"重庆",@"珠海",@"香港",@"澳门"];
  11. UINavigationController *navi = [[UINavigationController alloc]initWithRootViewController:tvc];
  12. self.window.rootViewController = navi;
  13. [self.window makeKeyAndVisible];
  14. return YES;
  15. }
  16. @end

本案例中,TRTableViewController.m文件中的完整代码如下所示:

  1. #import "TRTableViewController.h"
  2. @implementation TRTableViewController
  3. - (void)viewDidLoad
  4. {
  5. [super viewDidLoad];
  6. self.title = @"地区";
  7. }
  8. -(NSInteger)tableView:(UITableView *)tableView
  9. numberOfRowsInSection:(NSInteger)section
  10. {
  11. return self.data.count;
  12. }
  13. -(UITableViewCell *)tableView:(UITableView *)tableView
  14. cellForRowAtIndexPath:(NSIndexPath *)indexPath
  15. {
  16. static NSString *cellIdentifier = @"MyCell";
  17. UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
  18. if(cell==nil){
  19. cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellIdentifier];
  20. }
  21. cell.textLabel.text = self.data[indexPath.row];
  22. return cell;
  23. }
  24. @end

6 项目友录中的朋友列表

6.1 问题

模拟系统的通讯录功能,使用表视图控制器展示联系人界面,并实现添加联系人功能,本案例表视图需要展示的是一组对象数据,实现效果如图-7、图-8所示:

图-7

图-8

6.2 方案

首先创建一个SingleViewApplication项目,再创建一个表视图控制器类TRContactTableViewController,该类有一个管理联系人的NSMutableArray类型的属性contacts。

在TRAppDelegate的程序入口方法里面创建一个带有导航的表视图控制器做为根视图控制器。

其次根据本案例的需求创建一个联系人类TRCantact类,用于保存联系人信息。

然后创建一个TRInputViewController类继承至UIViewController,用于管理编辑联系人界面。再给TRContactTableViewController的导航栏添加一个编辑按钮,点击按钮跳转到编辑界面,并实现表视图数据源协议方法。

最后在TRInputViewController类里面根据输入的信息创建TRContact对象,然后通过委托的方式将新建联系人对象传递给TRContactTableViewController类,并回退到联系人界面。TRContactTableViewController类里面通过实现协议方法给联系人页面加载和更新数据。

6.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:创建项目,设置根视图控制器

创建一个SingleViewApplication项目,然后再创建一个表视图控制器类TRContactTableViewController,并且定义一个用来管理联系人的NSMutableArray类型的属性contacts,代码如下所示:

  1. @interface TRContactTableViewController ()
  2. @property(nonatomic, strong) NSMutableArray *contacts;
  3. @end
  4. //重写setter方法,初始化实例变量_contacts
  5. - (NSMutableArray *)contacts
  6. {
  7. if(!_contacts){
  8. _contacts = [@[] mutableCopy];
  9. }
  10. return _contacts;
  11. }

在TRAppDelegate的程序入口方法里面创建一个带有导航的表视图控制器做为根视图控制器,代码如下所示:

  1. -(BOOL)application:(UIApplication *)application
  2. didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
  3. {
  4. self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
  5. self.window.backgroundColor = [UIColor whiteColor];
  6. TRContactTableViewController *contactTVC = [[TRContactTableViewController alloc]initWithNibName:@"TRContactTableViewController" bundle:nil];
  7. UINavigationController *navi = [[UINavigationController alloc]initWithRootViewController:contactTVC];
  8. self.window.rootViewController = navi;
  9. [self.window makeKeyAndVisible];
  10. return YES;
  11. }

步骤二:创建TRContact联系人类

创建一个TRContact类继承至NSObject,并且定义两个NSString类型的属性name和phoneNumber,分别用于存储联系人的姓名和手机号,代码如下所示:

  1. @property (nonatomic,copy)NSString *name;
  2. @property (nonatomic,copy)NSString *phoneNumber;

然后自定义一个初始化方法initWithName:andPhoneNumber:,方便外部创建联系人对象的时候进行初始化,代码如下所示:

  1. -(instancetype)initWithName:(NSString*)name
  2. andPhoneNumber:(NSString*)phoneNumber
  3. {
  4. self = [super init];
  5. if (self) {
  6. self.name = name;
  7. self.phoneNumber = phoneNumber;
  8. }
  9. return self;
  10. }

步骤三:创建TRInputViewController类,并实现页面跳转

创建一个TRInputViewController类,用于管理编辑联系人界面,在xib文件中拖放两个输入框(接受用户输入的联系人姓名和手机号),两个label和一个按钮,并在检查器中设置相关属性,这里需要注意将用于接收用户输入手机号的输入框,键盘类型选择NumberPad类型,完成效果如图-9所示:

图-9

然后给TRContactTableViewController的导航栏添加标题和编辑按钮,点击按钮实现页面的跳转,这里使用present的方式跳转页面,新页面会带有一个新的导航控制器,代码如下所示:

  1. - (void)viewDidLoad
  2. {
  3. [super viewDidLoad];
  4. //设置导航栏标题
  5. self.title = @"通讯录";
  6. //给导航栏添加编辑按钮,点击按钮会调用addContact:方法
  7. self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(addContact:)];
  8. }
  9. //实现addContact方法
  10. - (void)addContact:(UIBarButtonItem *)sender
  11. {
  12. TRInputViewController *inputVC = [[TRInputViewController alloc]initWithNibName:@"TRInputViewController" bundle:nil];
  13. UINavigationController *navi = [[UINavigationController alloc]initWithRootViewController:inputVC];
  14. [self presentViewController:navi animated:YES completion:nil];
  15. }

实现表视图数据源协议方法,完成数据加载代码,这里只有一个分区,因此可不用实现分区方法默认返回1,行数直接返回self.contacts.count,在tableView: cellForRowAtIndexPath:方法里,让cell的textLabel显示联系人的姓名,cell的detailTextLabel显示联系人的手机号,代码如下所示:

  1. - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
  2. {
  3. return self.contacts.count;
  4. }
  5. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
  6. {
  7. static NSString *CellIdentifier = @"Cell";
  8. UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
  9. if (cell == nil) {
  10. //创建cell时,类型选择UITableViewCellStyleValue1,右边显示detailTextLabel
  11. cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier];
  12. }
  13. //通过indexPath.row在self.contacts数组里面找到当前cell对应的联系人对象
  14. TRContact *contact = self.contacts[indexPath.row];
  15. //cell.textLabel显示联系人姓名
  16. cell.textLabel.text = contact.name;
  17. //cell.detailTextLabel显示联系人的手机号
  18. cell.detailTextLabel.text = contact.phoneNumber;
  19. return cell;
  20. }

运行程序,界面效果如图-10所示:

图-10

从图中可见目前联系人页面没有任何信息,那是因为联系人数组里面还没有任何数据,下一步则是通过编辑页面给联系人数组添加数据。

步骤四:根据用户的输入创建联系人对象并更新联系人界面

将TRInputViewController界面上的两个输入框关联成私有属性nametextField和phoneNumberTextField,代码如下所示:

  1. @property (weak, nonatomic) IBOutlet UITextField *nameTextField;
  2. @property (weak, nonatomic) IBOutlet UITextField *phoneNumberTextField;

将确定按钮关联成私有方法submit,该方法的功能主要是根据用户输入的信息创建一个新的TRContact对象contact,并通过委托将contact对象传递给联系人界面,最后返回联系人界面。

因此首先需要在TRInputViewController.h文件里面定义一个协议TRInputViewDelegate和一个委托属性,代码如下所示:

  1. @class TRInputViewController;
  2. @protocol TRInputViewDelegate <NSObject>
  3. - (void)inputViewController:(TRInputViewController *)inputVC contact:(TRContact *)contact;
  4. @end
  5. @interface TRInputViewController : UIViewController
  6. //在创建TRInputViewController对象时对delegate属性进行赋值确定被委托者
  7. @property (nonatomic, weak) id<TRInputViewDelegate> delegate;
  8. @end

然后实现submit方法,代码如下所示:

  1. - (IBAction)submit
  2. {
  3. [self.phoneNumberTextField resignFirstResponder];
  4. //创建TRContact联系人对象
  5. TRContact *contact = [[TRContact alloc]initWithName:self.nameTextField.text andPhoneNumber:self.phoneNumberTextField.text];
  6. //将contact对象传递给联系人界面
  7. [self.delegate inputViewController:self contact:contact];
  8. [self dismissViewControllerAnimated:YES completion:nil];
  9. }

最后TRContactTableViewController遵守TRInputViewDelegate协议,并实现协议方法inputViewController:contact:,该方法里主要是根据传递过来的联系人信息更新contacts数组和界面,这里使用reloadData方法刷新界面,代码如下所示:

  1. //遵守协议
  2. @interface TRContactTableViewController () <TRInputViewDelegate>
  3. - (void)addContact:(UIBarButtonItem *)sender
  4. {
  5. TRInputViewController *inputVC = [[TRInputViewController alloc]initWithNibName:@"TRInputViewController" bundle:nil];
  6. //添加联系人方法里面给inputVC.delegate赋值,指定当前对象为被委托者
  7. //添加联系人方法里面给inputVC.delegate赋值,指定当前对象为被委托者
  8. inputVC.delegate = self;
  9. UINavigationController *navi = [[UINavigationController alloc]initWithRootViewController:inputVC];
  10. [self presentViewController:navi animated:YES completion:nil];
  11. }
  12. //实现TRInputViewDelegate协议方法
  13. - (void)inputViewController:(TRInputViewController *)inputVC contact:(TRContact *)contact
  14. {
  15. //将传递过来的contact对象添加到联系人数组
  16. [self.contacts addObject:contact];
  17. //重新加载数据,更新界面
  18. [self.tableView reloadData];
  19. }

6.4 完整代码

本案例中,TRAppDelegate.m文件中的完整代码如下所示:

  1. #import "TRAppDelegate.h"
  2. #import "TRContactTableViewController.h"
  3. @implementation TRAppDelegate
  4. - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
  5. {
  6. self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
  7. self.window.backgroundColor = [UIColor whiteColor];
  8. TRContactTableViewController *contactTVC = [[TRContactTableViewController alloc]initWithNibName:@"TRContactTableViewController" bundle:nil];
  9. UINavigationController *navi = [[UINavigationController alloc]initWithRootViewController:contactTVC];
  10. self.window.rootViewController = navi;
  11. [self.window makeKeyAndVisible];
  12. return YES;
  13. }
  14. @end

本案例中,TRContactTableViewController.m文件中的完整代码如下所示:

  1. #import "TRContactTableViewController.h"
  2. #import "TRInputViewController.h"
  3. @interface TRContactTableViewController () <TRInputViewDelegate>
  4. @property(nonatomic, strong) NSMutableArray *contacts;
  5. @end
  6. @implementation TRContactTableViewController
  7. - (NSMutableArray *)contacts
  8. {
  9. if(!_contacts){
  10. _contacts = [@[] mutableCopy];
  11. }
  12. return _contacts;
  13. }
  14. //实现TRInputViewDelegate协议方法
  15. - (void)inputViewController:(TRInputViewController *)inputVC contact:(TRContact *)contact
  16. {
  17. [self.contacts addObject:contact];
  18. //重新加载数据
  19. [self.tableView reloadData];
  20. }
  21. - (void)viewDidLoad
  22. {
  23. [super viewDidLoad];
  24. self.title = @"通讯录";
  25. self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(addContact:)];
  26. }
  27. - (void)addContact:(UIBarButtonItem *)sender
  28. {
  29. TRInputViewController *inputVC = [[TRInputViewController alloc]initWithNibName:@"TRInputViewController" bundle:nil];
  30. inputVC.delegate = self;
  31. UINavigationController *navi = [[UINavigationController alloc]initWithRootViewController:inputVC];
  32. [self presentViewController:navi animated:YES completion:nil];
  33. }
  34. -(NSInteger)tableView:(UITableView *)tableView
  35. numberOfRowsInSection:(NSInteger)section
  36. {
  37. return self.contacts.count;
  38. }
  39. -(UITableViewCell *)tableView:(UITableView *)tableView
  40. cellForRowAtIndexPath:(NSIndexPath *)indexPath
  41. {
  42. static NSString *CellIdentifier = @"Cell";
  43. UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
  44. if (cell == nil) {
  45. cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier];
  46. }
  47. TRContact *contact = self.contacts[indexPath.row];
  48. cell.textLabel.text = contact.name;
  49. cell.detailTextLabel.text = contact.phoneNumber;
  50. return cell;
  51. }
  52. @end

本案例中,TRInputViewController.h文件中的完整代码如下所示:

  1. #import <UIKit/UIKit.h>
  2. #import "TRContact.h"
  3. @class TRInputViewController;
  4. @protocol TRInputViewDelegate <NSObject>
  5. -(void)inputViewController:(TRInputViewController *)inputVC
  6. contact:(TRContact *)contact;
  7. @end
  8. @interface TRInputViewController : UIViewController
  9. @property (nonatomic, weak) id<TRInputViewDelegate> delegate;
  10. @end

本案例中,TRInputViewController.m文件中的完整代码如下所示:

  1. #import "TRInputViewController.h"
  2. @interface TRInputViewController ()
  3. @property (weak, nonatomic) IBOutlet UITextField *nameTextField;
  4. @property (weak, nonatomic) IBOutlet UITextField *phoneNumberTextField;
  5. @end
  6. @implementation TRInputViewController
  7. - (IBAction)submit
  8. {
  9. [self.phoneNumberTextField resignFirstResponder];
  10. TRContact *contact = [[TRContact alloc]initWithName:self.nameTextField.text andPhoneNumber:self.phoneNumberTextField.text];
  11. [self.delegate inputViewController:self contact:contact];
  12. [self dismissViewControllerAnimated:YES completion:nil];
  13. }
  14. - (IBAction)next {
  15. [self.nameTextField resignFirstResponder];
  16. [self.phoneNumberTextField becomeFirstResponder];
  17. }
  18. @end

本案例中,TRContact.h文件中的完整代码如下所示:

  1. #import <Foundation/Foundation.h>
  2. @interface TRContact : NSObject
  3. @property (nonatomic,copy)NSString *name;
  4. @property (nonatomic,copy)NSString *phoneNumber;
  5. -(instancetype)initWithName:(NSString*)name
  6. andPhoneNumber:(NSString*)phoneNumber;
  7. @end
隐藏

本案例中,TRContact.m文件中的完整代码如下所示:

  1. #import "TRContact.h"
  2. @implementation TRContact
  3. -(instancetype)initWithName:(NSString*)name
  4. andPhoneNumber:(NSString*)phoneNumber
  5. {
  6. self = [super init];
  7. if (self) {
  8. self.name = name;
  9. self.phoneNumber = phoneNumber;
  10. }
  11. return self;
  12. }
  13. @end
隐藏

7 增加一个新朋友

7.1 问题

上一个案例已经实现的通讯录的展示联系人、添加联系人功能,刷新界面的时候使用reloadData方法,本案例将使用另外一种局部更新界面的方式更新联系人界面。

7.2 方案

本案例直接在上一个案例上做修改,主要是重写TRContactTableViewController类里面更新界面的代码,reloadData方法更新整个界面,也就是会将整个表视图的数据重新加载一遍,本案例采用insertRowsAtIndexPath方法局部更新界面。

7.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:使用insertRowsAtIndexPath方法局部更新界面

insertRowsAtIndexPath方法主要用于在表视图里面插入一行单元格,indexPath参数是该单元格插入的位置,是一个NSIndexPath类型的参数,因此在inputViewController:contact:方法里面,首先同样的将传递过来的contact添加到数组,然后找到插入单元格的位置,使用insertRowsAtIndexPath方法在表视图里面插入单元格,代码如下所示:

  1. - (void)inputViewController:(TRInputViewController *)inputVC contact:(TRContact *)contact
  2. {
  3. [self.contacts addObject:contact];
  4. //创建一个NSIndexPath对象,插入单元格的位置
  5. NSIndexPath *indexPath = [NSIndexPath indexPathForRow:self.contacts.count - 1 inSection:0];
  6. //表视图中插入单元格
  7. [self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
  8. }

7.4 完整代码

本案例中,TRContactTableViewController.m文件中的完整代码如下所示:

  1. #import "TRContactTableViewController.h"
  2. #import "TRInputViewController.h"
  3. @interface TRContactTableViewController () <TRInputViewDelegate>
  4. @property(nonatomic, strong) NSMutableArray *contacts;
  5. @end
  6. @implementation TRContactTableViewController
  7. - (NSMutableArray *)contacts
  8. {
  9. if(!_contacts){
  10. _contacts = [@[] mutableCopy];
  11. }
  12. return _contacts;
  13. }
  14. //实现TRInputViewDelegate协议方法
  15. -(void)inputViewController:(TRInputViewController *)inputVC
  16. contact:(TRContact *)contact
  17. {
  18. [self.contacts addObject:contact];
  19. NSIndexPath *indexPath = [NSIndexPath indexPathForRow:self.contacts.count - 1 inSection:0];
  20. [self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
  21. }
  22. - (void)viewDidLoad
  23. {
  24. [super viewDidLoad];
  25. self.title = @"通讯录";
  26. self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(addContact:)];
  27. }
  28. - (void)addContact:(UIBarButtonItem *)sender
  29. {
  30. TRInputViewController *inputVC = [[TRInputViewController alloc]initWithNibName:@"TRInputViewController" bundle:nil];
  31. inputVC.delegate = self;
  32. UINavigationController *navi = [[UINavigationController alloc]initWithRootViewController:inputVC];
  33. [self presentViewController:navi animated:YES completion:nil];
  34. }
  35. -(NSInteger)tableView:(UITableView *)tableView
  36. numberOfRowsInSection:(NSInteger)section
  37. {
  38. return self.contacts.count;
  39. }
  40. -(UITableViewCell *)tableView:(UITableView *)tableView
  41. cellForRowAtIndexPath:(NSIndexPath *)indexPath
  42. {
  43. static NSString *CellIdentifier = @"Cell";
  44. UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
  45. if (cell == nil) {
  46. cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier];
  47. }
  48. TRContact *contact = self.contacts[indexPath.row];
  49. cell.textLabel.text = contact.name;
  50. cell.detailTextLabel.text = contact.phoneNumber;
  51. return cell;
  52. }
  53. @end
时间: 2024-10-27 11:21:33

表视图控制器(TableViewController)(一)的相关文章

表视图控制器(TableViewController)(三) 、 表视图搜索

1 乐库的设置界面 1.1 问题 tableView分为静态(static)和动态(dynamic),之前使用的都是动态的tableView,表视图的有多少分区.有多少行以及每一行显示的内容都不是固定的,都由数据模式来决定.而静态的tableView有多少分区.有多少行以及每一行显示的内容都是固定不变的. 静态tableView应用广泛,常用于各种应用软件的设置界面,本案例使用静态tableView完成乐库的设置界面,如图-1所示: 图-1 1.2 方案 由图-1可以看到该界面的显示内容是固定不

表视图控制器(TableViewController)(二)

1 tableView的编辑模式 1.1 问题 表视图可以进入编辑模式,当进入编辑模式就可以进行删除.插入.移动单元等操作,本案例还是使用联系人界面学习如何进入编辑模式,以及进入编辑模式之后的删除.插入.移动等操作,如图-1所示: 图-1 1.2 方案 首先还是创建一个带导航的TRContactTableViewController对象做为根视图控制器. 其次创建一个TRContact类用于管理联系人信息,有两个NSString类型的属性分别为name和phoneNumber,本案例为了学习方便

IOS学习之——表视图 给tableViewController添加悬浮窗口

前言 在IOS中,UITableViewController不如UIViewController用的方便,遇到了一个需求:在TableView中添加一个悬浮按钮,不随TableView滑动而滑动.这个需求在UIViewController里面很好实现,给self.view 添加子视图,再把子视图放到最上方即可.可是在表视图控制器中,就很难办,因为控制器中没有作为tableView的父视图的view存在,而把button作为tableView的子视图出现呢,则会随着table的滑动而滑动(⊙﹏⊙b

第09章 导航控制器和表视图

总结: 其实navigationController很简单,其往往就是应用程序委托的根视图控制器 且其往往就是通过第一个显示的视图控制器来初始化自己. 而后的工作,就是pushViewController,popViewController... [self.navigationController   pushViewController: controller animated:YES]; [self.navigationController popViewController Animat

表视图搜索栏

一.效果 1. 启动程序:上面搜索栏.右边索引条 2. 点击表单元有警告框提示 3. 点击搜索栏输入内容可根据长短范围搜索 二.分析 1. 创建一个表视图,指定委托,实现表单元的显示 2. 创建一个显示搜索结果的模型,该模型也是表视图,遵循搜索结果更新协议,作为更新器 3. 创建一个搜索控制器,指定搜索结果控制器,并创建搜索栏,让搜索结果控制器实现同步更新协议,让搜索到的内容得以在搜索结果中更新 4. 在更新协议实现方法中配置搜索的逻辑 三.实现 1. 实现文件 2. Main.storyboa

UI学习笔记---第十天UITableView表视图编辑

UITableView表视图编辑 表视图编辑的使用场景 当我们需要手动添加或者删除某条数据到tableView中的时候,就可以使用tableView编辑.比如微信 扣扣中删除和某人的通话 当我们需要手动调整单元格的顺序时,就可以通过tableView移动,移动单元格到指定位置 代理AppDelegate.m中代码 #import "AppDelegate.h" #import "RootViewController.h" @implementation AppDel

iOS学习之视图控制器

一.自定义视图(label-textField组合视图) 1.自定义视图:系统标准UI之外,自己组合出的新的视图. 2.优点:iOS提供了很多UI组件,借助它们我们可以实现不同的功能.尽管如此,实际开发中,我们还需自定义视图.积累自己的代码库,方便开发.自己封装的视图,能像UI空间一样,用于别的项目中,能大大降低开发成本,提高开发效率. 3.高质量代码的特点:可复用,可移植,精炼等.(高内聚,低耦合). 4.自定义视图步骤 根据需求的不同,自定义视图继承的类也有所不同.一般自定义的视图会继承于U

视图控制器、模态视图

一.什么是试图控制器? 视图控制器就是用来管理视图的加载.卸载.横屏竖屏显示等操作的控制器. 每一个界面都会由一个控制器来管理显示 ,单独的界面view需要视图控制器这个载体来管理显示. UIViewController是所有视图控制器的父类 . iOS提供了许多内置的视图控制器类,以支持标准的用户界面部分,比如导航控制器 (UINavigationController),标签栏控制器(UITabBarController),表视图控制器 (UITableViewController)等 . 每

UI学习笔记---第十一天UITableView表视图高级-自定义cell

自定义cell,多类型cell混合使用,cell自适应高度 自定义cell就是创建一个UITableViewCell的子类 把cell上的空间创建都封装在子类中,简化viewController中的代码 cell中的空间如何显示Model中的信息 cell中声明一个Model类型的属性,viewController中获取到Model对象后赋值给cell的Model属性 cell中重写Model的setter方法,把Model对象中的内容重新赋值给各个控件 M和V不直接通信,C负责M和V之间进行通