15 Subclassing UITableViewCell

1,161 views

Published on

子类化 UITableViewCell

Published in: Technology
  • Be the first to comment

  • Be the first to like this

15 Subclassing UITableViewCell

  1. 1. 创建 UITableViewCell 的⼦子类范圣刚,princetoad@gmail.com, www.tfan.org
  2. 2. • UITableView 显⽰示的是⼀一个 UITableViewCell 的列 表。对于⼤大多数应⽤用⽽而⾔言,带有 textLabel, detailTextLabel, 和 imageView 的基本单元格够⽤用• 有时候我们想要能够显⽰示更详细信息,或者⼀一种 不同布局的单元格,这时候我们就要⼦子类化 UITableViewCell
  3. 3. HomepwnerItemCell• 这⼀一节我们来创建⼀一个名为 HomepwnerCell 的可 以更有⼒力的显⽰示 BNRItem 实例的 UITableViewCell 的⼦子类• 每⼀一个单元格将会显⽰示 BNRItem 的名称,以美元 记的价格,以及它的图⽚片的⼀一个缩略图
  4. 4. 创建 HomepwnerItemCell
  5. 5. UIView 和 UITableViewCell 的⼦子类化• UITableViewCell 也是 UIView 的⼀一个⼦子类。通常我 们⼦子类化 UIView (以及任何它的⼦子类)的时候, 我们会重写它的 drawRect: ⽅方法来⾃自定义 view 的 外形• 但是⼦子类化 UITableViewCell 的时候,我们并不直 接修改单元格的外观
  6. 6. ⼦子类化 UITableViewCell 的⽅方法• 每个单元格都有⼀一个名为 contentView 的⼦子视 图,它是构成⼀一个单元格⼦子类布局的视图对象的 容器• 也就是说我们可以通过改变在单元格的 contentView 中的视图对象来⼦子类化 UITableViewCell。
  7. 7. • ⽐比⽅方说,我们可以创建 UITextField,UILabel 和 UIButton 的实例并把它们加到 contentView• 甚⾄至我们可以创建⼀一个 UIView 的⼦子类,重写它的 drawRect: ,然后添加⼀一个它的实例到 contentView
  8. 8. HomepwnerItemCell hierarchy
  9. 9. 为什么要加到 contentView?• 把⼦子视图添加到 contentView ,⽽而不是直接添加 到 UITableViewCell 的⼦子类很重要。因为单元格在 ⼀一个特定的时候将会调整 contentView 的⼤大⼩小。• 例如,当 table view 进⼊入编辑模式时, contentView 会调整它的⼤大⼩小给编辑控件腾出空 间。
  10. 10. • 如果我们直接把⼦子视图加到了 UITableViewCell, 编 辑控件将会遮掩这些⼦子视图。• 单元格在进⼊入编辑模式时不能调整它的⼤大⼩小,但 是 contentView 可以并且也是这么做的。
  11. 11. HomepwnerItemCell 类• 打开 Homepwner.xcodeproj。⽣生成⼀一个新的 UITableViewCell 的⼦子类,命名为 HomepwnerItemCell
  12. 12. 配置 UITableViewCell ⼦子类的界⾯面
  13. 13. • 创建⼀一个空的 XIB ⽂文件,命名为 HomepwnerItemCell.xib (Device Family 没有关系)• 打开 HomepwnerItemCell.xib 并且拖动⼀一个 UITableViewCell 实例到绘制区域• 这个单元格需要显⽰示三个⽂文本元素和⼀一个图⽚片, 所以我们拖拽三个 UILabel 和 ⼀一个 UIImageView 到单元格上,分别显⽰示名称,价格,序列号和它 的图⽚片的缩略图(把序列号字体调稍⼩小点,颜⾊色深 灰)
  14. 14. 单元格上控件的⼤大⼩小问题• ⾸首先我们来看⼀一下单元格⼤大⼩小的问题。• 虽然单元格在 XIB ⽂文件上具有特定的⼤大⼩小,但是我们并不知道在应⽤用程序⾥里⾯面实际的宽度和⾼高度会是多少。
  15. 15. • 因为我们的应⽤用将会运⾏行在 iPhone 和 iPad 上,⽽而 且在 iPad 还有可能是 portrait 和 landscape oritentation。• 单元格需要进⾏行横向调整来匹配它所在的窗⼝口⼤大 ⼩小。这样我们就必须针对每个⼦子视图设置它的 autosizing mask• 在 size inspector ⾥里⾯面,我们来按照下⼀一⻚页的图⽰示 来更改 subview 的 autosizing mask
  16. 16. HomepwnerItemCell 的Autoresizing masks• image,name 和 serialNumber 位置都固定在左上 ⾓角;价格标签位置固定在右上⾓角;• name 和 serialNumber 宽度横向延伸
  17. 17. 更改 Class• 最后,我们点击 outline view 中的 cell,选择 identity inspector。把它的 Class 改成 HomepwnerItemCell
  18. 18. 导出 HomepwnerItemCell 的属性
  19. 19. • 现在这个单元格视觉效果看起来还不错,但是我们必须让它能够被⽤用起来。• 也就是说当我们在tableView:cellForRowAtIndexPath: ⾥里⾯面创建⼀一个HomepwnerItemCell 的实例时,我们需要能够设置每⼀一个 label 的 text 属性,以及 UIImageView 的image。
  20. 20. • 下⼀一步我们就是要来为每⼀一个⼦子视图创建和连接 HomepwnerItemCell 上的 outlets。• 我们还是使⽤用前⼏几节⽤用到的 Controll-dragging 到 源⽂文件的⽅方式来创建它们的 outlets。• 但是对于 HomepwnerItemCell 的 outlets,情况⼜又 有稍许不同:它们将会是 properites,⽽而不是简单 的实例变量。
  21. 21. • 在 DetailViewController 中,没有 UITextFields 的 outlets 被曝露为属性,因为没有其它的对象会访 问它们。• 在我们这种情况下,table view 的 data source 必 须配置每⼀一个 subview。通过把这些 subviews 导 出为 properties,数据源(ItemsViewController) 在需要的时候将可以访问。
  22. 22. • 在 HomepwnerItemCell.xib ⽂文件打开的时候,在 HomepwnerItemCell.h 上 Option-click 。• 从每⼀一个 subview Control-drag 到 HomepwnerItemCell.h 的⽅方法声明区域。• 然后给每⼀一个 outlet 命名并且配置连接的每⼀一个 属性,让它们如下图所⽰示:
  23. 23. Connections
  24. 24. • 操作结束后的 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
  25. 25. 使⽤用 HomepwnerItemCell
  26. 26. • 在 ItemsViewController 的 tableview:cellForRoAtIndexPath: ⽅方法中,我们将 为表格中的每⼀一⾏行创建⼀一个 HomepwnerItemCell 的实例。 • 在 ItemsViewController.h 的顶部,导⼊入 HomepwnerItemCell 的头⽂文件#import "HomepwnerItemCell.h"
  27. 27. • 在之前的 tableview:cellForRowAtIndexPath: 的实现 中,我们⾸首先会询问 table view 它是否有可重⽤用 的 cell,然后如果没有的话我们再⽣生成⼀一个全新的• 当使⽤用⼀一个 XIB ⽂文件来加载 UITableViewCell 的⼦子 类时,这个过程稍有不同:我们在 table 第⼀一次加 载时使⽤用⼀一个给定的 reuse identifier 向 UITableView 来注册这个 XIB ⽂文件。
  28. 28. // 重写 viewDidLoad 为 HomepwnerItemCell 复⽤用标识注册 HomepwnerItemCell.xib- (void)viewDidLoad{ [super viewDidLoad]; // 加载 xib ⽂文件 UINib *nib = [UINib nibWithNibName:@"HomepwnerItemCell" bundle:nil]; // 注册这个包含 cell 的 NIB [[self tableView] registerNib:nibforCellReuseIdentifier:@"HomepwnerItemCell"];} • 在 ItemsViewController.m 中 重写 viewDidLoad 来 为 HomepwnerItemCell reuse identifier 注册 HomepwnerItemCell.xib
  29. 29. • 要得到⼀一个 HomepwnerItemCell 的实例,我们向 table view 请求 dequeue ⼀一个符合 HomepwnerItemCell reuse identifier 的 Cell。• 如果 table 有⼀一个可复⽤用的 HomepwnerItemCell 的 实例,就会返回它;如果没有的话,就会加载 HomepwnerItemCell.xib ,并且给你⼀一个 archived cell 的实例。• 在 ItemsViewController.m 中,定位 tableView:cellForRowAtIndexPath: 并进⾏行如下修 改:
  30. 30. - (UITableViewCell *)tableView:(UITableView *)tableViewcellForRowAtIndexPath:(NSIndexPath *)indexPath{// // ⾸首先检查是否有可以重⽤用的单元格,如果存在的化就使⽤用// UITableViewCell *cell = [tableViewdequeueReusableCellWithIdentifier:@"UITableViewCell"];// if (!cell) {// cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefaultreuseIdentifier:@"UITableViewCell"];// }// else {// NSLog(@"重⽤用: %d", [indexPath row]);// } BNRItem *p = [[[BNRItemStore defaultStore] allItems] objectAtIndex:[indexPath row]];// [[cell textLabel] setText:[p description]]; // 把原来相关的复⽤用代码删掉 // 获得⼀一个新的或者循环使⽤用的 cell HomepwnerItemCell *cell = [tableViewdequeueReusableCellWithIdentifier:@"HomepwnerItemCell"]; // 使⽤用上⾯面的 BNRItem 配置这个单元格 [[cell nameLabel] setText:[p itemName]]; [[cell serialNumberLabel] setText:[p serialNumber]]; [[cell valueLabel] setText:[NSString stringWithFormat:@"$%d", [pvalueInDollars]]]; return cell;}
  31. 31. • 构建并运⾏行,单元格将会如下图所⽰示:• 要注意的两个问题:第⼀一,我们前⾯面使⽤用 UINib 来 进⾏行注册,XIB 的初始化底层调⽤用的也是 Nib;第 ⼆二,我们前⾯面的 XIB ⽂文件没有设置 File’s Owner, 因 为 table view 注册的时候只会扫描 UITableViewCell。所以 XIB ⽂文件⾥里⾯面也不能放置多 个 UITableViewCell。
  32. 32. 图像操作
  33. 33. • 要在单元格⾥里⾯面使⽤用图像,我们可以使⽤用 image的缩略图。• 要创建⼀一个 BNRItem 图像的缩略图,我们需要绘制⼀一个全尺⼨寸图⽚片 scale-down 的版本到 offscreencontext, 然后在 BNRItem 实例中保持⼀一个指向这个新图⽚片的指针。
  34. 34. • 同样我们也需要⼀一个位置来存储这个缩略图,以 便在系统重启后可以重新加载。• 全尺⼨寸的图⽚片我们保存在 BNRImageStore 中,必 要的时候我们可以把它 flush 掉;⽽而缩略图⾜足够 ⼩小,所以我们可以直接把它和 BNRItem 的其他实 例变量⼀一起 archive 起来。
  35. 35. • 问题是缩略图是 UIImage 的⼀一个实例,⽽而 UIImage 并不符合 NSCoding 的 protocol,所以我们⽆无法在 NSCode 中直接对缩略图进⾏行编码。• 我们可以把缩略图作为数据(PNG 格式)进⾏行编 码,并且把它封装在⼀一个 NSData 对象中,NSData 是符合 NSCoding 的。
  36. 36. • 打开 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;
  37. 37. • 当我们为 BNRItem 选择⼀一个图⽚片的时候,我们就 把这个图⽚片赋给了 BNRItem。同时我们把它转成 ⼀一个缩略图,保存成它的 thumbnail。• 同时还会⽣生成 PNG 格式的 NSData,并且把它保存 成 thumbnailData。• thumbnailData 将会随着 BNRItem ⼀一起被 archive,⼀一起被从 archive 中加载,并且从它的数 据来重新创建 thumbnail。
  38. 38. • 在 BNRItem.m 中,为 thumbnail ⽣生成⼀一个 getter ⽅方法,必要的时候从数据⽣生成它。// ⾃自定义 getter ⽅方法- (UIImage *)thumbnail{ if (!thumbnailData) { return nil; } // 如果还没有从数据中⽣生成缩略图,创建它 if (!thumbnail) { // 从数据⽣生成图⽚片 thumbnail = [UIImage imageWithData:thumbnailData]; } return thumbnail;}
  39. 39. • 现在我们来实现 setThumbnailDataFromImage: ⽅方 法• 这个⽅方法将把⼀一个全尺⼨寸图⽚片⽣生成为缩略图,然 后把 thumbnail 指向这个图⽚片
  40. 40. - (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:newRectcornerRadius: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();}
  41. 41. • 在 DetailViewController.m 中,修改 imagePickerController:didFinishPickingMediaWithIn fo:,在采集到原始图⽚片时⽣生成缩略图- (void)imagePickerController:(UIImagePickerController *)pickerdidFinishPickingMediaWithInfo:(NSDictionary *)info{ // 如果存在就图⽚片,先从 BNRImageStore 中删除 NSString *oldKey = [item imageKey]; if (oldKey) { [[BNRImageStore sharedStore] deleteImageForkey:oldKey]; } // 从 info 字典中获得拾取的图⽚片 UIImage *image = [infoobjectForKey:UIImagePickerControllerOriginalImage]; // 拾取图⽚片以后⽣生成缩略图 [item setThumbnailDataFromImage:image];
  42. 42. • 现在我们已经有了⼀一个可以⽤用在 ItemsViewController 的 table view 的缩略图。 • 在 ItemsViewController.m 中,更新 tableView:cellForRowAtIndexPath: // 使⽤用上⾯面的 BNRItem 配置这个单元格 [[cell nameLabel] setText:[p itemName]]; [[cell serialNumberLabel] setText:[p serialNumber]]; [[cell valueLabel] setText:[NSString stringWithFormat:@"$%d", [pvalueInDollars]]]; // 设置缩略图 [[cell thumbnailView] setImage:[p thumbnail]]; return cell;}构建并运⾏行,测试⼀一下缩略图的⽣生成和显⽰示
  43. 43. • 最后我们来把缩略图的数据添加到 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:[aDecoderdecodeObjectForKey:@"serialNumber"]]; [self setImageKey:[aDecoder decodeObjectForKey:@"imageKey"]]; [self setValueInDollars:[aDecoderdecodeIntForKey:@"valueInDollars"]]; dateCreated = [aDecoder decodeObjectForKey:@"dateCreated"]; thumbnailData = [aDecoder decodeObjectForKey:@"thumbnailData"]; } return self;}
  44. 44. 从 UITableViewCells 转发 action
  45. 45. • 我们希望⽤用户点击单元格中的⼀一个缩略图以后能够看到⼀一个全尺⼨寸的图⽚片。• 可以在 thumbnail 上⾯面加⼀一个透明的按钮• 点击这个按钮以后,如果应⽤用运⾏行的 iPad 之上,将会使⽤用 UIPopoverController 显⽰示⼀一个全尺⼨寸的图⽚片。
  46. 46. • 打开 HomepwnerItemCell.xib ,在 UIImageView 上 ⾯面拖拽⼀一个 UIButton,重新调整这个按钮的⼤大⼩小 让它正好和 UIIImageView 的⼤大⼩小完全⼀一致。 • 然后在 HomepwnerItemCell.h 上 Option-click 打开 assistant editor。从按钮 Control-drag 到⽅方法区 域,然后像下⾯面这样配置⼀一下。- (IBAction)showImage:(id)sender;
  47. 47. • 为了不让这个按钮掩盖下⾯面的图⽚片,在 attributesinspector ⾥里⾯面,把按钮的类型改成 Custom。• 默认情况下,⼀一个 custom 的按钮不做任何绘制,也就是说它是⼀一个透明的,上⾯面什么也没有的按钮。
  48. 48. • 现在的问题是 HomepwnerItemCell 不是⼀一个 controller,⽽而且也⽆无法访问任何拿到全尺⼨寸图⽚片 需要的数据。• 解决⽅方法是给 HomepwnerItemCell ⼀一个到 ItemsViewController 的指针。• 这样在 HomepwnerItemCell 收到从按钮来的 action message时,它会发⼀一个新的消息到 ItemsViewController,这样 controller 就可以获取 图⽚片并且把它在 UIPopoverController 中显⽰示。
  49. 49. 增加指针到单元格⼦子类
  50. 50. • ⾸首先给 HomepwnerItemCell 指向 controller 的指 针,还有⼀一个指向 cell 所在的 table view 的指针。 • 在 HomepwnerItemCell.m 中,synthesizie 这些属 性@property (weak, nonatomic) id controller;@property (weak, nonatomic) UITableView *tableView;@implementation HomepwnerItemCell@synthesize controller;@synthesize tableView;
  51. 51. • 现在我们需要当 cell 被创建时来设置这些属性。 • 在 ItemsViewController.m 中,找到 tableView:cellForRowAtIndexPath: ⽅方法,添加下⾯面 代码: HomepwnerItemCell *cell = [tableViewdequeueReusableCellWithIdentifier:@"HomepwnerItemCell"]; // 设置 HomepwnerItemCell 中传递消息⽤用的属性 [cell setController:self]; [cell setTableView:tableView]; // 使⽤用上⾯面的 BNRItem 配置这个单元格 [[cell nameLabel] setText:[p itemName]];
  52. 52. 转发消息到控制器• 现在单元格已经知道它的控制器和它要在上⾯面显 ⽰示的 table view 了• 当 showImage: 消息被发送到 HomepwnerItemCell 时,我们希望 HomepwnerItemCell 可以告诉 ItemsViewController 为在 index path 位置的单元 格显⽰示图⽚片
  53. 53. • 在 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:senderwithObject:indexPath]; } }}
  54. 54. • 我们先来验证⼀一下是不是⼀一切⼯工作正常,在 ItemViewController.m 中实现 showImage:atIndexPath: 来打印出⺫⽬目前的 index path • 然后构建并运⾏行,点击⼀一个缩略图,看⼀一下控制 台消息// 实现 showImage:atIndexPath:- (void)showImage:(id)sender atIndexPath:(NSIndexPath *)ip{ NSLog(@"即将为 %@ 显⽰示图⽚片", ip);}
  55. 55. 在popover controller中显⽰示图像
  56. 56. • 现在 ItemsViewController 需要修改 showImage:atIndexPath: 来提取 BNRItem 和按钮被 按下的单元格相关联的图⽚片• 然后在 UIPopoverController 中显⽰示它的图⽚片
  57. 57. • 要在 popover 中显⽰示⼀一个图⽚片,我们需要⼀一个 UIViewController 作为 popover 的 content view controller,这个 UIViewController 要能够显⽰示图⽚片• 我们新建⼀一个 UIViewController 的⼦子类,命名为: ImageViewController, 选择 UIViewController 作为 它的⽗父类,然后勾选“With XIB for user interface”
  58. 58. • 打开新建的这个 ImageViewController.xib, ⾸首先拖 拽⼀一个 UIScrollView 到 View 上;然后拖拽⼀一个 UIImageView 到 UIScrollView 上
  59. 59. ImageViewController XIB
  60. 60. • 在配置像具有这种堆叠在⼀一起的视图的 XIB ⽂文件 时,很难对视图进⾏行选择,因为都是完全重叠 的。• 这时候我们可以从 outline view ⾥里⾯面的 objects 开 始拖拽,⽽而不是从 canvas 区域的可视化部分• 在 ImageViewController.h 中,给 interface 添加⼤大 括号,然后建⽴立连接。这些连接应该是 weak ⽅方式 的实例变量(imageView, scrollView)
  61. 61. • 然后在 ImageViewController.h 中增加⼀一个属性来 持有这个 image@interface ImageViewController : UIViewController{ __weak IBOutlet UIScrollView *scrollView; __weak IBOutlet UIImageView *imageView;}@property (nonatomic, strong) UIImage *image;@end
  62. 62. • 当⼀一个 ImageViewController 的实例被创建时,它 会被赋予⼀一个 image。• 显⽰示的时候,它将会调整 imageView 来适应图 ⽚片,然后告诉 scrollView 来更新它的 content size 来适配。
  63. 63. • ⾸首先在 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]];}
  64. 64. • popover 完成以后,我们就可以继续完成我们的 showImage:atIndexPath: 了 • ⾸首先在 ItemsViewController.h 中声明它符合 UIPopoverControllerDelegate, 然后给它⼀一个实例 变量来持有这个 popover@interface ItemsViewController : UITableViewController <UIPopoverControllerDelegate>{ UIPopoverController *imagePopover;}
  65. 65. • 然后在 ItemsViewController.m 顶部导⼊入需要的头 ⽂文件// 为 image popover 导⼊入相应头⽂文件#import "BNRImageStore.h"#import "ImageViewController.h"
  66. 66. • 然后完成 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]; }}
  67. 67. • 最后,在 ItemsViewController.m 中,实现⽤用户点 击屏幕上任何位置都把 popover 去掉的功能 • 构建并运⾏行,点击缩略图看⼀一下popover⾥里⾯面的 image,点屏幕上的其他任何地⽅方,可以把 popover 关闭- (void)popoverControllerDidDismissPopover:(UIPopoverController*)popoverController{ [imagePopover dismissPopoverAnimated:YES]; imagePopover = nil;}

×