现在为BNRItemCell中的UIImageView对象设置图片。要在BNRItemCell中显示BNRItem对象的图片,可以先根据BNRItem对象从BNRImageStore中获取相应的图片,然后直接赋给UIImageView对象。但是因为图片的尺寸很大,所以这样做需要读取大量的数据,还要由UIImageView负责调整图片的大小,这些额外的工作都会影响应用的性能。另一种更合理的解决方案是创建并使用图片的缩略图(thumbnail)。
iOS SDK提供了多种创建缩略图的途径,其中之一是根据原图在屏外上下文(offscreen context)中画出按比率缩小后的版本,然后从上下文取出新创建的图片。下面通过这种途径为BNRItem对象的图片创建缩略图。首先要为BNRItem添加一个UIImage属性,指向缩略图;然后要将这些缩略图保存起来,以便在应用再次启动时重新载入。
第11章通过BNRImageStore对象存取全尺寸的图片,每张图片都有自己的独立文件;相反,缩略图的文件体积很小,可以直接将其和BNRItem对象的其他属性一起固化。
打开BNRItem.h,为BNRItem对象的缩略图声明一个新属性,此外还要声明一个新方法,根据全尺寸图片设置缩略图,代码如下:
@property (nonatomic, copy) NSString *imageKey;
@property (nonatomic, strong) UIImage *thumbnail;
- (void)setThumbnailFromImage:(UIImage *)image;
@end
当用户为某个BNRItem对象拍摄或选取了图片后,BNRItem对象会获得相应的全尺寸图片。然后,BNRItem对象要为全尺寸图片生成缩略图,并赋给thumbnail属性。
完成上述过程的方法是setThumbnailFromImage:。它根据传入的全尺寸图片,在屏外上下文中创建图片的小尺寸版本,并将其赋给thumbnail。
为了能够创建屏外上下文,并通过上下文创建图片,iOS特别提供了一套函数。UIGraphicsBeginImageContext函数可以创建屏外图形上下文(offscreen image context)。需要传入该函数的实参有:图形上下文的宽和高(CGSize结构)、缩放倍数(scaling factor)和图片是否透明(布尔值)。调用该函数后,新创建的CGContext结构将成为当前图形上下文。
要在上下文中绘图,需要使用Core Graphics。具体做法和实现UIView子类的drawRect:类似。调用UIGraphicsGetImageFromCurrentImageContext函数可以从上下文得到一个UIImage对象,即绘制的图片。
通过图形上下文得到UIImage对象后,必须调用UIGraphicsEndImageContext函数,清理相应的上下文。
在BNRItem.m中实现以下方法,通过屏外上下文创建缩略图。
- (void)setThumbnailFromImage:(UIImage *)image
{
CGSize origImageSize = image.size;
// 缩略图的大小
CGRect newRect = CGRectMake(0, 0, 40, 40);
// 确定缩放倍数并保持宽高比不变
float ratio = MAX(newRect.size.width / origImageSize.width,
newRect.size.height / origImageSize.height);
// 根据当前设备的屏幕scaling factor创建透明的位图上下文
UIGraphicsBeginImageContextWithOptions(newRect.size, NO, 0.0);
// 创建表示圆角矩形的UIBezierPath对象
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:newRect
cornerRadius:5.0];
// 根据UIBezierPath对象裁剪图形上下文
[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对象,并将其赋给thumbnail属性
UIImage *smallImage = UIGraphicsGetImageFromCurrentImageContext();
self.thumbnail = smallImage;
// 清理图形上下文
UIGraphicsEndImageContext();
}
修改BNRDetailViewController.m中的imagePickerController:didFinishPicking- MediaWithInfo:,加入以下代码,在获取全尺寸图片后创建缩略图。
- (void)imagePickerController:(UIImagePickerController *)picker
didFinishPickingMediaWithInfo:(NSDictionary *)info
{
UIImage *image = info [UIImagePickerControllerOriginalImage];
[self.item setThumbnailFromImage:image];
为BNRItem添加thumbnail属性后,就可以在BNRItemsViewController中使用该属性。更新BNRItemsViewController.m中的tableView:cellForRowAtIndexPath:,代码如下:
cell.valueLabel.text =
[NSString stringWithFormat:@“$%d”, item.valueInDollars];
cell.thumbnailView.image = item.thumbnail;
return cell;
}
构建并运行应用。为某个BNRItem对象拍摄照片,当UITableView对象再次出现时,应该会显示该对象的缩略图、名称和价值(对已经存在的BNRItem对象需要重新拍摄照片)。
最后还要将缩略图固化到文件。在BNRItem.m的initWithCoder:中加入以下代码:
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self) {
_itemName = [aDecoder decodeObjectForKey:@“itemName”];
_serialNumber = [aDecoder decodeObjectForKey:@“serialNumber”];
_dateCreated = [aDecoder decodeObjectForKey:@“dateCreated”];
_itemKey = [aDecoder decodeObjectForKey:@“itemKey”];
_thumbnail = [aDecoder decodeObjectForKey:@“thumbnail”];
_valueInDollars = [aDecoder decodeIntForKey:@“valueInDollars”]
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:self.itemName forKey:@“itemName”];
[aCoder encodeObject:self.serialNumber forKey:@“serialNumber”];
[aCoder encodeObject:self.dateCreated forKey:@“dateCreated”];
[aCoder encodeObject:self.itemKey forKey:@“itemKey”];
[aCoder encodeObject:self.thumbnail forKey:@“thumbnail”];
[aCoder encodeInt:self.valueInDollars forKey:@“valueInDollars”];
}
构建并运行应用。为若干BNRItem对象拍摄照片,然后终止应用并重新启动,应该能看到之前拍摄的照片的缩略图。