iOS:自定义相机

图片.png
代码部分
#import "MZScanVC.h"

#import <AVFoundation/AVFoundation.h>

#define kScreenWidth UIScreen.mainScreen.bounds.size.width
#define kScreenHeight UIScreen.mainScreen.bounds.size.height

@interface MZScanView : UIView

/// 重写layerClass, 并将该view设置为控制器的view
+ (Class)layerClass;

@end

@implementation MZScanView

+ (Class)layerClass {
    return [AVCaptureVideoPreviewLayer class];
}

@end


@interface MZScanBackgroundView : UIView

@property(nonatomic, assign) CGRect scanFrame;

@end

@implementation MZScanBackgroundView

- (instancetype)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        self.backgroundColor = [UIColor clearColor];
    }
    return self;
}

- (void)drawRect:(CGRect)rect {
    [[[UIColor blackColor] colorWithAlphaComponent:0.2] setFill];
    //半透明区域
    UIRectFill(rect);
    //透明的区域
    CGRect holeiInterSection = CGRectIntersection(self.scanFrame, rect);
    [[UIColor clearColor] setFill];
    UIRectFill(holeiInterSection);
}

@end

@interface MZScanVC ()<AVCaptureVideoDataOutputSampleBufferDelegate>

@property (nonatomic, strong) AVCaptureStillImageOutput *stillImageOutput;
@property (nonatomic, strong) AVCaptureSession *captureSession;
@property (nonatomic, strong) AVCaptureVideoPreviewLayer *videoPreviewLayer;
@property (nonatomic, strong) AVCaptureDeviceInput *videoInput;

@property (nonatomic, strong) MZScanBackgroundView *backgroundView;

@end

@implementation MZScanVC

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.view.backgroundColor = [UIColor clearColor];
    [self setupSession];
    [self backgroundView];
}

- (void)setupSession {
    
    self.captureSession = [[AVCaptureSession alloc] init];
    NSError *error;
    AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
    
    //更改这个设置的时候必须先锁定设备,修改完后再解锁,否则崩溃
    [device lockForConfiguration:nil];
    
    [device unlockForConfiguration];
    
    self.videoInput = [[AVCaptureDeviceInput alloc] initWithDevice:device error:&error];
    if (error) NSLog(@"%@",error);
    
    self.stillImageOutput = [[AVCaptureStillImageOutput alloc] init];
    //输出设置AVVideoCodecJPEG  输出jpeg格式图片
    NSDictionary * outputSettings = [[NSDictionary alloc] initWithObjectsAndKeys:AVVideoCodecJPEG,AVVideoCodecKey, nil];
    [self.stillImageOutput setOutputSettings:outputSettings];
    
    if ([self.captureSession canAddInput:self.videoInput]) {
        [self.captureSession addInput:self.videoInput];
    }
    if ([self.captureSession canAddOutput:self.stillImageOutput]) {
        [self.captureSession addOutput:self.stillImageOutput];
    }
    
    //初始化预览图层
    self.videoPreviewLayer = (AVCaptureVideoPreviewLayer *)self.view.layer;
    self.videoPreviewLayer.session = self.captureSession;
    [self.videoPreviewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
    [self.videoPreviewLayer setFrame:self.view.layer.bounds];
    
    [self resetModes];
}

- (void)resetModes {
    AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
    AVCaptureExposureMode exposureMode = AVCaptureExposureModeContinuousAutoExposure;
    AVCaptureFocusMode focusMode = AVCaptureFocusModeContinuousAutoFocus;
    BOOL canResetFocus = [device isFocusPointOfInterestSupported] && [device isFocusModeSupported:focusMode];
    BOOL canResetExposure = [device isExposurePointOfInterestSupported] && [device isExposureModeSupported:exposureMode];
    CGPoint centerPoint = CGPointMake(0.5f, 0.5f);
    NSError *error;
    if ([device lockForConfiguration:&error]) {
        if (canResetFocus) {
            device.focusMode = focusMode;
            device.focusPointOfInterest = centerPoint;
        }
        if (canResetExposure) {
            device.exposureMode = exposureMode;
            device.exposurePointOfInterest = centerPoint;
        }
        [device unlockForConfiguration];
    }
}

- (void)startCapturing {
    if (!self.captureSession.isRunning) {
        [_captureSession startRunning];
    }
}

- (void)stopCapturing {
    if (self.captureSession.isRunning) {
        [_captureSession stopRunning];
    }
}

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    
    [self.navigationController setNavigationBarHidden:YES animated:animated];
    [self startCapturing];
}

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    
    [self.navigationController setNavigationBarHidden:NO animated:animated];
    [self stopCapturing];
}

