为了便于操作图像iOS中定义图像类,UIImage是UIKit框架中定义的图像类,其封装了高层次图像类,可以通过多种方式创建这些对象。在Core Graphics框架(或Quartz 2D)中也定义了CGImage,它表示位图图像,因为CGImage被封装起来了,所以通常通过CGImageRef来使用CGImage。
除了UIImage和CGImage外,在Core Image框架中也有一个图像类CIImage,CIImage封装的图像类能够很好地进行图像效果处理,例如,滤镜的使用。UIImage、CGImage和CIImage之间可以互相转化,这个过程中需要注意内存释放问题,特别是CGImage与UIImage之间转化,涉及从C变量到Objective-C对象转化,如果这里使用了ARC(Automatic Reference Counting,自动引用计数)技术,反而会使内存释放问题更复杂。随着后面的学习会逐步了解这些内存释放问题。
本节介绍的创建图像主要是创建UIImage对象,CGImage一般不直接创建,因此不打算特意介绍了。CIImage对象创建在滤镜一节介绍,这一节中暂时不介绍。
下面介绍如何创建UIImage类图像对象。UIImage有一些构造方法和静态创建方法(即直接通过类名调用静态方法创建)。
对于上面介绍的方法可以根据图像来源的不同进行分类。在iOS设备中图像来源主要有以下4种不同渠道:
如果一个icon.png文件放在应用程序包中(资源文件)加载图像,可以通过下面的几种代码实现:
UIImage* image = [UIImage imageNamed:@"icon.png"];
或
NSString *path = [[NSBundle mainBundle] pathForResource:@"icon" ofType:@"png"]; UIImage* image = [UIImage imageWithContentsOfFile:path];
或
NSString *path = [[NSBundle mainBundle] pathForResource:@"icon" ofType:@"png"]; UIImage* image = [[UIImage alloc] initWithContentsOfFile:path];
或
NSString *path = [[NSBundle mainBundle] pathForResource:@"icon" ofType:@"png"]; NSData *data = [[NSData alloc] initWithContentsOfFile:path]; UIImage* image = [UIImage imageWithData:data];
或
NSString *path = [[NSBundle mainBundle] pathForResource:@"icon" ofType:@"png"]; NSData *data = [[NSData alloc] initWithContentsOfFile:path]; UIImage* image = [[UIImage alloc] initWithData:data];
如果icon.png文件放在应用程序沙箱目录中的Document目录下面,可以通过下面的几种代码实现:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString* path = [[paths lastObject] stringByAppendingPathComponent:@"icon.png"]; UIImage* image = [UIImage imageWithContentsOfFile:path];
或
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString* path = [[paths lastObject] stringByAppendingPathComponent:@"icon.png"]; UIImage* image = [[UIImage alloc] initWithContentsOfFile:path];
或
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString* path = [[paths lastObject] stringByAppendingPathComponent:@"icon.png"]; NSData *data = [[NSData alloc] initWithContentsOfFile:path]; UIImage* image = [UIImage imageWithData:data];
或
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString* path = [[paths lastObject] stringByAppendingPathComponent:@"icon.png"]; NSData *data = [[NSData alloc] initWithContentsOfFile:path]; UIImage* image = [[UIImage alloc] initWithData:data];
在上述代码中获得应用程序沙箱目录中Document目录语句如下:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString* path = [[paths lastObject] stringByAppendingPathComponent:@"icon.png"];
如果icon.png文件放在云服务器端http://xxx/icon.png下,可以通过如下的几种方式创建UIImage图像对象:
NSURL *url = [NSURL URLWithString:@"http://xxx/icon.png"]; NSData *data = [[NSData alloc] initWithContentsOfURL:url]; UIImage* image = [UIImage imageWithData:data];
或
NSURL *url = [NSURL URLWithString:@"http://xxx/icon.png"]; NSData *data = [[NSData alloc] initWithContentsOfURL:url]; UIImage* image = [[UIImage alloc] initWithData:data];
上述代码介绍了3种情况下创建图像对象,而从设备图片库或从照相机抓取后面章节介绍。
下面通过一个具体的实例介绍这3种创建图像方式。实例通过屏幕下方的3个按钮分别从3个不同的来源创建UIImage对象显示在屏幕上方,如图3-2所示。
![]() |
![]() |
![]() |
图3-2 创建图像实例
首先使用Xcode选择Single View Application工程模板,创建一个ImageSample工程,并添加资源文件到工程中,具体的UI设计过程不再赘述,重点看看代码部分。其中视图控制器类ViewController.h代码如下所示。
#import <UIKit/UIKit.h> #define FILE_NAME @"flower.png" @interface ViewController: UIViewController @property (retain, nonatomic) IBOutlet UIImageView *imageView; - (IBAction)loadBundle:(id)sender; - (IBAction)loadSandbox:(id)sender; - (IBAction)loadWebService:(id)sender; @end
视图控制器类ViewController.m文件代码如下所示。
#import "ViewController.h" @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; //复制图片到沙箱目录下 [self createEditableCopyOfDatabaseIfNeeded]; _imageView.image = [UIImage imageNamed:@"SkyDrive340.png"]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; } - (void)createEditableCopyOfDatabaseIfNeeded { NSFileManager *fileManager = [NSFileManager defaultManager]; ① NSString *writableDBPath= [self applicationDocumentsDirectoryFile]; ② BOOL dbexits = [fileManager fileExistsAtPath:writableDBPath]; ③ if (!dbexits) { NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:FILE_NAME]; ④ NSError *error; BOOL success = [fileManager copyItemAtPath:defaultDBPath toPath:writableDBPath error:&error]; ⑤ if (!success) { NSAssert1(0, @"错误写入文件:'%@'。", [error localizedDescription]); } } } - (NSString *)applicationDocumentsDirectoryFile { NSString *documentDirectory = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]; NSString *path = [documentDirectory stringByAppendingPathComponent:FILE_NAME]; return path; } - (IBAction)loadBundle:(id)sender { _imageView.image = [UIImage imageNamed:@"SkyDrive340.png"]; NSString *path = [[NSBundle mainBundle] pathForResource:@"SkyDrive340" ofType:@"png"]; UIImage* image = [[UIImage alloc] initWithContentsOfFile:path]; ⑥ _imageView.image = [UIImage imageNamed:@"SkyDrive340.png"]; [image release]; } - (IBAction)loadSandbox:(id)sender { NSString *path = [self applicationDocumentsDirectoryFile]; UIImage* image = [UIImage imageWithContentsOfFile:path]; ⑦ _imageView.image = image; } - (IBAction)loadWebService:(id)sender { NSURL *url = [NSURL URLWithString:@"http://iosbook3.com/service/download.php?email=<您的http://iosbook3.com网站注册邮箱>&FileName=2004-2OH.jpg"]; ⑧ NSData *data = [[NSData alloc] initWithContentsOfURL:url]; UIImage* image = [[UIImage alloc] initWithData:data]; ⑨ _imageView.image = image; [data release]; [image release]; } - (void)dealloc { [_imageView release]; [super dealloc]; } @end
上述代码中,viewDidLoad是视图加载方法,其中[self createEditableCopyOfDatabaseIfNeeded]语句是把资源文件中的图片复制到沙箱中Document目录下面,这是因为沙箱目录一开始没有任何图片。还有该方法中_imageView.image=[UIImage imageNamed:@"SkyDrive340.png"]语句是初始化显示SkyDrive340.png图片,这样启动应用时候就可以看到有一张图片显示在界面中,而不是空白界面。
在createEditableCopyOfDatabaseIfNeeded方法中,第①行代码[NSFileManager defaultManager]是获得NSFileManager实例,是采用单例设计模式实现的。第②行代码调用applicationDocumentsDirectoryFile方法获得沙箱目录,这个方法是自己编写的。第③行代码是沙箱目录下文件是否存在,如果文件不存在则通过第⑤行代码调用NSFileManager的copyItemAtPath:toPath:error:方法从应用程序包中复制一个图片文件到沙箱目录。第④行代码是获得应用程序包目录。
单击资源目录加载按钮触发-(IBAction)loadBundle:(id)sender方法。在该方法中第⑥行代码是通过UIImage的initWithContentsOfFile:方法从文件中创建图像对象。
单击沙箱目录加载按钮触发-(IBAction)loadSandbox:(id)sender方法。在该方法中第⑦行代码是通过imageWithContentsOfFile:方法从文件中创建图像对象。
单击云服务器加载按钮触发-(IBAction)loadWebService:(id)sender方法。其中第⑧行代码是请求服务器地址,其中<您的http://iosbook3.com网站注册邮箱>需要读者换成自己的注册邮箱,如果没有,请读者到iosbook3.com网站注册成为会员,然后把注册的邮箱替换<您的http://iosbook3.com网站注册邮箱>,这样iosbook3.com才能提供服务,客户端才能显示图片。第⑨行代码是通过initWithData:方法从服务器端取得数据来创建图像对象。
下面,可以运行以下实例,如图3-3所示。
![]() |
![]() |
![]() |
图3-3 实例运行
图像的另外一个重要来源是从设备图片库选取或从照相机抓取。UIKit中提供一个图像选择器UIImagePickerController,UIImagePickerController不仅可以实现选取图像还可以捕获视频信息。而且UIImagePickerController不仅可以从照相机中选取图像,还可以从相簿和相机胶卷中选择。相簿和相机胶卷是有区别的,相簿包含了相机胶卷,图3-4左所示是iPod touch(或iPhone)中的相簿,其中包含了相机胶卷、照片图库等内容,单击相机胶卷进入图3-4右所示是iPod touch(或iPhone)中的相机胶卷。相簿是可以查看所有图片,而相机胶卷是通过照相机拍摄或截屏获得的图片。
![]() |
![]() |
图3-4 iPod touch(或iPhone)中的照片应用
UIImagePickerController的主要属性是sourceType,sourceType属性是在枚举UIImage-PickerControllerSourceType中定义的3个常量:
UIImagePickerController委托对象需要实现UIImagePickerControllerDelegate委托协议。UIImagePickerControllerDelegate中定义了以下两个方法:
下面通过一个实例具体介绍图像选择器过程。如图3-5和图3-6所示是图像选择器实例,其中图3-5左图是实例启动的第一个界面,单击“从图片库中选取”按钮会从设备的图片库中选择图片(图3-5中),选择图片后回到开始界面,这时候选择的照片显示在屏幕上(图3-5右)。如果单击“从照相机中抓取”(图3-6左)按钮会从启动照相机预览(图3-6中),选择图片后回到开始界面,这时候选择的照片显示在屏幕上(图3-6右)。
![]() |
![]() |
![]() |
图3-5 图像选择器实例(从图片库中选取)
![]() |
![]() |
![]() |
图3-6 图像选择器实例(从照相机中抓取)
首先使用Xcode选择Single View Application工程模板,创建一个ImagePicker工程。具体的UI设计过程不再赘述,重点看看代码部分。其中视图控制器类ViewController.h代码如下所示。
#import <UIKit/UIKit.h> @interface ViewController: UIViewController <UIImagePickerControllerDelegate,UINavigationControllerDelegate> ① @property (strong, nonatomic) UIImagePickerController *imagePicker; ② @property (retain, nonatomic) IBOutlet UIImageView *imageView; - (IBAction)pickPhotoLibrary:(id)sender; - (IBAction)pickPhotoCamera:(id)sender; @end
在h文件的第①行代码声明实现UIImagePickerControllerDelegate和UINavigationControllerDelegate委托协议,其中UINavigationControllerDelegate也是UIImagePickerController的delegate属性要求实现的协议,UINavigationControllerDelegate协议定义了两个方法:
– navigationController:willShowViewController:animated: – navigationController:didShowViewController:animated:
这两个方法是在抓取界面出现前后回调的方法。第②行代码UIImagePickerController *imagePicker定义了图像控制器的一个属性。
视图控制器类ViewController.m主要代码如下所示。
- (IBAction) pickPhotoCamera:(id)sender { ① if ([UIImagePickerController isSourceTypeAvailable: UIImagePickerControllerSourceTypeCamera]) { _imagePicker = [[UIImagePickerController alloc] init]; _imagePicker.delegate = self; _imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera; [self presentViewController:_imagePicker animated:YES completion:nil]; } else { NSLog(@"照相机不可用。"); } } - (IBAction)pickPhotoLibrary:(id)sender { if (_imagePicker == nil) { _imagePicker = [[UIImagePickerController alloc] init]; } _imagePicker.delegate = self; _imagePicker.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum; [self presentViewController:_imagePicker animated:YES completion:nil]; } - (void) imagePickerControllerDidCancel: (UIImagePickerController *) picker { ② _imagePicker.delegate = nil; [self dismissViewControllerAnimated:YES completion:nil]; } - (void) imagePickerController: (UIImagePickerController *) picker didFinishPickingMediaWithInfo: (NSDictionary *) info { ③ UIImage *originalImage = (UIImage *) [info objectForKey: UIImagePickerControllerOriginalImage]; self.imageView.image = originalImage; self.imageView.contentMode = UIViewContentModeScaleAspectFill; _imagePicker.delegate = nil; [self dismissViewControllerAnimated:YES completion:nil]; }
在上述代码中第①行pickPhotoCamera:方法是从照相机抓取图片的方法,第②行和第③行代码是实现UIImagePickerControllerDelegate协议方法。
下面详细说明pickPhotoCamera:和imagePickerController:didFinishPickingMediaWithInfo:方法。takeImage:代码如下。
- (IBAction) pickPhotoCamera:(id)sender { if ([UIImagePickerController isSourceTypeAvailable: UIImagePickerControllerSourceTypeCamera]) { ① _imagePicker = [[UIImagePickerController alloc] init]; ② _imagePicker.delegate = self; ③ _imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera; ④ [self presentViewController:_imagePicker animated:YES completion:nil]; ⑤ } else { NSLog(@"照相机不可用。"); } }
其中,第①行代码是通过UIImagePickerController的类方法isSourceTypeAvailable:法判断是否设备支持照相机图像源(UIImagePickerControllerSourceTypeCamera),如果是在iOS设备上运行该方法则返回true,如果是在模拟器上运行则返回值是false。
第②行代码是实例化UIImagePickerController对象。
第③行代码_imagePicker.delegate=self是指定UIImagePickerController的委托对象为当前视图控制器,注意委托对象要求实现UIImagePickerControllerDelegate和UINavigationControllerDelegate委托协议。
第④行代码_imagePicker.sourceType=UIImagePickerControllerSourceTypeCamera是指定UIImagePickerController对象的图像源为照相机。
第⑤行代码[self presentViewController:_imagePicker animated:YES completion:nil]是呈现系统提供的图像选择器界面。
imagePickerController:didFinishPickingMediaWithInfo:方法代码如下:
- (void) imagePickerController: (UIImagePickerController *) picker didFinishPickingMediaWithInfo: (NSDictionary *) info { UIImage *originalImage = (UIImage *) [info objectForKey: UIImagePickerControllerOriginalImage]; ① self.imageView.image = originalImage; ② self.imageView.contentMode = UIViewContentModeScaleAspectFill; ③ _imagePicker.delegate = nil; [self dismissViewControllerAnimated:YES completion:nil]; ④ }
第①行代码是从参数info中取出原始图片数据,参数info如果抓取的是图片,则包含了原始或编辑后的图片数据,如果抓取的是视频,则包含的是视频存放路径。UIImagePickerControllerOriginalImage键是获取原始图片数据,此外常用的键还有
第②行代码self.imageView.image=originalImage是将从参数info中取出的图像对象保存到Image View控件上显示出来。
第③行代码self.imageView.contentMode=UIViewContentModeScaleAspectFill是设置Image View控件显示图像的方式,UIViewContentModeScaleAspectFill是缩放图像填充视图,有可能被裁减掉一些内容。
第④行代码[self dismissViewControllerAnimated:YES completion:nil]是关闭图像选择器界面。