iOS AVFoundation/AVCaptureSession实现自定义相机界面拍照(四)拍照、自定义裁剪框、图片查看器

    xiaoxiao2021-03-25  157

    ----参考框架:

    a122273328/Camera

    https://blog.csdn.net/aa122273328/article/details/72886022

    https://github.com/a122273328/Camera

    从系统相册选择照片后,自定义裁剪框:

    LazyDuan/LDImagePicker

    https://github.com/LazyDuan/LDImagePicker

    图片查看器:PYPhotoBrowser    https://github.com/ko1o/PYPhotoBrowser

    MWPhotoBrowser  :https://github.com/mwaterfall/MWPhotoBrowser

    -----------自定义裁剪框的步骤:-----------

    1.当我们实现了UIImagePickerController的代理后,需要在 imagePickerController:didFinishPickingMediaWithInfo:

    imagePickerController.allowsEditing = true; 如果为true,就不会展现我们自己的裁剪视图了,就无法自定义了

    2.跳转到自定义的控制器中去,吧从相册中的获取的图片传递到控制器中;

     UIImage *originImage = info[@"UIImagePickerControllerOriginalImage"];

        ImagePickerCropViewController *vc = [[ImagePickerCropViewController alloc] init];

        vc.originImage = originImage;

        [picker pushViewController:vc animated:true];

    3.在控制器中用一个Imageview显示图片,然后给imageview添加手势;实现缩放;最后保存;

    ************

    #import "ViewController.h" #import "ImagePickerCropViewController.h" @interface ViewController () <UINavigationControllerDelegate, UIImagePickerControllerDelegate> @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; } - (IBAction)choosePhotot:(id)sender { UIImagePickerController *imagePickerController = [[UIImagePickerController alloc] init]; imagePickerController.sourceType = UIImagePickerControllerSourceTypePhotoLibrary; // imagePickerController.allowsEditing = true; 如果为true,就不会展现我们自己的裁剪视图了,就无法自定义了 imagePickerController.delegate = self; [self presentViewController:imagePickerController animated:true completion:nil]; } - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info { UIImage *originImage = info[@"UIImagePickerControllerOriginalImage"]; ImagePickerCropViewController *vc = [[ImagePickerCropViewController alloc] init]; vc.originImage = originImage; [picker pushViewController:vc animated:true]; } - (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker { [picker dismissViewControllerAnimated:true completion:nil]; } @end #import <UIKit/UIKit.h> @interface ImagePickerCropViewController : UIViewController @property (nonatomic, strong) UIImage *originImage; @end

    #import "ImagePickerCropViewController.h" @interface ImagePickerCropViewController () @property (nonatomic, strong) UIImageView *cropImageView; @end @implementation ImagePickerCropViewController - (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor = [UIColor blackColor]; UIBarButtonItem *item = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemSave target:self action:@selector(save)]; self.navigationItem.rightBarButtonItem = item; CGRect frame = CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height - 44); self.cropImageView = [[UIImageView alloc] initWithFrame:frame]; _cropImageView.contentMode = UIViewContentModeScaleAspectFit; [_cropImageView setImage:self.originImage]; _cropImageView.userInteractionEnabled = true; [self.view addSubview:_cropImageView]; UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)]; [self.cropImageView addGestureRecognizer:pan]; UIPinchGestureRecognizer *pinch = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(handlePinch:)]; [self.cropImageView addGestureRecognizer:pinch]; UIView *maskView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)]; maskView.center = self.view.center; maskView.backgroundColor = [UIColor clearColor]; maskView.layer.borderWidth = 0.5; maskView.layer.borderColor = [UIColor whiteColor].CGColor; [self.view addSubview:maskView]; } - (void)handlePinch:(UIPinchGestureRecognizer *)recognizer { CGFloat scale = recognizer.scale; recognizer.view.transform = CGAffineTransformScale(recognizer.view.transform, scale, scale); //在已缩放大小基础下进行累加变化;区别于:使用 CGAffineTransformMakeScale 方法就是在原大小基础下进行变化 recognizer.scale = 1.0; } - (void)handlePan:(UIPanGestureRecognizer *)recognizer { if (recognizer.state != UIGestureRecognizerStateEnded && recognizer.state != UIGestureRecognizerStateFailed){ CGPoint translation = [recognizer translationInView:self.view]; CGPoint center = self.cropImageView.center; self.cropImageView.center = CGPointMake(center.x + translation.x, center.y + translation.y); [recognizer setTranslation:CGPointMake(0, 0) inView:self.view]; } } - (UIImage *)captureScreen { UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow]; CGRect rect = [keyWindow bounds]; UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0); CGContextRef context = UIGraphicsGetCurrentContext(); [keyWindow.layer renderInContext:context]; UIImage *img = UIGraphicsGetImageFromCurrentImageContext(); img = [UIImage imageWithCGImage:CGImageCreateWithImageInRect(img.CGImage, CGRectMake((self.view.center.x-100)*2+2, (self.view.center.y-100)*2+2, 400-4, 400-4))]; UIGraphicsEndImageContext(); return img; } - (void)save { UIImageWriteToSavedPhotosAlbum([self captureScreen], nil, nil, nil); } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; } @end

    ----------------------

    #import "ViewController.h"

    #import <AVFoundation/AVFoundation.h>

    #import <AssetsLibrary/AssetsLibrary.h>//写入相册需要的

    @interface ViewController ()<UIGestureRecognizerDelegate>

    /**

     *  AVCaptureSession对象来执行输入设备和输出设备之间的数据传递

     */

    @property (nonatomic,strong)AVCaptureSession* session;

    /**

     *  输入设备

     */

    @property (nonatomic,strong)AVCaptureDeviceInput* videoInput;

    /**

     *  照片输出流

     */

    @property (nonatomic,strong)AVCaptureStillImageOutput* stillImageOutput;//10.0以前

    //@property (nonatomic, strong) AVCapturePhotoOutput* stillImageOutput;//10.0以后

    /**

     *  预览图层

     */

    @property (nonatomic,strong)AVCaptureVideoPreviewLayer* previewLayer;

    @property (strong,nonatomicUIButton *captureBtn;//拍照按钮

    @property (strong,nonatomic)UIButton *openCaptureBtn;//打开摄像头按钮

    @property(nonatomic,assign)BOOL isUsingFrontFacingCamera;//切换镜头

    /**

     *  记录开始的缩放比例

     */

    @property(nonatomic,assign)CGFloat beginGestureScale;

    /**

     *  最后的缩放比例

     */

    @property(nonatomic,assign)CGFloat effectiveScale;

    @end

    @implementation ViewController

    -(void)takeCapture{

        [selftakePhotoButtonClick:nil];//拍照

    }

    -(void)openCapture{

    }

    -(void)flashBtn{

        [selfflashButtonClick:nil];//闪光灯

    }

    -(void)glassBtn{

        [selfswitchCameraSegmentedControlClick:nil];//切换镜头

    }

    - (void)viewDidLoad {

        [superviewDidLoad];

        [selfsetUpGesture];

        UIButton *captureBtn=[[UIButtonalloc]initWithFrame:CGRectMake(30,300,60,30)];

        self.captureBtn=captureBtn;

        captureBtn.backgroundColor=[UIColorblueColor];

        captureBtn.titleLabel.textColor=[UIColorgreenColor];

        [captureBtn setTitle:@"拍照"forState:UIControlStateNormal];

        [captureBtn addTarget:self action:@selector(takeCapture)forControlEvents:UIControlEventTouchDown];

        [self.viewaddSubview:captureBtn];

        

        UIButton *openCaptureBtn=[[UIButtonalloc]initWithFrame:CGRectMake(30,380,60,30)];

        self.openCaptureBtn=openCaptureBtn;

        openCaptureBtn.backgroundColor=[UIColorblueColor];

        [openCaptureBtn setTitle:@"摄像"forState:UIControlStateNormal];

        [openCaptureBtn addTarget:selfaction:@selector(openCapture)forControlEvents:UIControlEventTouchDown];

        [self.viewaddSubview:openCaptureBtn];

        

        UIButton *flashBtn=[[UIButtonalloc]initWithFrame:CGRectMake(30,420,60,30)];

        flashBtn.backgroundColor=[UIColorblueColor];

        [flashBtn setTitle:@"闪光灯"forState:UIControlStateNormal];

        [flashBtn addTarget:selfaction:@selector(flashBtn)forControlEvents:UIControlEventTouchDown];

        [self.viewaddSubview:flashBtn];

        UIButton *glassBtn=[[UIButtonalloc]initWithFrame:CGRectMake(30,480,60,30)];

        

        glassBtn.backgroundColor=[UIColorblueColor];

        [glassBtn setTitle:@"切换摄像头"forState:UIControlStateNormal];

        [glassBtn addTarget:selfaction:@selector(glassBtn)forControlEvents:UIControlEventTouchDown];

        [self.viewaddSubview:glassBtn];

            [selfinitAVCaptureSession];

    //    self.openCaptureBtn.hidden = NO;

    //    self.captureBtn.hidden = YES;

    }

    //闪光灯的设置非常简单,只需要修改deviceflashMode属性即可,这里需要注意的是,修改device时候需要先锁住,修改完成后再解锁,否则会崩溃,设置闪光灯的时候也需要做安全判断,验证设备是否支持闪光灯,有些iOS设备是没有闪光灯的,如果不做判断还是会crash

    - (void)flashButtonClick:(UIBarButtonItem *)sender {

        

        NSLog(@"flashButtonClick");

        //mediatype类型: AVMediaTypeVideo, AVMediaTypeAudio, or AVMediaTypeMuxed

        AVCaptureDevice *device = [AVCaptureDevicedefaultDeviceWithMediaType:AVMediaTypeVideo];

        

        //修改前必须先锁定

        [device lockForConfiguration:nil];

        //必须判定是否有闪光灯,否则如果没有闪光灯会崩溃

        if ([devicehasFlash]) {

            device.flashMode =AVCaptureFlashModeOn;

    //        if (device.flashMode == AVCaptureFlashModeOff) {

    //            device.flashMode = AVCaptureFlashModeOn;

    //            

                [sender setTitle:@"flashOn"];

    //        } else if (device.flashMode == AVCaptureFlashModeOn) {

    //            device.flashMode = AVCaptureFlashModeAuto;

                [sender setTitle:@"flashAuto"];

    //        } else if (device.flashMode == AVCaptureFlashModeAuto) {

    //            device.flashMode = AVCaptureFlashModeOff;

                [sender setTitle:@"flashOff"];

    //        }

            

        } else {

            

            NSLog(@"设备不支持闪光灯");

        }

        [device unlockForConfiguration];

    }

    #pragma 创建手势

    - (void)setUpGesture{

        

        UIPinchGestureRecognizer *pinch = [[UIPinchGestureRecognizeralloc]initWithTarget:selfaction:@selector(handlePinchGesture:)];

        pinch.delegate =self;

        [self.viewaddGestureRecognizer:pinch];

    }

    #pragma mark gestureRecognizer delegate

    - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer

    {

        if ( [gestureRecognizerisKindOfClass:[UIPinchGestureRecognizerclass]] ) {

            self.beginGestureScale =self.effectiveScale;

        }

        returnYES;

    }

    //缩放手势用于调整焦距

    - (void)handlePinchGesture:(UIPinchGestureRecognizer *)recognizer{

        

        BOOL allTouchesAreOnThePreviewLayer =YES;

        NSUInteger numTouches = [recognizernumberOfTouches], i;//几个手指

        for ( i =0; i < numTouches; ++i ) {

            CGPoint location = [recognizerlocationOfTouch:iinView:self.view];//i是指第几个手指

            CGPoint convertedLocation = [self.previewLayerconvertPoint:locationfromLayer:self.previewLayer.superlayer];//selfview上的点转换到self.previewLayer上坐标

            if ( ! [self.previewLayercontainsPoint:convertedLocation] ) {

                allTouchesAreOnThePreviewLayer = NO;//如果self.view上的转换到preview的坐标不在preview上,设置NO

                break;

            }

        }

        

        if ( allTouchesAreOnThePreviewLayer ) {

            

            

            self.effectiveScale =self.beginGestureScale * recognizer.scale;//获取当前手势的缩放比例

            if (self.effectiveScale <1.0){

                self.effectiveScale =1.0;

            }

            

            NSLog(@"%f-------------->%f------------recognizerScale%f",self.effectiveScale,self.beginGestureScale,recognizer.scale);

            

            //获取最大缩放比例

            CGFloat maxScaleAndCropFactor = [[self.stillImageOutputconnectionWithMediaType:AVMediaTypeVideo]videoMaxScaleAndCropFactor];

            

            NSLog(@"%f",maxScaleAndCropFactor);

            //超过最大,取最大

            if (self.effectiveScale > maxScaleAndCropFactor)

                self.effectiveScale = maxScaleAndCropFactor;

       

            //layer层的隐式动画

            [CATransactionbegin];

            [CATransactionsetAnimationDuration:.025];

            [self.previewLayersetAffineTransform:CGAffineTransformMakeScale(self.effectiveScale,self.effectiveScale)];//缩放

            [CATransactioncommit];//提交动画

            

        }

        

    }

    //切换镜头

    - (void)switchCameraSegmentedControlClick:(UISegmentedControl *)sender {

        

        NSLog(@"%ld",(long)sender.selectedSegmentIndex);

        

        AVCaptureDevicePosition desiredPosition;

        if (self.isUsingFrontFacingCamera){

            desiredPosition = AVCaptureDevicePositionBack;//摄像头位置在后面

        }else{

            desiredPosition = AVCaptureDevicePositionFront;//摄像头位置在前面

        }

        

        for (AVCaptureDevice *din [AVCaptureDevicedevicesWithMediaType:AVMediaTypeVideo]) {

            if ([dposition] == desiredPosition) {

                [self.previewLayer.sessionbeginConfiguration];//开启配置

                AVCaptureDeviceInput *input = [AVCaptureDeviceInputdeviceInputWithDevice:derror:nil];

                for (AVCaptureInput *oldInputinself.previewLayer.session.inputs) {

                    [[self.previewLayersession]removeInput:oldInput];//移除旧的会话

                }

                [self.previewLayer.sessionaddInput:input];//新的输入关联会话

                [self.previewLayer.sessioncommitConfiguration];//重新配置后,会话重新提交配置

                break;

            }

        }

        

        self.isUsingFrontFacingCamera = !self.isUsingFrontFacingCamera;

    }

    - (void)initAVCaptureSession{

        

        self.session = [[AVCaptureSessionalloc]init];

        

        NSError *error;

        

        AVCaptureDevice *device = [AVCaptureDevicedefaultDeviceWithMediaType:AVMediaTypeVideo];

        

        //更改这个设置的时候必须先锁定设备,修改完后再解锁,否则崩溃

        [device lockForConfiguration:nil];

        //设置闪光灯为自动

        [device setFlashMode:AVCaptureFlashModeAuto];

        [device unlockForConfiguration];

        

        self.videoInput = [[AVCaptureDeviceInputalloc]initWithDevice:deviceerror:&error];

        if (error) {

            NSLog(@"%@",error);

        }

        self.stillImageOutput = [[AVCaptureStillImageOutputalloc]init];

        //输出设置。AVVideoCodecJPEG  输出jpeg格式图片

        NSDictionary * outputSettings = [[NSDictionaryalloc]initWithObjectsAndKeys:AVVideoCodecJPEG,AVVideoCodecKey,nil];

        [self.stillImageOutputsetOutputSettings:outputSettings];

        

        if ([self.sessioncanAddInput:self.videoInput]) {

            [self.sessionaddInput:self.videoInput];

        }

        if ([self.sessioncanAddOutput:self.stillImageOutput]) {

            [self.sessionaddOutput:self.stillImageOutput];

        }

        

        //初始化预览图层

        self.previewLayer = [[AVCaptureVideoPreviewLayeralloc]initWithSession:self.session];

        [self.previewLayersetVideoGravity:AVLayerVideoGravityResizeAspect];

        self.previewLayer.frame =CGRectMake(0,0,375,375 - 64);

        self.view.layer.masksToBounds = YES;

        [self.view.layeraddSublayer:self.previewLayer];

    }

    - (void)viewWillAppear:(BOOL)animated{

        

        [superviewWillAppear:YES];

        

        if (self.session) {

            

            [self.sessionstartRunning];//开启会话

        }

    }

    - (void)viewDidDisappear:(BOOL)animated{

        

        [superviewDidDisappear:YES];

        

        if (self.session) {

            

            [self.sessionstopRunning];

        }

    }

    //拍照,输出图片

    - (void)takePhotoButtonClick:(UIBarButtonItem *)sender {

        //输出图像的时候需要用到AVCaptureConnection这个类,session通过AVCaptureConnection连接AVCaptureStillImageOutput进行图片输出,

        AVCaptureConnection *stillImageConnection = [self.stillImageOutput        connectionWithMediaType:AVMediaTypeVideo];

        UIDeviceOrientation curDeviceOrientation = [[UIDevicecurrentDevice]orientation];

        AVCaptureVideoOrientation avcaptureOrientation = [selfavOrientationForDeviceOrientation:curDeviceOrientation];

        [stillImageConnection setVideoOrientation:avcaptureOrientation];

    //这个方法是控制焦距用的暂时先固定为1,后边写手势缩放焦距的时候会修改这里

        [stillImageConnection setVideoScaleAndCropFactor:1];

        

        [self.stillImageOutputcaptureStillImageAsynchronouslyFromConnection:stillImageConnectioncompletionHandler:^(CMSampleBufferRef imageDataSampleBuffer,NSError *error) {

        //获得图片数据

            NSData *jpegData = [AVCaptureStillImageOutputjpegStillImageNSDataRepresentation:imageDataSampleBuffer];

            

            CFDictionaryRef attachments =CMCopyDictionaryOfAttachments(kCFAllocatorDefault,

                                                                        imageDataSampleBuffer,

                                                                       kCMAttachmentMode_ShouldPropagate);

    //

            

        // 写入相册之前需要判断用户是否允许了程序访问相册,否则程序会崩溃,包括在开启相机的时候和拍摄按钮点击的时候都需要做安全验证,验证设别是否支持拍照,用户是否允许程序访问相机。

    //        ALAuthorizationStatus author = [ALAssetsLibrary authorizationStatus];

    //        if (author == ALAuthorizationStatusRestricted || author == ALAuthorizationStatusDenied){

    //            //无权限

    //            return ;

    //        }

    //        ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];

    //        [library writeImageDataToSavedPhotosAlbum:jpegData metadata:(__bridge id)attachments completionBlock:^(NSURL *assetURL, NSError *error) {

    //

    //        }];

    //        

        }];

        

         }

    //设置方向,       照片写入相册之前需要进行旋转

    -(AVCaptureVideoOrientation )avOrientationForDeviceOrientation:(UIDeviceOrientation)deviceOrientation

         {

             AVCaptureVideoOrientation result = (AVCaptureVideoOrientation)deviceOrientation;

             if ( deviceOrientation ==UIDeviceOrientationLandscapeLeft )

                 result = AVCaptureVideoOrientationLandscapeRight;

             elseif ( deviceOrientation ==UIDeviceOrientationLandscapeRight )

                 result = AVCaptureVideoOrientationLandscapeLeft;

             return result;

         }

    @end

    ===================================================

    转载请注明原文地址: https://ju.6miu.com/read-2639.html

    最新回复(0)