/// 点击取消
- (IBAction)onClickCancel {
    [self stopCapturing];
    [self dismissViewControllerAnimated:YES completion:nil];
}

/// 点击拍照
- (IBAction)onClickTakePhoto {
    AVCaptureConnection *stillImageCon = [self.stillImageOutput connectionWithMediaType:AVMediaTypeVideo];
    __weak typeof(self) weakself = self;
    [self.stillImageOutput captureStillImageAsynchronouslyFromConnection:stillImageCon completionHandler:^(CMSampleBufferRef  _Nullable imageDataSampleBuffer, NSError * _Nullable error) {
        NSData *jpegData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
        UIImage *image = [[UIImage alloc] initWithData:jpegData];
        //返回拍照
        !weakself.getBlock ?: weakself.getBlock(image);
        [weakself onClickCancel];
    }];
}

- (MZScanBackgroundView *)backgroundView {
    if (!_backgroundView) {
        _backgroundView = [MZScanBackgroundView new];
        [self.view addSubview:_backgroundView];
        _backgroundView.frame = [UIScreen mainScreen].bounds;
        CGFloat x = 10;
        CGFloat y = 120 + (kScreenHeight >= 812 ? 44 : 20);
        _backgroundView.scanFrame = CGRectMake(x, y, kScreenWidth - 20, (kScreenWidth - 20)/1.58);
        [self.view insertSubview:_backgroundView atIndex:1];
    }
    return _backgroundView;
}

