最近的项目版本-社区用到了读取照片、拍照裁切、美化照片等功能,正好趁有时间对这一部分的内容做一个总结。
- UIImagePickerController类给我们提供了一个类似相机应用程序的界面,可以通过其实现读取相册、拍照和录制视频的功能。
- Assets Library框架可以以编程的方式访问图片库和其中的照片。需要链接到AssetsLibrary.framework并导入<AssetsLibrary/AssetsLibrary.h>。
- AV Foundation从更深层次来看,其提供了对照相机硬件的直接控制功能。需要链接到AVFoundation.framework(或许还有CoreMedia.framework)并导入<AVFoundation/AVFoundation.h>。
[TOC]
UIImagePickerController
UIImagePickerController是一个视图控制器(UINavigationController),它的视图提供了类似于Photo应用程序的导航界面,用户可以借此从图片库中选取一个照片,或者,在硬件条件允许的条件下,它可以提供一个类似于Cemera应用程序的界面,用来拍摄视频或静态图片.
实例化UIImagePickerController,须设置soureceType,如:
// 判断硬件是否支持拍照
- (BOOL)imagePickerControlerIsAvailabelToCamera {
if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
return YES;
} else {
return NO;
}
}
如果没有返回YES,就不要用这种资源类型给视图控制器赋值。
利用UIAlertController进行选择:
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"图片" message:@"选择" preferredStyle:UIAlertControllerStyleActionSheet];
UIAlertAction *photoAlbumAction = [UIAlertAction actionWithTitle:@"相册" style:UIAlertActionStyleDestructive handler:^(UIAlertAction * _Nonnull action) {
[self creatUIImagePickerControllerWithAlertActionType:1 controller:Controller];
}];
UIAlertAction *cemeraAction = [UIAlertAction actionWithTitle:@"相机" style:UIAlertActionStyleDestructive handler:^(UIAlertAction * _Nonnull action) {
[self creatUIImagePickerControllerWithAlertActionType:2 controller:Controller];
}];
UIAlertAction *cancleAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
[self creatUIImagePickerControllerWithAlertActionType:0 controller:Controller];
}];
[alertController addAction:photoAlbumAction];
[alertController addAction:cancleAction];
if ([self imagePickerControlerIsAvailabelToCamera]) {
[alertController addAction:cemeraAction];
}
[self presentViewController:alertController animated:YES completion:^{
}];
创建UIImagePickerController
enum {
UIImagePickerControllerSourceTypePhotoLibrary ,//来自图库
UIImagePickerControllerSourceTypeCamera ,//来自相机
UIImagePickerControllerSourceTypeSavedPhotosAlbum //来自相册
};
NSUInteger sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
switch (type) {
case 1:
{
// 用户可以在一组相册列表中选择,并进入其中一个相册选择图片
sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
}
break;
case 2:
{
sourceType = UIImagePickerControllerSourceTypeCamera;
}
break;
case 0:
{
return;
}
break;
default:
break;
}
UIImagePickerController *picker = [UIImagePickerController new];
picker.delegate = self;
picker.allowsEditing = YES;
picker.sourceType = sourceType;
[self presentViewController:picker animated:YES completion:^{
}];
第一次这样操作的时候,系统会弹出授权提示框,我们可以在Info.plist中对
“Privacy-Photo Library Usage Description”键(NSPhotoLibraryUsageDescription)进行设置修改其显示内容。
当用户拒绝时,UIImagePickerController依然可以显现,只不过是空的视图,显示用户拒绝访问图库,除了取消不能进行其它操作。
NSString *mediaType = AVMediaTypeVideo;
AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:mediaType];
switch (authStatus) {
case 0: { //第一次使用,则会弹出是否打开权限
[AVCaptureDevice requestAccessForMediaType : AVMediaTypeVideo completionHandler:^(BOOL granted) {
// granted 是否授权成功
}];
}
break;
case 1:{ //还未授权
}
break;
case 2:{ //主动拒绝授权
}
break;
case 3: { //已授权
}
break;
default:
break;
}
如果UIImagePickerControllerDelegate方法未被实现,视图控制器会自动退出,但我们应该主动实现两种委托方法并退出视图控制器。
设置代理<UINavigationControllerDelegate, UIImagePickerControllerDelegate>
#pragma mark - UIImagePickerControllerDelegate
-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info
{
//获取编辑后的图片
UIImage *image = info[@"UIImagePickerControllerEditedImage"];
}
// 取消选择照片:
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
[self dismissViewControllerAnimated:YES completion:nil];
}
选取的信息都在info中,info 是一个字典,字典中的键与媒体类型有关。
NSString *const UIImagePickerControllerMediaType ;指定用户选择的媒体类型(文章最后进行扩展)
NSString *const UIImagePickerControllerOriginalImage ;原始图片
NSString *const UIImagePickerControllerEditedImage ;修改后的图片
NSString *const UIImagePickerControllerCropRect ;裁剪尺寸
NSString *const UIImagePickerControllerMediaURL ;媒体的URL
NSString *const UIImagePickerControllerReferenceURL ;原件的URL
NSString *const UIImagePickerControllerMediaMetadata;当来数据来源是照相机的时候这个值才有效
使用照相机
对拍摄的图像和视频可以进行编辑,也可以对产生的委托消息进行处理,图像并不是存放于图片库中,所以传送给委托的数据字典里没有UIImagePickerControllerReferenceURL键。在图像拍摄过程中并不牵涉图库,所以无须考虑允许用户访问图片库的问题。
BOOL ok = [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera];
if (!ok) {
NSLog(@"相机硬件不支持");
return;
}
/** 检查支持的媒体类型
* @param kUTTypeImage 静态图片
* @prama kUTTypeMovie 视频 (这两个字符串常量定义在MobileCoreServices框架中)
*/
NSArray *ary = [UIImagePickerController availableMediaTypesForSourceType:UIImagePickerControllerSourceTypeCamera];
if ([ary indexOfObject:(NSString *)kUTTypeImage] == NSNotFound) {
return;
}
UIImagePickerController *picker = [UIImagePickerController new];
picker.sourceType = UIImagePickerControllerSourceTypeCamera;
// 设置图像选取控制器的类型为静态图像
picker.mediaTypes = @[(NSString *)kUTTypeImage];
picker.delegate = self;
[self presentViewController:picker animated:YES completion:nil];
-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info
{
UIImage *image = info[UIImagePickerControllerOriginalImage];
NSLog(@"%@",image);
[self dismissViewControllerAnimated:YES completion:nil];
}
-(void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
[self dismissViewControllerAnimated:YES completion:nil];
}
自定义相机拍摄界面
对图像拍摄界面的外观和行为进行自定义的关键是能够理解UIImagePickerController的本质上是一个UINavigationController。在默认情况下,界面中底部显示的控件是导航控制器的工具栏。
将默认控件移除,用双击图像来完成拍摄
BOOL ok = [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera];
if (!ok) {
NSLog(@"相机硬件不支持");
return;
}
/*** 检查支持的媒体类型
** UIImagePickerControllerMediaType 包含着KUTTypeImage 和KUTTypeMovie
** @param kUTTypeImage 静态图片
* KUTTypeImage 包含:
const CFStringRef kUTTypeImage ;抽象的图片类型
const CFStringRef kUTTypeJPEG ;
const CFStringRef kUTTypeJPEG2000 ;
const CFStringRef kUTTypeTIFF ;
const CFStringRef kUTTypePICT ;
const CFStringRef kUTTypeGIF ;
const CFStringRef kUTTypePNG ;
const CFStringRef kUTTypeQuickTimeImage ;
const CFStringRef kUTTypeAppleICNS
const CFStringRef kUTTypeBMP;
const CFStringRef kUTTypeICO;
** @prama kUTTypeMovie 视频 (这两个字符串常量定义在MobileCoreServices框架中)
* KUTTypeMovie 包含:
const CFStringRef kUTTypeAudiovisualContent ;抽象的声音视频
const CFStringRef kUTTypeMovie ;抽象的媒体格式(声音和视频)
const CFStringRef kUTTypeVideo ;只有视频没有声音
const CFStringRef kUTTypeAudio ;只有声音没有视频
const CFStringRef kUTTypeQuickTimeMovie ;
const CFStringRef kUTTypeMPEG ;
const CFStringRef kUTTypeMPEG4 ;
const CFStringRef kUTTypeMP3 ;
const CFStringRef kUTTypeMPEG4Audio ;
const CFStringRef kUTTypeAppleProtectedMPEG4Audio;
*/
NSArray *ary = [UIImagePickerController availableMediaTypesForSourceType:UIImagePickerControllerSourceTypeCamera];
if ([ary indexOfObject:(NSString *)kUTTypeImage] == NSNotFound) {
return;
}
UIImagePickerController *picker = [UIImagePickerController new];
picker.sourceType = UIImagePickerControllerSourceTypeCamera;
// 设置图像选取控制器的类型为静态图像
picker.mediaTypes = @[(NSString *)kUTTypeImage];
// 全屏效果
picker.cameraViewTransform = CGAffineTransformMakeScale(1.5, 1.5);
picker.delegate = self;
// 隐藏标准的拍摄视图控件
picker.showsCameraControls = NO;
CGRect f = self.view.window.bounds;
UIView *view = [[UIView alloc]initWithFrame:f];
UITapGestureRecognizer *tapG = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tap:)];
tapG.numberOfTapsRequired = 2;
[view addGestureRecognizer:tapG];
picker.cameraOverlayView = view;
self.picker = picker;
[self presentViewController:picker animated:YES completion:nil];
-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info
{
UIImage *image = info[UIImagePickerControllerOriginalImage];
NSLog(@"%@",image);
[self dismissViewControllerAnimated:YES completion:nil];
}
-(void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void)tap:(id)recognizer {
[self.picker takePicture];
}
在上面的demo中,出现了一块工具条大小空白区域,便界面看起来很丑,我们可以自己创建视图放置在这个空白区域。
CGFloat h = 53;
UIView *view2 = [[UIView alloc]initWithFrame:CGRectMake(0, f.size.height - h, f.size.width, h)];
view2.backgroundColor = [UIColor orangeColor];
[view addSubview:view2];
UILabel *lable = [UILabel new];
lable.text = @"双击拍摄照片";
lable.backgroundColor = [UIColor clearColor];
[lable sizeToFit];
lable.center = CGPointMake(CGRectGetMidX(view2.bounds), CGRectGetMidY(view2.bounds));
[view2 addSubview:lable];
我们创建了UIImagePickerController的委托,不仅接受了UIImagePickerControllerDelegate协议,还有UINavigationControllerDelegate协议。所以另外一种修改界面可以利用从导航控制器的界面来获得一些控件,放在根视图控制器的工具栏。
-(void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
[navigationController setToolbarHidden:NO];
CGRect f = navigationController.toolbar.frame;
CGFloat h = 56;
CGFloat diff = h - f.size.height;
f.size.height = h;
f.origin.y -= diff;
navigationController.toolbar.frame = f;
UIBarButtonItem *barBtnItem = [[UIBarButtonItem alloc]initWithTitle:@"取消" style:UIBarButtonItemStyleDone target:self action:@selector(doCancel:)];
UILabel *lable = [UILabel new];
lable.text = @"双击拍照";
lable.backgroundColor = [UIColor clearColor];
[lable sizeToFit];
UIBarButtonItem *barBtnItem2 = [[UIBarButtonItem alloc]initWithCustomView:lable];
[navigationController.topViewController setToolbarItems:@[barBtnItem,barBtnItem2]];
}
- (void)doCancel:(id)b {
NSLog(@"doCancel");
[self.picker dismissViewControllerAnimated:YES completion:nil];
}
可以通过在导航控制器上放置另一个视图控制器,自己生成接收图像的界面:
-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info
{
UIImage *image = info[UIImagePickerControllerOriginalImage];
NSLog(@"%@",image);
// [self dismissViewControllerAnimated:YES completion:nil];
if (!image) {
return;
}
ViewController2 *VC2 = [[ViewController2 alloc]initWithImage:image];
[picker pushViewController:VC2 animated:YES];
}
Assets Library
Assets Library框架突出的一个用途是实现自己的界面,从而突破UIImagePickerController的限制,让用户可以实现多选。在图片库中的一个照片或者一段视频就是一个ALAsset对象。
//获取相册中的全部照片
-(void)getAllPictures {
self.photosModelArray = [[NSMutableArray alloc]init];
self.albumandPhotosDict = [[NSMutableDictionary alloc]init];
NSMutableArray *assetURLDictionaries = [[NSMutableArray alloc] init];
ALAssetsLibrary *assetsLibrary = [[ALAssetsLibrary alloc]init];
[assetsLibrary enumerateGroupsWithTypes:ALAssetsGroupAll usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
//group 相册分组
if (group) {
NSMutableArray *tempArray = [[NSMutableArray alloc]init];
// if ([[group valueForProperty:ALAssetsGroupPropertyType] intValue] == 16) {
//16 表示系统默认相册
/*
//查看相册的名字
NSLog(@"ALAssetsGroupPropertyName:%@",[group valueForProperty:ALAssetsGroupPropertyName]);
//查看相册的类型
NSLog(@"ALAssetsGroupPropertyType:%@",[group valueForProperty:ALAssetsGroupPropertyType]);
*/
__block int groupNum = 0;
//从相册中获取照片
[group enumerateAssetsUsingBlock:^(ALAsset *asset, NSUInteger index, BOOL *stop){
if (asset) {
if([[asset valueForProperty:ALAssetPropertyType] isEqualToString:ALAssetTypePhoto]) {
[assetURLDictionaries addObject:[asset valueForProperty:ALAssetPropertyURLs]];
//NSURL *url= (NSURL*) [[asset defaultRepresentation]url];
//NSLog(@"%@,%@",[asset valueForProperty:ALAssetPropertyDate],url);
groupNum ++;
PhotoModel *photoModel = [[PhotoModel alloc]init];
photoModel.isSelected = NO;
photoModel.asset = asset;
photoModel.i = groupNum;
photoModel.image = [UIImage imageWithCGImage:[asset aspectRatioThumbnail]];
if ([[group valueForProperty:ALAssetsGroupPropertyType] intValue] == 16){
[self.photosModelArray addObject:photoModel];
}
[tempArray addObject:photoModel];
}
//获取资源图片的详细资源信息
//ALAssetRepresentation *representation = [result defaultRepresentation];
/*
//获取资源图片的长宽
CGSize dimension = [representation dimensions];
//获取资源图片的高清图
[representation fullResolutionImage];
//获取资源图片的全屏图
[representation fullScreenImage];
//获取资源图片的名字
[representation filename];
//缩放倍数
[representation scale];
//图片资源容量大小
[representation size];
//图片资源原数据
[representation metadata];
//旋转方向
[representation orientation];
//资源图片url地址,该地址和ALAsset通过ALAssetPropertyAssetURL获取的url地址是一样的
NSURL* url = [representation url];
//资源图片uti,唯一标示符
[representation UTI];
*/
}
}];
[self.collectionView reloadData];
NSString *assetsGroupPropertyNameString = [group valueForProperty:ALAssetsGroupPropertyName];
NSLog(@"ALAssetsGroupPropertyName:%@",[group valueForProperty:ALAssetsGroupPropertyName]);
[self photosDictWithKey:assetsGroupPropertyNameString ValueArray:tempArray];
}
} failureBlock:^(NSError *error) {
NSLog(@"获取照片失败");
}];
}
//字典存放相册和全部照片
- (void)photosDictWithKey:(NSString *)key ValueArray:(NSMutableArray *)array
{
[self.albumandPhotosDict setObject:array forKey:key];
}
使用AV Foundation拍摄图像
使用AV Foundation框架实现对摄像头的控制和拍摄,可以有更多对细节的控制,比如独立地直接控制调焦和曝光度,对于视频可以决定所拍摄影片的图像质量、尺寸和帧速。