读取照片、图片库、拍摄图像

最近的项目版本-社区用到了读取照片、拍照裁切、美化照片等功能,正好趁有时间对这一部分的内容做一个总结。

  • 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框架实现对摄像头的控制和拍摄,可以有更多对细节的控制,比如独立地直接控制调焦和曝光度,对于视频可以决定所拍摄影片的图像质量、尺寸和帧速。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,619评论 4 59
  • 看著易初還有陽陽、劉晨,好多好多曾經在第一科大交換了半年的大家,都慢慢回到本來生活的地方,有種說不出的捨不得。交換...
    1bd1cb74e0dc阅读 293评论 0 1
  • (一)简书的江湖 文人墨客心中,江湖是归隐的好去处。 就像黄老邪的桃花岛,有萧、有剑、有明月。 一处风景、一壶薄酒...
    一笑作春风阅读 2,379评论 78 133
  • 这几天很糟心,工作倒是没有什么波澜,倒是生活乱糟糟的。 人在逆境中应该怎么面对自我?还是闭口不言吧!何必把自己的不...
    Aoson_姚阅读 176评论 0 0
  • 一个月没洗澡 来了一只苍蝇 吵醒了睡觉的我 啪——苍蝇拍过去了 苍蝇死了 好几天没出门倒垃圾 屋子里有臭味 来了一...
    叶之优优阅读 246评论 2 1