@end
xib部分
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="17506" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
    <device id="retina6_1" orientation="portrait" appearance="light"/>
    <dependencies>
        <deployment identifier="iOS"/>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17505"/>
        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    </dependencies>
    <objects>
        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="MZScanVC">
            <connections>
                <outlet property="view" destination="iN0-l3-epB" id="KHG-9D-L5m"/>
            </connections>
        </placeholder>
        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
        <view contentMode="scaleToFill" id="iN0-l3-epB" customClass="MZScanView">
            <rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
            <subviews>
                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="vTX-dQ-8Gl">
                    <rect key="frame" x="10" y="164" width="394" height="249.5"/>
                    <subviews>
                        <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="扫描框-人像面" translatesAutoresizingMaskIntoConstraints="NO" id="Ks3-m2-6Bn">
                            <rect key="frame" x="0.0" y="0.0" width="394" height="249.5"/>
                        </imageView>
                    </subviews>
                    <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                    <constraints>
                        <constraint firstAttribute="bottom" secondItem="Ks3-m2-6Bn" secondAttribute="bottom" id="klT-V5-ikd"/>
                        <constraint firstAttribute="trailing" secondItem="Ks3-m2-6Bn" secondAttribute="trailing" id="pJd-wp-G1c"/>
                        <constraint firstItem="Ks3-m2-6Bn" firstAttribute="leading" secondItem="vTX-dQ-8Gl" secondAttribute="leading" id="roA-r3-DRd"/>
                        <constraint firstItem="Ks3-m2-6Bn" firstAttribute="top" secondItem="vTX-dQ-8Gl" secondAttribute="top" id="w2u-dR-MmR"/>
                        <constraint firstAttribute="width" secondItem="vTX-dQ-8Gl" secondAttribute="height" multiplier="1.58" id="xof-bC-YQ5"/>
                    </constraints>
                </view>
                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="vzR-sj-lX0">
                    <rect key="frame" x="0.0" y="696" width="414" height="200"/>
                    <subviews>
                        <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="6up-o4-afi" userLabel="拍照">
                            <rect key="frame" x="174" y="45" width="66" height="66"/>
                            <state key="normal" image="icon-相机"/>
                            <connections>
                                <action selector="onClickTakePhoto" destination="-1" eventType="touchUpInside" id="kCg-Yx-A2g"/>
                            </connections>
                        </button>
                        <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="aQ9-jg-nlu">
                            <rect key="frame" x="357" y="61" width="37" height="34"/>
                            <state key="normal" title="取消"/>
                            <connections>
                                <action selector="onClickCancel" destination="-1" eventType="touchUpInside" id="c6d-28-eSn"/>
                            </connections>
                        </button>
                    </subviews>
                    <color key="backgroundColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                    <constraints>
                        <constraint firstItem="6up-o4-afi" firstAttribute="top" secondItem="vzR-sj-lX0" secondAttribute="top" constant="45" id="AE0-fd-JXs"/>
                        <constraint firstItem="6up-o4-afi" firstAttribute="centerX" secondItem="vzR-sj-lX0" secondAttribute="centerX" id="GdT-Wa-Fq2"/>
                        <constraint firstItem="aQ9-jg-nlu" firstAttribute="centerY" secondItem="6up-o4-afi" secondAttribute="centerY" id="Hsh-RR-RHh"/>
                        <constraint firstAttribute="height" constant="200" id="K7T-ug-cqY"/>
                        <constraint firstAttribute="trailing" secondItem="aQ9-jg-nlu" secondAttribute="trailing" constant="20" id="hHZ-tv-cA7"/>
                    </constraints>
                </view>
                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="请将身份证人像面放入扫描框内" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="wzH-hl-jRN">
                    <rect key="frame" x="100" y="428.5" width="214.5" height="18"/>
                    <fontDescription key="fontDescription" type="system" pointSize="15"/>
                    <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                    <nil key="highlightedColor"/>
                </label>
            </subviews>
            <viewLayoutGuide key="safeArea" id="vUN-kp-3ea"/>
            <constraints>
                <constraint firstItem="vTX-dQ-8Gl" firstAttribute="top" secondItem="vUN-kp-3ea" secondAttribute="top" constant="120" id="B0s-1Y-6RG"/>
                <constraint firstAttribute="bottom" secondItem="vzR-sj-lX0" secondAttribute="bottom" id="DxT-LM-dSC"/>
                <constraint firstItem="vTX-dQ-8Gl" firstAttribute="leading" secondItem="vUN-kp-3ea" secondAttribute="leading" constant="10" id="KNV-Ps-uLQ"/>
                <constraint firstItem="vUN-kp-3ea" firstAttribute="trailing" secondItem="vTX-dQ-8Gl" secondAttribute="trailing" constant="10" id="V9Y-tw-qdn"/>
                <constraint firstItem="vUN-kp-3ea" firstAttribute="trailing" secondItem="vzR-sj-lX0" secondAttribute="trailing" id="WaY-3C-trz"/>
                <constraint firstItem="vzR-sj-lX0" firstAttribute="leading" secondItem="vUN-kp-3ea" secondAttribute="leading" id="c7n-0A-l75"/>
                <constraint firstItem="wzH-hl-jRN" firstAttribute="top" secondItem="vTX-dQ-8Gl" secondAttribute="bottom" constant="15" id="hVB-v0-vZM"/>
                <constraint firstItem="wzH-hl-jRN" firstAttribute="centerX" secondItem="iN0-l3-epB" secondAttribute="centerX" id="v7b-5O-A6w"/>
            </constraints>
            <point key="canvasLocation" x="-249" y="76"/>
        </view>
    </objects>
    <resources>
        <image name="icon-相机" width="66" height="66"/>
        <image name="扫描框-人像面" width="315" height="198"/>
    </resources>
</document>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 161,326评论 4 369
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,228评论 1 304
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 110,979评论 0 252
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,489评论 0 217
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,894评论 3 294
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,900评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,075评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,803评论 0 205
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,565评论 1 249
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,778评论 2 253
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,255评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,582评论 3 261
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,254评论 3 241
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,151评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,952评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,035评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,839评论 2 277

推荐阅读更多精彩内容