09 uitableview and uitableviewcontroller

42
UITableView UITableViewController 范圣刚,[email protected] , www.tfan.org

Upload: tom-fan

Post on 22-Apr-2015

998 views

Category:

Technology


11 download

DESCRIPTION

 

TRANSCRIPT

Page 1: 09 UITableView and UITableViewController

UITableView 和 UITableViewController

范圣刚,[email protected], www.tfan.org

Page 2: 09 UITableView and UITableViewController

使⽤用 UITableView 的例⼦子

Page 3: 09 UITableView and UITableViewController

开始构建 Homepwner

•Homepwner: ⼀一个库存管理的⼩小程序

•第⼀一阶段⺫⽬目标:把前⾯面我们实现的 BNRItem 在 UITableView 中展⽰示出来

•新建⼀一个 Empty Application 项⺫⽬目,命名为 Homepwner,类前缀:Homepwner

Page 4: 09 UITableView and UITableViewController

UITableViewController

• UITableView 是 ⼀一个 view 对象,知道如何对⾃自⾝身进⾏行绘制;但并不处理程序逻辑或数据。

•要使 table 能够正常⼯工作,下⾯面这些是必要的:• view controller - 控制 UITableView 在屏幕上的样⼦子

• data source - 有多少⾏行数要显⽰示,以及这些⾏行显⽰示的数据。UITableView 的 dataSource 可以是任何 Objective-C 对象,只要符合 UITableViewDataSource protcol

• delegate - UITableView ⼀一般需要⼀一个 delegate,可以通知其他对象涉及 UITableView 的⼀一些事件。这个 delegate 要遵循 UITableViewDelegate protocol

•⼀一个 UITableViewController 类的实例就可以填补这三种⾓角⾊色:view controller, data source 和 delegate

Page 5: 09 UITableView and UITableViewController

UITableViewController 和 UITableView

•因为 UITableViewController 是 UIViewController 的⼀一个⼦子类,所以 UITableViewController 拥有⼀一个 view

•UITableViewController 的 view 总是 UITableView 的⼀一个实例,并且 UITableViewController 处理 UITableView 的呈现和准备⼯工作

•当 UITableViewController ⽣生成它的 view 时,UITableView 的 dataSource 和 delegate 实例变量被⾃自动设成指向 UITableViewController

Page 6: 09 UITableView and UITableViewController

UITableViewController 和 UITableView 关系⽰示意图

Page 7: 09 UITableView and UITableViewController

⼦子类化 UITableViewController

•为 Homepwner 编写⼀一个 UITableViewController 的⼦子类• File -> New -> File, iOS -> Cocoa Touch -> Objective-C

class, 命名:ItemsViewController,⽗父类是: UITableViewController

Page 8: 09 UITableView and UITableViewController

•UITableViewController 的 designated initializer 是 initWithStyle:,使⽤用⼀一个常量来确定 table view 的样式,有两个选项:

• UITableViewStylePlain: 每⾏行是⼀一个矩形

• UITableViewStyleGrouped: 顶部和底部⾏行具有圆⾓角

•实现下列的 initializers: init 和 initWithStyle

Page 9: 09 UITableView and UITableViewController

•这就确保了不管发送什么初始化消息给 ItemsViewController, 所有的 ItemsViewController 的实例都使⽤用 UITableViewStyleGrouped 样式。

