15 subclassing uitableviewcell

67
创建 UITableViewCell 的类 范圣刚,[email protected] , www.tfan.org

Upload: tom-fan

Post on 04-Dec-2014

1.170 views

Category:

Technology


3 download

DESCRIPTION

子类化 UITableViewCell

TRANSCRIPT

Page 1: 15 Subclassing UITableViewCell

创建 UITableViewCell 的⼦子类

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

Page 2: 15 Subclassing UITableViewCell

•UITableView 显⽰示的是⼀一个 UITableViewCell 的列表。对于⼤大多数应⽤用⽽而⾔言,带有 textLabel, detailTextLabel, 和 imageView 的基本单元格够⽤用

•有时候我们想要能够显⽰示更详细信息,或者⼀一种不同布局的单元格,这时候我们就要⼦子类化 UITableViewCell

Page 3: 15 Subclassing UITableViewCell

HomepwnerItemCell

•这⼀一节我们来创建⼀一个名为 HomepwnerCell 的可以更有⼒力的显⽰示 BNRItem 实例的 UITableViewCell 的⼦子类

•每⼀一个单元格将会显⽰示 BNRItem 的名称,以美元记的价格,以及它的图⽚片的⼀一个缩略图

Page 4: 15 Subclassing UITableViewCell

创建 HomepwnerItemCell

Page 5: 15 Subclassing UITableViewCell

UIView 和 UITableViewCell 的⼦子类化•UITableViewCell 也是 UIView 的⼀一个⼦子类。通常我们⼦子类化 UIView (以及任何它的⼦子类)的时候,我们会重写它的 drawRect: ⽅方法来⾃自定义 view 的外形

•但是⼦子类化 UITableViewCell 的时候,我们并不直接修改单元格的外观

Page 6: 15 Subclassing UITableViewCell

⼦子类化 UITableViewCell 的⽅方法•每个单元格都有⼀一个名为 contentView 的⼦子视图,它是构成⼀一个单元格⼦子类布局的视图对象的容器

•也就是说我们可以通过改变在单元格的 contentView 中的视图对象来⼦子类化 UITableViewCell。

Page 7: 15 Subclassing UITableViewCell

•⽐比⽅方说,我们可以创建 UITextField,UILabel 和 UIButton 的实例并把它们加到 contentView

•甚⾄至我们可以创建⼀一个 UIView 的⼦子类,重写它的 drawRect: ,然后添加⼀一个它的实例到 contentView

Page 8: 15 Subclassing UITableViewCell

HomepwnerItemCell hierarchy

Page 9: 15 Subclassing UITableViewCell

•把⼦子视图添加到 contentView ,⽽而不是直接添加到 UITableViewCell 的⼦子类很重要。因为单元格在⼀一个特定的时候将会调整 contentView 的⼤大⼩小。

•例如,当 table view 进⼊入编辑模式时,contentView 会调整它的⼤大⼩小给编辑控件腾出空间。

为什么要加到 contentView?

Page 10: 15 Subclassing UITableViewCell

•如果我们直接把⼦子视图加到了 UITableViewCell, 编辑控件将会遮掩这些⼦子视图。

•单元格在进⼊入编辑模式时不能调整它的⼤大⼩小,但是 contentView 可以并且也是这么做的。

Page 11: 15 Subclassing UITableViewCell

HomepwnerItemCell 类

•打开 Homepwner.xcodeproj。⽣生成⼀一个新的 UITableViewCell 的⼦子类,命名为 HomepwnerItemCell

Page 12: 15 Subclassing UITableViewCell

配置 UITableViewCell ⼦子类的界⾯面

Page 13: 15 Subclassing UITableViewCell

•创建⼀一个空的 XIB ⽂文件,命名为 HomepwnerItemCell.xib (Device Family 没有关系)

•打开 HomepwnerItemCell.xib 并且拖动⼀一个 UITableViewCell 实例到绘制区域

•这个单元格需要显⽰示三个⽂文本元素和⼀一个图⽚片,所以我们拖拽三个 UILabel 和 ⼀一个 UIImageView 到单元格上,分别显⽰示名称,价格,序列号和它的图⽚片的缩略图(把序列号字体调稍⼩小点,颜⾊色深灰)

Page 14: 15 Subclassing UITableViewCell

