下面在takePicture:中创建并显示UIImagePickerController对象。创建该对象时,必须为新创建的对象设置sourceType属性和delegate属性。
设置UIImagePickerController对象的源
设置sourceType属性时必须使用特定的常量,这些常量表示UIImagePicker- Controller对象获取照片的“源”。目前有以下三种可以使用的常量。
•UIImagePickerControllerSourceTypeCamera:用于用户拍摄一张新照片。
•UIImagePickerControllerSourceTypePhotoLibrary:用于显示界面,让用户选择相册,然后从选中的相册中选择一张照片。
•UIImagePickerControllerSourceTypeSavedPhotosAlbum:用于让用户从最近拍摄的照片里选择一张照片。
图11-8列出了三种常量的使用效果。
图11-8 三种常量的使用效果
对于没有相机的设备,第一种选取类型UIImagePickerControllerSourceTypeCamera是无效的。所以在使用第一种变量前,需要向UIImagePickerController类发送isSourceTypeAvailable:消息,检查设备是否支持相机。发送该消息时,需要传入待检查的选取类型常量。
+ (BOOL)isSourceTypeAvailable:(UIImagePickerControllerSourceType)sourceType;
isSourceTypeAvailable:会返回一个布尔类型的值,表示设备是否支持传入的选取类型。
下面创建一个UIImagePickerController对象,然后设置其sourceType属性。在BNRDetailViewController.m的takePicture:方法中添加以下代码:
- (IBAction)takePicture:(id)sender
{
UIImagePickerController *imagePicker =
[[UIImagePickerController alloc] init];
// 如果设备支持相机,就使用拍照模式
// 否则让用户从照片库中选择照片
if ([UIImagePickerController
isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
} else {
imagePicker.sourceType =
UIImagePickerControllerSourceTypePhotoLibrary;
}
}
设置UIImagePickerController对象的委托
除了sourceType属性,还需要为UIImagePickerController对象设置委托,也就是delegate属性。当用户从UIImagePickerController对象中选择了一张照片后,委托会收到imagePickerController:didFinishPickingMediaWithInfo:消息(如果用户取消了选择,则委托会收到imagePickerControllerDidCancel:消息)。
UIImagePickerController对象的委托通常应该设置为需要获取照片的对象,因此这里应该设置为BNRDetailViewController对象(需要在详细界面显示用户选择的照片)。在BNRDetailViewController.m的类扩展中,将BNRDetailViewController声明为遵守UINavigationControllerDelegate和UIImagePickerControllerDelegate协议(UIImagePickerController是UINavigationController的子类,所以UIImagePicker- Controller的委托也要遵守UINavigationControllerDelegate协议),代码如下:
@interface BNRDetailViewController ()
<UINavigationControllerDelegate, UIImagePickerControllerDelegate>
接下来在takePicture:中将UIImagePickerController对象的委托设置为BNRDetailViewController对象自身。
- (IBAction)takePicture:(id)sender
{
UIImagePickerController *imagePicker =
[[UIImagePickerController alloc] init];
// 如果设备支持相机,就使用拍照模式
// 否则让用户从照片库中选择照片
if ([UIImagePickerController
isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
} else {
imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
}
imagePicker.delegate = self;
}
以模态的形式显示UIImagePickerController对象
为UIImagePickerController对象设置了源类型和委托之后,就可以在屏幕中显示该对象。和之前显示的UIViewController子类对象不同,该对象必须以模态的(modal)形式显示。以模态的形式显示的视图控制器,其视图会占据整个屏幕,直到关闭。
要以模态的形式显示某个视图控制器,需要向窗口当前显示的UIViewController对象发送presentViewController:animated:completion:,并为第一个参数传入需要显示的视图控制器。同时,如果为第二个参数animated:传入YES,相应的视图控制器的视图会从屏幕底部滑入(第17章会深入介绍模态视图控制器与第三个参数)。
在BNRDetailViewController.m中的takePicture:方法结尾处添加以下代码,以模态形式显示UIImagePickerController对象。
imagePicker.delegate = self;
// 以模态的形式显示UIImagePickerController对象
[self presentViewController:imagePicker animated:YES completion:nil];
}
构建并运行应用。选中某个BNRItem对象,Homepwner会显示BNRDetailViewController对象的视图。按下UIToolbar对象上的相机按钮,Homepwner会显示UIImagePicker- Controller对象的界面(见图11-9)。读者可以拍摄一张照片(如果设备没有相机,则可以选择一张现有的照片)。
图11-9 UIImagePickerController对象的界面
(如果读者是针对模拟器构建并运行应用的,那么模拟器上的相册默认是没有图片的。解决方法是启动模拟器中的Safari浏览器,访问某个带图片的网页,按住某张图片不放直到出现动作菜单,然后选择存储图像(Save Image)。这样,Safari会将选中的图片存入模拟器的照片库。但是模拟器终究只是模拟器,如果UIImagePickerController对象无法显示之前保存的图片,那么更换图片再试几次。)
保存照片
选择了一张照片以后,UIImagePickerController对象就会自动关闭,返回BNRDetailViewController界面。但是,如何在代码中获取选择的照片呢?答案是之前介绍过的委托方法——imagePickerController:didFinishPickingMediaWithInfo:。
在BNRDetailViewController.m中实现imagePickerController:didFinishPicking- MediaWithInfo:,将选择的照片放入之前创建的UIImageView对象中,然后关闭UIImagePickerController对象,代码如下:
- (void)imagePickerController:(UIImagePickerController *)picker
didFinishPickingMediaWithInfo:(NSDictionary *)info
{
// 通过info字典获取选择的照片
UIImage *image = info[UIImagePickerControllerOriginalImage];
// 将照片放入UIImageView对象
self.imageView.image = image;
// 关闭UIImagePickerController对象
[self dismissViewControllerAnimated:YES completion:nil];
}
构建并运行应用。拍摄或选择一张照片,UIImagePickerController对象会自动关闭,返回BNRDetailViewController界面,这时界面会显示之前拍摄或选择的照片。
虽然目前Homepwner还可以正常工作,但假设之后用户添加了几百个BNRItem对象,而且每个BNRItem对象都带有一张高清照片,就必须采用新的方案存储和显示照片。为了避免一次性向内存中放入大量的照片,可以先将所有照片存储在磁盘中,只有当用户进入某个BNRItem对象的详细界面时,才能将该对象的照片从磁盘读取到内存中。同时,如果用户浏览了多个BNRItem对象,应用由于内存中加载了大量BNRItem对象的照片而收到内存过低警告,就必须清空内存中不再使用的照片,释放占用的内存空间。下面就通过创建BNRImageStore类实现上述方案。