- (id)init{ // 调⽤用超类的 designated initializer self = [super initWithStyle:UITableViewStyleGrouped]; if (self) { } return self;}

- (id)initWithStyle:(UITableViewStyle)style{ return [self init];}

Page 10: 09 UITableView and UITableViewController

•打开 HomepwnerAppDelegate.m, 在 application:didFinishLaunchingWithOptions: 中,创建⼀一个 ItemsViewController 的实例并把它设成 window 的 rootViewController

•确保在⽂文件顶部导⼊入 ItemsViewController 头⽂文件#import "ItemsViewController.h"

@implementation HomepwnerAppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{ self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; // Override point for customization after application launch. ItemsViewController *ivc = [[ItemsViewController alloc] init]; [[self window] setRootViewController:ivc]; self.window.backgroundColor = [UIColor whiteColor]; [self.window makeKeyAndVisible]; return YES;}

Page 11: 09 UITableView and UITableViewController

空⽩白的 UITableView

•让每⾏行显⽰示⼀一个 BNRItem 的实例

•把前⾯面实现的 BNRItem 的头⽂文件和实现⽂文件拖到 Homepwner 的 project navigator

Page 12: 09 UITableView and UITableViewController

UITableView 的 Data Source

Page 13: 09 UITableView and UITableViewController

• table view 会询问另⼀一个对象 - 它的 dataSource - 应该显⽰示什么

• ItemsViewController 就是 data source,因此需要⼀一种⽅方法来存储 item 数据

•我们在前⾯面使⽤用⼀一个 NSMutableArray 来存储 BNRItem 实例,现在还是使⽤用 NSMutableArray,但是会把它抽象成另⼀一个对象 - BNRItemStore

Page 14: 09 UITableView and UITableViewController

Homepwner 对象⽰示意图

Page 15: 09 UITableView and UITableViewController

• BNRItemStore 本⾝身包含⼀一个数组,它负责在这个数组上执⾏行操作,像排序,增加和删除 BNRItem

•也将负责从磁盘保存和加载 BNRItem

•下⾯面我们开始创建 BNRItemStore 对象

BNRItemStore 介绍

Page 16: 09 UITableView and UITableViewController

创建 BNRItemStore

•创建⼀一个新的 NSObject 的⼦子类,命名:BNRItemStore

•增加⼀一个类⽅方法:defaultStore: ,实现单例模式

+ (BNRItemStore *)defaultStore{ static BNRItemStore *defaultStore = nil; if (!defaultStore) { defaultStore = [[super allocWithZone:nil] init]; } return defaultStore;}

#import <Foundation/Foundation.h>

@interface BNRItemStore : NSObject

// 增加⼀一个类⽅方法,前缀 ++ (BNRItemStore *)defaultStore;

@end

Page 17: 09 UITableView and UITableViewController

静态变量•静态变量不在栈内存放,当⽅方法返回时不会被销毁

•静态变量会在程序被加载时只声明⼀一次,并且永远不会被销毁

•静态变量(static variable)有点像本地变量,只能从声明它的⽅方法内访问它。因此没有其他的对象或⽅方法可以使⽤用由这个变量指向的 BNRItemStore, 除了通过 defaultStore ⽅方法

Page 18: 09 UITableView and UITableViewController

静态变量指向的对象• defaultStore 的初始值是 nil

•这个⽅方法第⼀一次被调⽤用的时候呢,⼀一个 BNRItemStore 的实例将被创建,并且 defaultStore 将会指向它

•在后续的对这个⽅方法的调⽤用中,defaultStore 仍将指向 BNRItemStore 的这个实例

•这个变量具有对 BNRItemStore 的强引⽤用,并且由于这个变量永远不会被销毁,因此它指向的对象也永远不会被销毁

Page 19: 09 UITableView and UITableViewController

确保 BNRItemStore 的单例状态•就要确保不会有另⼀一个 BNRItemStore 被分配

•⼀一种⽅方法是重写 BNRItemStore 中的 alloc ,让它不创建⼀一个新的实例,⽽而是返回⼀一个已经存在的实例

•这种⽅方法有⼀一个问题就是:因为历史的原因, alloc 仅仅是对 allocWithZone: 的调⽤用,allocWithZone: 然后调⽤用 C 函数 NSAllocateObject,NSAllocateObject 再执⾏行真正的内存分配

•为了防⽌止跳过 alloc: 直接调⽤用 allocWithZone:, 我们要对 allocWitheZone: 进⾏行重写

Page 20: 09 UITableView and UITableViewController

重写 initWithZone:// zone 参数在 Objective-C ⾥里⾯面是没有⽤用的,忽略即可+ (id)allocWithZone:(NSZone *)zone{ return [self defaultStore];}

•调⽤用 NSObject 的 allocWithZone: 实现, ⽽而不是⾃自⾝身的 allocWithZone:

• defaultStore = [[super allocWithZone:nil] init];

Page 21: 09 UITableView and UITableViewController

默认的 allocation chain

Page 22: 09 UITableView and UITableViewController

BNRItemStore 和 NSObject allocation ⽅方法

Page 23: 09 UITableView and UITableViewController

保存 BNRItem 的数组•通过上⾯面的操作,我们就确保了不会创建多个

BNRItemStore 的实例,同时我们也确保了⼀一旦 BNRItemStore 的实例被创建,它就不会被销毁,因为⼀一个永远不会被销毁的静态变量⼀一直维护对它的 ownership

•下⾯面我们创建⼀一个⽤用于保存 BNRItem 的数组,和两个⽅方法

Page 24: 09 UITableView and UITableViewController

@class

• @class 指⽰示符告诉编译器有⼀一个 BNRItem 类,并且在当前⽂文件不需要知道这个类的细节

• 这样就允许我们在没有导⼊入 BNRItem.h 的情况下也可以在 createItem 声明中使⽤用 BNRItem 符号

• 使⽤用 @class 可以加快编译速度,同时还可以避免⼀一些其他问题

#import <Foundation/Foundation.h>

@class BNRItem;

@interface BNRItemStore : NSObject{ NSMutableArray *allItems;}

// 增加⼀一个类⽅方法,前缀 ++ (BNRItemStore *)defaultStore;

- (NSArray *)allItems;- (BNRItem *)createItem;

@end

Page 25: 09 UITableView and UITableViewController

创建数组并实现相应⽅方法•在真正给 BNRItem 类发送消息或者对它进⾏行实例化的类中,必须导⼊入声明它的⽂文件,这样编译器才能知道它的细节

•在 BNRItemStore.m 中,导⼊入 BNRItem.h

•在 BNRItemStore.m 中,重写 init 来⽣生成⼀一个 NSMutableArray 的实例并把它分配给实例变量

•并且实现前⾯面声明的两个⽅方法

Page 26: 09 UITableView and UITableViewController

代码#import "BNRItemStore.h"// 导⼊入 BNRItem 头⽂文件#import "BNRItem.h"

@implementation BNRItemStore

- (id)init{ self = [super init]; if (self) { allItems = [[NSMutableArray alloc] init]; } return self;}

- (NSArray *)allItems{ return allItems;}

- (BNRItem *)createItem{ BNRItem *p = [BNRItem randomItem]; [allItems addObject:p]; return p;}

Page 27: 09 UITableView and UITableViewController

实现数据源⽅方法

Page 28: 09 UITableView and UITableViewController

•导⼊入头⽂文件•在 designated initializer 中增加5个随机条⺫⽬目到

BNRItemStore

•UITableViewDataSource

• tableView:numberOfRowsInSection:

• tableView:cellForRowAtIndexPath:

Page 29: 09 UITableView and UITableViewController

在 designated initializer 中增加5个随机条⺫⽬目到 BNRItemStore

#import "ItemsViewController.h"#import "BNRItemStore.h"#import "BNRItem.h"

@implementation ItemsViewController

- (id)init{ self = [super initWithStyle:UITableViewStyleGrouped]; if (self) { for (int i = 0; i < 5; i++) { [[BNRItemStore defaultStore] createItem]; } } return self;}

Page 30: 09 UITableView and UITableViewController

UITableViewDataSource 必须实现的⽅方法

Page 31: 09 UITableView and UITableViewController

获得⾏行数

Page 32: 09 UITableView and UITableViewController

tableView:numberOfRowsInSection:- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ return [[[BNRItemStore defaultStore] allItems] count];}

•第⼆二个必须要实现的⽅方法是:tableView:cellForRowAtIndexPath:

•要实现这个⽅方法,我们要先了解另外⼀一个类 - UITableViewCell

Page 33: 09 UITableView and UITableViewController

UITableViewCells

Page 34: 09 UITableView and UITableViewController

•UITableViewCell 是 UIView 的⼦子类,并且 UITableView 的每⼀一⾏行都是⼀一个 UITableViewCell

• iOS 中的 table 只能有⼀一列,所以⼀一⾏行只有⼀一个单元格

•UITableViewCells 都是 UITableView 的⼦子视图

Page 35: 09 UITableView and UITableViewController

UITableViewCell 的布局

•每个单元格本⾝身有⼀一个⼦子视图 - contentView

• contentView 是单元格内容的⽗父视图

•它也可以绘制⼀一个 accessory indicator,accessory indicator 显⽰示⼀一个⾏行为导向的 icon,例如复选标记

•可以通过预定义的常量访问,默认是 UITableViewCellAccessoryNone

Page 36: 09 UITableView and UITableViewController

UITableViewCell 的层次结构•对于 UITableViewCell, 我们真正关⼼心的是 contentView 的三个⼦子视图

•其中两个是 UILabel 实例

• textLabel

• detailTextLabel

•第三个⼦子视图是叫做 imageView 的 UIImageView

Page 37: 09 UITableView and UITableViewController

UITableViewCellStyle

每个单元格也有⼀一个 UITableViewCellStyle ⽤用于确定哪个⼦子视图被使⽤用以及它们在 contentView 中的位置

Page 38: 09 UITableView and UITableViewController

创建和获取 UITableViewCells

•每个单元格将显⽰示 BNRItem 的 description,作为它的 textLabel

•我们将实现 UITableViewDataSource protocol 的第⼆二个必要⽅方法:tableView:cellForRowAtIndexPath:

•这个⽅方法将创建⼀一个单元格,把它的 textLabel 设成对应的 BNRItem 的 description,然后把它返回给 UITableView

Page 39: 09 UITableView and UITableViewController

UITableViewCell 的检索

Page 40: 09 UITableView and UITableViewController

实现单元格的获取

• tableView:cellForRowAtIndexPath: 的⼀一个参数是 NSIndexPath, 它具有两个属性:section 和 row

•因为我们只有⼀一个section,我们就让第 n ⾏行显⽰示 allItems 数组中的第 n 条记录

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ // 使⽤用默认的外观⽣生成⼀一个 UITableViewCell 的实例 UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"UITableViewCell"]; // 使⽤用 item 的 description 设置单元格的⽂文本 BNRItem *p = [[[BNRItemStore defaultStore] allItems] objectAtIndex:[indexPath row]]; [[cell textLabel] setText:[p description]]; return cell;}

Page 41: 09 UITableView and UITableViewController

复⽤用 UITableViewCells

•只需要⾜足够的单元格来填满屏幕

•移出屏幕的单元格被放到了⼀一个可以复⽤用的单元格池

•不同类型的单元格和 reuseIdenti!er 属性

• reuse identi!er ⼀一般可以⽤用单元格类的名字,同⼀一类型,不同 style 的单元格可以使⽤用 style 作为后缀

Page 42: 09 UITableView and UITableViewController

单元格复⽤用的实现- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ // ⾸首先检查是否有可以重⽤用的单元格,如果存在的化就使⽤用 UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"UITableViewCell"]; if (!cell) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"UITableViewCell"]; } // 使⽤用 item 的 description 设置单元格的⽂文本 BNRItem *p = [[[BNRItemStore defaultStore] allItems] objectAtIndex:[indexPath row]]; [[cell textLabel] setText:[p description]]; return cell;}