•⾸首先我们来看⼀一下单元格⼤大⼩小的问题。•虽然单元格在 XIB ⽂文件上具有特定的⼤大⼩小,但是我们并不知道在应⽤用程序⾥里⾯面实际的宽度和⾼高度会是多少。

单元格上控件的⼤大⼩小问题

Page 15: 15 Subclassing UITableViewCell

•因为我们的应⽤用将会运⾏行在 iPhone 和 iPad 上,⽽而且在 iPad 还有可能是 portrait 和 landscape oritentation。

•单元格需要进⾏行横向调整来匹配它所在的窗⼝口⼤大⼩小。这样我们就必须针对每个⼦子视图设置它的 autosizing mask

•在 size inspector ⾥里⾯面,我们来按照下⼀一⻚页的图⽰示来更改 subview 的 autosizing mask

Page 16: 15 Subclassing UITableViewCell

HomepwnerItemCell 的Autoresizing masks

• image,name 和 serialNumber 位置都固定在左上⾓角;价格标签位置固定在右上⾓角;

• name 和 serialNumber 宽度横向延伸

Page 17: 15 Subclassing UITableViewCell

更改 Class

•最后,我们点击 outline view 中的 cell,选择 identity inspector。把它的 Class 改成 HomepwnerItemCell

Page 18: 15 Subclassing UITableViewCell

导出 HomepwnerItemCell 的属性

Page 19: 15 Subclassing UITableViewCell

•现在这个单元格视觉效果看起来还不错,但是我们必须让它能够被⽤用起来。

•也就是说当我们在 tableView:cellForRowAtIndexPath: ⾥里⾯面创建⼀一个 HomepwnerItemCell 的实例时,我们需要能够设置每⼀一个 label 的 text 属性,以及 UIImageView 的 image。

Page 20: 15 Subclassing UITableViewCell

•下⼀一步我们就是要来为每⼀一个⼦子视图创建和连接 HomepwnerItemCell 上的 outlets。

•我们还是使⽤用前⼏几节⽤用到的 Controll-dragging 到源⽂文件的⽅方式来创建它们的 outlets。

•但是对于 HomepwnerItemCell 的 outlets,情况⼜又有稍许不同:它们将会是 properites,⽽而不是简单的实例变量。

Page 21: 15 Subclassing UITableViewCell

•在 DetailViewController 中,没有 UITextFields 的 outlets 被曝露为属性,因为没有其它的对象会访问它们。

•在我们这种情况下,table view 的 data source 必须配置每⼀一个 subview。通过把这些 subviews 导出为 properties,数据源(ItemsViewController)在需要的时候将可以访问。

Page 22: 15 Subclassing UITableViewCell

•在 HomepwnerItemCell.xib ⽂文件打开的时候,在 HomepwnerItemCell.h 上 Option-click 。

•从每⼀一个 subview Control-drag 到 HomepwnerItemCell.h 的⽅方法声明区域。

•然后给每⼀一个 outlet 命名并且配置连接的每⼀一个属性,让它们如下图所⽰示:

Page 23: 15 Subclassing UITableViewCell

Connections

Page 24: 15 Subclassing UITableViewCell

•操作结束后的 HomepwnerItemCell.h 看起来应该像下⾯面这样:

@interface HomepwnerItemCell : UITableViewCell

@property (weak, nonatomic) IBOutlet UIImageView *thumbnailView;@property (weak, nonatomic) IBOutlet UILabel *nameLabel;@property (weak, nonatomic) IBOutlet UILabel *serialNumberLabel;@property (weak, nonatomic) IBOutlet UILabel *valueLabel;

@end

Page 25: 15 Subclassing UITableViewCell

使⽤用 HomepwnerItemCell

Page 26: 15 Subclassing UITableViewCell

•在 ItemsViewController 的 tableview:cellForRoAtIndexPath: ⽅方法中,我们将为表格中的每⼀一⾏行创建⼀一个 HomepwnerItemCell 的实例。

•在 ItemsViewController.h 的顶部,导⼊入 HomepwnerItemCell 的头⽂文件

#import "HomepwnerItemCell.h"

Page 27: 15 Subclassing UITableViewCell

•在之前的 tableview:cellForRowAtIndexPath: 的实现中,我们⾸首先会询问 table view 它是否有可重⽤用的 cell,然后如果没有的话我们再⽣生成⼀一个全新的

•当使⽤用⼀一个 XIB ⽂文件来加载 UITableViewCell 的⼦子类时,这个过程稍有不同:我们在 table 第⼀一次加载时使⽤用⼀一个给定的 reuse identi!er 向 UITableView 来注册这个 XIB ⽂文件。

Page 28: 15 Subclassing UITableViewCell

•在 ItemsViewController.m 中 重写 viewDidLoad 来为 HomepwnerItemCell reuse identi!er 注册 HomepwnerItemCell.xib

// 重写 viewDidLoad 为 HomepwnerItemCell 复⽤用标识注册 HomepwnerItemCell.xib - (void)viewDidLoad{ [super viewDidLoad]; // 加载 xib ⽂文件 UINib *nib = [UINib nibWithNibName:@"HomepwnerItemCell" bundle:nil]; // 注册这个包含 cell 的 NIB [[self tableView] registerNib:nib forCellReuseIdentifier:@"HomepwnerItemCell"];}

Page 29: 15 Subclassing UITableViewCell

•要得到⼀一个 HomepwnerItemCell 的实例,我们向 table view 请求 dequeue ⼀一个符合 HomepwnerItemCell reuse identi!er 的 Cell。

•如果 table 有⼀一个可复⽤用的 HomepwnerItemCell 的实例,就会返回它;如果没有的话,就会加载 HomepwnerItemCell.xib ,并且给你⼀一个 archived cell 的实例。

•在 ItemsViewController.m 中,定位 tableView:cellForRowAtIndexPath: 并进⾏行如下修改:

Page 30: 15 Subclassing UITableViewCell

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{// // ⾸首先检查是否有可以重⽤用的单元格,如果存在的化就使⽤用// UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"UITableViewCell"];// if (!cell) {// cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"UITableViewCell"];// }// else {// NSLog(@"重⽤用: %d", [indexPath row]);// } BNRItem *p = [[[BNRItemStore defaultStore] allItems] objectAtIndex:[indexPath row]];// [[cell textLabel] setText:[p description]]; // 把原来相关的复⽤用代码删掉 // 获得⼀一个新的或者循环使⽤用的 cell HomepwnerItemCell *cell = [tableView dequeueReusableCellWithIdentifier:@"HomepwnerItemCell"]; // 使⽤用上⾯面的 BNRItem 配置这个单元格 [[cell nameLabel] setText:[p itemName]]; [[cell serialNumberLabel] setText:[p serialNumber]]; [[cell valueLabel] setText:[NSString stringWithFormat:@"$%d", [p valueInDollars]]]; return cell;}

Page 31: 15 Subclassing UITableViewCell

•构建并运⾏行,单元格将会如下图所⽰示:

•要注意的两个问题:第⼀一,我们前⾯面使⽤用 UINib 来进⾏行注册,XIB 的初始化底层调⽤用的也是 Nib;第⼆二,我们前⾯面的 XIB ⽂文件没有设置 File’s Owner, 因为 table view 注册的时候只会扫描 UITableViewCell。所以 XIB ⽂文件⾥里⾯面也不能放置多个 UITableViewCell。

Page 32: 15 Subclassing UITableViewCell

图像操作

Page 33: 15 Subclassing UITableViewCell

•要在单元格⾥里⾯面使⽤用图像,我们可以使⽤用 image 的缩略图。

•要创建⼀一个 BNRItem 图像的缩略图,我们需要绘制⼀一个全尺⼨寸图⽚片 scale-down 的版本到 offscreen context, 然后在 BNRItem 实例中保持⼀一个指向这个新图⽚片的指针。

Page 34: 15 Subclassing UITableViewCell

•同样我们也需要⼀一个位置来存储这个缩略图,以便在系统重启后可以重新加载。

•全尺⼨寸的图⽚片我们保存在 BNRImageStore 中,必要的时候我们可以把它 #ush 掉;⽽而缩略图⾜足够⼩小,所以我们可以直接把它和 BNRItem 的其他实例变量⼀一起 archive 起来。

Page 35: 15 Subclassing UITableViewCell

•问题是缩略图是 UIImage 的⼀一个实例,⽽而 UIImage 并不符合 NSCoding 的 protocol,所以我们⽆无法在 NSCode 中直接对缩略图进⾏行编码。

•我们可以把缩略图作为数据(PNG 格式)进⾏行编码,并且把它封装在⼀一个 NSData 对象中,NSData 是符合 NSCoding 的。

Page 36: 15 Subclassing UITableViewCell

•打开 BNRItem.h , 声明两个新的属性:⼀一个 UIImage,⼀一个 NSData。

•同样我们还需要⼀一个⽅方法来把全尺⼨寸的图⽚片转换成⼀一个缩略图。

•在 BNRItem.m 中,synthesize 这两个属性。@interface BNRItem : NSObject <NSCoding>{}// 分别保存图⽚片和数据的两个属性,以及⼀一个把全尺⼨寸图⽚片转成缩略图的⽅方法@property (nonatomic, strong) UIImage *thumbnail;@property (nonatomic, strong) NSData *thumbnailData;

- (void)setThumbnailDataFromImage:(UIImage *)image;

@synthesize thumbnail, thumbnailData;

Page 37: 15 Subclassing UITableViewCell

•当我们为 BNRItem 选择⼀一个图⽚片的时候,我们就把这个图⽚片赋给了 BNRItem。同时我们把它转成⼀一个缩略图,保存成它的 thumbnail。

•同时还会⽣生成 PNG 格式的 NSData,并且把它保存成 thumbnailData。

• thumbnailData 将会随着 BNRItem ⼀一起被 archive,⼀一起被从 archive 中加载,并且从它的数据来重新创建 thumbnail。

Page 38: 15 Subclassing UITableViewCell

•在 BNRItem.m 中,为 thumbnail ⽣生成⼀一个 getter ⽅方法,必要的时候从数据⽣生成它。

// ⾃自定义 getter ⽅方法- (UIImage *)thumbnail{ if (!thumbnailData) { return nil; } // 如果还没有从数据中⽣生成缩略图,创建它 if (!thumbnail) { // 从数据⽣生成图⽚片 thumbnail = [UIImage imageWithData:thumbnailData]; } return thumbnail;}

Page 39: 15 Subclassing UITableViewCell

•现在我们来实现 setThumbnailDataFromImage: ⽅方法

•这个⽅方法将把⼀一个全尺⼨寸图⽚片⽣生成为缩略图,然后把 thumbnail 指向这个图⽚片

Page 40: 15 Subclassing UITableViewCell

- (void)setThumbnailDataFromImage:(UIImage *)image{ CGSize origImageSize = [image size]; // thumnail 的矩形⼤大⼩小 CGRect newRect = CGRectMake(0, 0, 40, 40); // 计算缩放⽐比 float ratio = MAX(newRect.size.width / origImageSize.width, newRect.size.height / origImageSize.height); // ⽣生成⼀一个带缩放因⼦子的透明位图上下⽂文 UIGraphicsBeginImageContextWithOptions(newRect.size, NO, 0.0); // ⽣生成⼀一个圆⾓角矩形路径 UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:newRect cornerRadius:5.0]; // 后续绘制 clip 到这个圆⾓角矩形 [path addClip]; // 图⽚片放到缩略图中间 CGRect projectRect; projectRect.size.width = ratio * origImageSize.width; projectRect.size.height = ratio * origImageSize.height; projectRect.origin.x = (newRect.size.width - projectRect.size.width) / 2.0; projectRect.origin.y = (newRect.size.height - projectRect.size.height) / 2.0; // 把图⽚片绘制上来 [image drawInRect:projectRect]; // 从图⽚片上下⽂文获得图⽚片,作为我们的缩略图保存 UIImage *smallImage = UIGraphicsGetImageFromCurrentImageContext(); [self setThumbnail:smallImage]; // 得到该图⽚片 PNG 形式,把它作为我们可以 archive 的数据 NSData *data = UIImagePNGRepresentation(smallImage); [self setThumbnailData:data]; // 完成以后,清除图⽚片上下⽂文资源 UIGraphicsEndImageContext();}

Page 41: 15 Subclassing UITableViewCell

•在 DetailViewController.m 中,修改 imagePickerController:didFinishPickingMediaWithInfo:,在采集到原始图⽚片时⽣生成缩略图

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info{ // 如果存在就图⽚片,先从 BNRImageStore 中删除 NSString *oldKey = [item imageKey]; if (oldKey) { [[BNRImageStore sharedStore] deleteImageForkey:oldKey]; } // 从 info 字典中获得拾取的图⽚片 UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage]; // 拾取图⽚片以后⽣生成缩略图 [item setThumbnailDataFromImage:image];

Page 42: 15 Subclassing UITableViewCell

•现在我们已经有了⼀一个可以⽤用在 ItemsViewController 的 table view 的缩略图。

•在 ItemsViewController.m 中,更新 tableView:cellForRowAtIndexPath:

// 使⽤用上⾯面的 BNRItem 配置这个单元格 [[cell nameLabel] setText:[p itemName]]; [[cell serialNumberLabel] setText:[p serialNumber]]; [[cell valueLabel] setText:[NSString stringWithFormat:@"$%d", [p valueInDollars]]]; // 设置缩略图 [[cell thumbnailView] setImage:[p thumbnail]]; return cell;}

构建并运⾏行,测试⼀一下缩略图的⽣生成和显⽰示

Page 43: 15 Subclassing UITableViewCell

•最后我们来把缩略图的数据添加到 archive,修改 BNRItem.m。然后构建并运⾏行,退出再启动应⽤用

- (void)encodeWithCoder:(NSCoder *)aCoder{ [aCoder encodeObject:itemName forKey:@"itemName"]; [aCoder encodeObject:serialNumber forKey:@"serialNumber"]; [aCoder encodeObject:dateCreated forKey:@"dateCreated"]; [aCoder encodeObject:imageKey forKey:@"imageKey"]; [aCoder encodeInt:valueInDollars forKey:@"valueInDollars"]; [aCoder encodeObject:thumbnailData forKey:@"thumbnailData"];}- (id)initWithCoder:(NSCoder *)aDecoder{ self = [super init]; if (self) { [self setItemName:[aDecoder decodeObjectForKey:@"itemName"]]; [self setSerialNumber:[aDecoder decodeObjectForKey:@"serialNumber"]]; [self setImageKey:[aDecoder decodeObjectForKey:@"imageKey"]]; [self setValueInDollars:[aDecoder decodeIntForKey:@"valueInDollars"]]; dateCreated = [aDecoder decodeObjectForKey:@"dateCreated"]; thumbnailData = [aDecoder decodeObjectForKey:@"thumbnailData"]; } return self;}

Page 44: 15 Subclassing UITableViewCell

从 UITableViewCells 转发 action

Page 45: 15 Subclassing UITableViewCell

•我们希望⽤用户点击单元格中的⼀一个缩略图以后能够看到⼀一个全尺⼨寸的图⽚片。

•可以在 thumbnail 上⾯面加⼀一个透明的按钮

•点击这个按钮以后,如果应⽤用运⾏行的 iPad 之上,将会使⽤用 UIPopoverController 显⽰示⼀一个全尺⼨寸的图⽚片。

Page 46: 15 Subclassing UITableViewCell

•打开 HomepwnerItemCell.xib ,在 UIImageView 上⾯面拖拽⼀一个 UIButton,重新调整这个按钮的⼤大⼩小让它正好和 UIIImageView 的⼤大⼩小完全⼀一致。

•然后在 HomepwnerItemCell.h 上 Option-click 打开 assistant editor。从按钮 Control-drag 到⽅方法区域,然后像下⾯面这样配置⼀一下。

- (IBAction)showImage:(id)sender;

Page 47: 15 Subclassing UITableViewCell

•为了不让这个按钮掩盖下⾯面的图⽚片,在 attributes inspector ⾥里⾯面,把按钮的类型改成 Custom。

•默认情况下,⼀一个 custom 的按钮不做任何绘制,也就是说它是⼀一个透明的,上⾯面什么也没有的按钮。

Page 48: 15 Subclassing UITableViewCell

•现在的问题是 HomepwnerItemCell 不是⼀一个 controller,⽽而且也⽆无法访问任何拿到全尺⼨寸图⽚片需要的数据。

•解决⽅方法是给 HomepwnerItemCell ⼀一个到 ItemsViewController 的指针。

•这样在 HomepwnerItemCell 收到从按钮来的action message时,它会发⼀一个新的消息到 ItemsViewController,这样 controller 就可以获取图⽚片并且把它在 UIPopoverController 中显⽰示。

Page 49: 15 Subclassing UITableViewCell

增加指针到单元格⼦子类

Page 50: 15 Subclassing UITableViewCell

•⾸首先给 HomepwnerItemCell 指向 controller 的指针,还有⼀一个指向 cell 所在的 table view 的指针。

•在 HomepwnerItemCell.m 中,synthesizie 这些属性

@property (weak, nonatomic) id controller;@property (weak, nonatomic) UITableView *tableView;

@implementation HomepwnerItemCell@synthesize controller;@synthesize tableView;

Page 51: 15 Subclassing UITableViewCell

•现在我们需要当 cell 被创建时来设置这些属性。

•在 ItemsViewController.m 中,找到 tableView:cellForRowAtIndexPath: ⽅方法,添加下⾯面代码:

HomepwnerItemCell *cell = [tableView dequeueReusableCellWithIdentifier:@"HomepwnerItemCell"]; // 设置 HomepwnerItemCell 中传递消息⽤用的属性 [cell setController:self]; [cell setTableView:tableView]; // 使⽤用上⾯面的 BNRItem 配置这个单元格 [[cell nameLabel] setText:[p itemName]];

Page 52: 15 Subclassing UITableViewCell

转发消息到控制器•现在单元格已经知道它的控制器和它要在上⾯面显⽰示的 table view 了

•当 showImage: 消息被发送到 HomepwnerItemCell 时,我们希望 HomepwnerItemCell 可以告诉 ItemsViewController 为在 index path 位置的单元格显⽰示图⽚片

Page 53: 15 Subclassing UITableViewCell

•在 HomepwnerItemCell.m 中实现 showImage: , 从 table view 拿到它的 index path,并且给 controller 发送 showImage:atIndexPath: 消息

- (IBAction)showImage:(id)sender { // 拿到该⽅方法的名字,“showImage” NSString *selector = NSStringFromSelector(_cmd); // selector 现在变成了 “showImage:atIndexPath:” selector = [selector stringByAppendingString:@"atIndexPath:"]; // 从字符串准备⼀一个 selector SEL newSelector = NSSelectorFromString(selector); // 拿到 indexPath NSIndexPath *indexPath = [[self tableView] indexPathForCell:self];// // 调⽤用 controller 的⽅方法// [[self controller] showImage:sender atIndexPath:indexPath]; // ⾸首先检查 if (indexPath) { if ([[self controller] respondsToSelector:newSelector]) { // 使⽤用 performSelector:withObject:withObject: 发送动态消息 [[self controller] performSelector:newSelector withObject:sender withObject:indexPath]; } }}

Page 54: 15 Subclassing UITableViewCell

•我们先来验证⼀一下是不是⼀一切⼯工作正常,在 ItemViewController.m 中实现 showImage:atIndexPath: 来打印出⺫⽬目前的 index path

•然后构建并运⾏行,点击⼀一个缩略图,看⼀一下控制台消息

// 实现 showImage:atIndexPath:- (void)showImage:(id)sender atIndexPath:(NSIndexPath *)ip{ NSLog(@"即将为 %@ 显⽰示图⽚片", ip);}

Page 55: 15 Subclassing UITableViewCell

在popover controller中显⽰示图像

Page 56: 15 Subclassing UITableViewCell

•现在 ItemsViewController 需要修改 showImage:atIndexPath: 来提取 BNRItem 和按钮被按下的单元格相关联的图⽚片

•然后在 UIPopoverController 中显⽰示它的图⽚片

Page 57: 15 Subclassing UITableViewCell

•要在 popover 中显⽰示⼀一个图⽚片,我们需要⼀一个 UIViewController 作为 popover 的 content view controller,这个 UIViewController 要能够显⽰示图⽚片

•我们新建⼀一个 UIViewController 的⼦子类,命名为:ImageViewController, 选择 UIViewController 作为它的⽗父类,然后勾选“With XIB for user interface”

Page 58: 15 Subclassing UITableViewCell

•打开新建的这个 ImageViewController.xib, ⾸首先拖拽⼀一个 UIScrollView 到 View 上;然后拖拽⼀一个 UIImageView 到 UIScrollView 上

Page 59: 15 Subclassing UITableViewCell

ImageViewController XIB

Page 60: 15 Subclassing UITableViewCell

•在配置像具有这种堆叠在⼀一起的视图的 XIB ⽂文件时,很难对视图进⾏行选择,因为都是完全重叠的。

•这时候我们可以从 outline view ⾥里⾯面的 objects 开始拖拽,⽽而不是从 canvas 区域的可视化部分

•在 ImageViewController.h 中,给 interface 添加⼤大括号,然后建⽴立连接。这些连接应该是 weak ⽅方式的实例变量(imageView, scrollView)

Page 61: 15 Subclassing UITableViewCell

•然后在 ImageViewController.h 中增加⼀一个属性来持有这个 image

@interface ImageViewController : UIViewController{ __weak IBOutlet UIScrollView *scrollView; __weak IBOutlet UIImageView *imageView;}

@property (nonatomic, strong) UIImage *image;@end

Page 62: 15 Subclassing UITableViewCell

•当⼀一个 ImageViewController 的实例被创建时,它会被赋予⼀一个 image。

•显⽰示的时候,它将会调整 imageView 来适应图⽚片,然后告诉 scrollView 来更新它的 content size 来适配。

Page 63: 15 Subclassing UITableViewCell

•⾸首先在 ImageViewController.m 中,synthesize 这个 image 属性

•然后实现 viewWillAppear: 来配置这些视图

@implementation ImageViewController@synthesize image;

- (void)viewWillAppear:(BOOL)animated{ [super viewWillAppear:animated]; CGSize sz = [[self image] size]; [scrollView setContentSize:sz]; [imageView setFrame:CGRectMake(0, 0, sz.width, sz.height)]; [imageView setImage:[self image]];}

Page 64: 15 Subclassing UITableViewCell

• popover 完成以后,我们就可以继续完成我们的 showImage:atIndexPath: 了

•⾸首先在 ItemsViewController.h 中声明它符合 UIPopoverControllerDelegate, 然后给它⼀一个实例变量来持有这个 popover

@interface ItemsViewController : UITableViewController <UIPopoverControllerDelegate>{ UIPopoverController *imagePopover;}

Page 65: 15 Subclassing UITableViewCell

•然后在 ItemsViewController.m 顶部导⼊入需要的头⽂文件

// 为 image popover 导⼊入相应头⽂文件#import "BNRImageStore.h"#import "ImageViewController.h"

Page 66: 15 Subclassing UITableViewCell

•然后完成 showImage:atIndexPath:的实现

// 实现 showImage:atIndexPath:- (void)showImage:(id)sender atIndexPath:(NSIndexPath *)ip{ NSLog(@"即将为 %@ 显⽰示图⽚片", ip); // 实现 image popover if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad ) { // 从 index path 得到 item BNRItem *i = [[[BNRItemStore defaultStore] allItems] objectAtIndex:[ip row]]; NSString *imageKey = [i imageKey]; // 如果没有图⽚片,就不需要显⽰示 UIImage *img = [[BNRImageStore sharedStore] imageForKey:imageKey]; if (!img) { return; } CGRect rect = [[self view] convertRect:[sender bounds] fromView:sender]; // ⽣生成⼀一个新的 ImageViewController 然后设置它的 image ImageViewController *ivc = [[ImageViewController alloc] init]; [ivc setImage:img]; // 使⽤用 ImageViewController ⽣生成⼀一个 popover // 600*600 imagePopover = [[UIPopoverController alloc] initWithContentViewController:ivc]; [imagePopover setDelegate:self]; [imagePopover setPopoverContentSize:CGSizeMake(600, 600)]; [imagePopover presentPopoverFromRect:rect inView:[self view] permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES]; }}

Page 67: 15 Subclassing UITableViewCell

•最后,在 ItemsViewController.m 中,实现⽤用户点击屏幕上任何位置都把 popover 去掉的功能

•构建并运⾏行,点击缩略图看⼀一下popover⾥里⾯面的image,点屏幕上的其他任何地⽅方,可以把popover 关闭

- (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController{ [imagePopover dismissPopoverAnimated:YES]; imagePopover = nil;}