天天看点

iOS10自定义相机(swift)

直接上代码
//
//  TGCameraVC.swift
//  TGPhotoPicker
//
//  Created by targetcloud on 2017/7/25.
//  Copyright © 2017年 targetcloud. All rights reserved.
//

import UIKit
import AVFoundation
import Photos

@available(iOS 10.0, *)
class TGCameraVC: UIViewController {

    var callbackPicutureData: ((Data?) -> ())?
    
    private var device: AVCaptureDevice?
    private var input: AVCaptureDeviceInput?
    private var imageOutput: AVCapturePhotoOutput?
    private var session: AVCaptureSession?
    private var previewLayer: AVCaptureVideoPreviewLayer?
    fileprivate var showImageContainerView: UIView?
    fileprivate var showImageView: UIImageView?
    fileprivate var picData: Data?
    private var flashMode: AVCaptureFlashMode = .auto
    private weak var flashButton: UIButton?
    
    override func viewDidLoad() {
        super.viewDidLoad()

        setupCamera()
        setupUI()
        
        if #available(iOS 9.0, *) {
            let isVCBased = Bundle.main.infoDictionary?["UIViewControllerBasedStatusBarAppearance"] as? Bool ?? false
            if !isVCBased{
                UIApplication.shared.setStatusBarHidden(false, with: .none)
            }
        }else {
            UIApplication.shared.statusBarStyle = .lightContent
            UIApplication.shared.setStatusBarHidden(false, with: .none)
        }
    }
    
    override var prefersStatusBarHidden: Bool{
        return false
    }
    
    override var preferredStatusBarStyle: UIStatusBarStyle {
        return .lightContent
    }

    private func setupCamera() {
        AVCaptureDevice.requestAccess(forMediaType: AVMediaTypeVideo) { success in
            if !success {
                let alertVC = UIAlertController(title: TGPhotoPickerConfig.shared.cameraUsage, message: TGPhotoPickerConfig.shared.cameraUsageTip, preferredStyle: .actionSheet)
                alertVC.addAction(UIAlertAction(title: TGPhotoPickerConfig.shared.confirmTitle, style: .default, handler: nil))
                self.present(alertVC, animated: true, completion: nil)
            }
        }
        device = cameraWithPosistion(.back)
        input = try? AVCaptureDeviceInput(device: device)
        guard input != nil else {
            return
        }
        
        imageOutput = AVCapturePhotoOutput()
        session = AVCaptureSession()
        session?.beginConfiguration()
        session?.sessionPreset = TGPhotoPickerConfig.shared.sessionPreset
        if session!.canAddInput(input) {
            session!.addInput(input)
        }
        if session!.canAddOutput(imageOutput) {
            session!.addOutput(imageOutput)
        }
        previewLayer = AVCaptureVideoPreviewLayer(session: session)
        previewLayer?.frame = view.bounds
        previewLayer?.videoGravity = TGPhotoPickerConfig.shared.videoGravity
        view.layer.addSublayer(previewLayer!)
        session?.commitConfiguration()
        session?.startRunning()
    }
    
    private func cameraWithPosistion(_ position: AVCaptureDevicePosition) -> AVCaptureDevice {
        let type = AVCaptureDeviceType(rawValue: TGPhotoPickerConfig.shared.captureDeviceType.rawValue)
        return AVCaptureDevice.defaultDevice(withDeviceType: type, mediaType: AVMediaTypeVideo, position: position)
    }
    
    private func setupUI() {
        let takeButton = UIButton(frame: CGRect(x: 0, y: 0, width: TGPhotoPickerConfig.shared.takeWH, height: TGPhotoPickerConfig.shared.takeWH))
        takeButton.center = CGPoint(x: UIScreen.main.bounds.width / 2, y: UIScreen.main.bounds.height - TGPhotoPickerConfig.shared.buttonEdge.bottom)
        takeButton.setImage(UIImage.size(width: TGPhotoPickerConfig.shared.takeWH, height: TGPhotoPickerConfig.shared.takeWH).border(width: 3).border(color: .white).color(.clear).corner(radius: TGPhotoPickerConfig.shared.takeWH / 2).image +
            UIImage.size(width: TGPhotoPickerConfig.shared.takeWH - 10, height: TGPhotoPickerConfig.shared.takeWH - 10).color(UIColor(white: 0.95, alpha: 1) ).corner(radius: (TGPhotoPickerConfig.shared.takeWH - 10) / 2).image, for: .normal)
        takeButton.setImage(UIImage.size(width: TGPhotoPickerConfig.shared.takeWH, height: TGPhotoPickerConfig.shared.takeWH).border(width: 3).border(color: .white).color(.clear).corner(radius: TGPhotoPickerConfig.shared.takeWH / 2).image +
            UIImage.size(width: TGPhotoPickerConfig.shared.takeWH - 10, height: TGPhotoPickerConfig.shared.takeWH - 10).color(UIColor(white: 0.8, alpha: 1) ).corner(radius: (TGPhotoPickerConfig.shared.takeWH - 10) / 2).image, for: .highlighted)
        takeButton.addTarget(self, action: #selector(takePhotoAction), for: .touchUpInside)
        view.addSubview(takeButton)
        
        let cameraChangeButton = UIButton(frame: CGRect(x: 0, y: 0, width: TGPhotoPickerConfig.shared.takeWH * 0.6, height: TGPhotoPickerConfig.shared.takeWH * 0.6))
        cameraChangeButton.setImage(TGPhotoPickerConfig.getImageNo2x3xSuffix("camera"), for: .normal)
        cameraChangeButton.center = CGPoint(x: UIScreen.main.bounds.width - TGPhotoPickerConfig.shared.buttonEdge.right, y: takeButton.center.y)
        cameraChangeButton.addTarget(self, action: #selector(changeCameraPositionAction), for: .touchUpInside)
        cameraChangeButton.contentMode = .scaleAspectFit
        view.addSubview(cameraChangeButton)
        
        let flashChangeButton = UIButton(frame: CGRect(x: 0, y: 0, width: TGPhotoPickerConfig.shared.takeWH * 0.5, height: TGPhotoPickerConfig.shared.takeWH * 0.5))
        flashChangeButton.center = CGPoint(x: cameraChangeButton.center.x, y: TGPhotoPickerConfig.shared.buttonEdge.top)
        flashChangeButton.setImage(TGPhotoPickerConfig.getImageNo2x3xSuffix("flashauto"), for: .normal)
        flashChangeButton.addTarget(self, action: #selector(flashChangeAction), for: .touchUpInside)
        flashChangeButton.contentMode = .scaleAspectFit
        flashButton = flashChangeButton
        view.addSubview(flashChangeButton)
        
        let backButton = UIButton(frame: CGRect(x: 0, y: 0, width: TGPhotoPickerConfig.shared.takeWH * 0.4, height: TGPhotoPickerConfig.shared.takeWH * 0.4))
        backButton.center = CGPoint(x: TGPhotoPickerConfig.shared.buttonEdge.left , y: flashChangeButton.center.y)
        backButton.setImage(UIImage.size(width: TGPhotoPickerConfig.shared.takeWH * 0.4, height: TGPhotoPickerConfig.shared.takeWH * 0.4)
            .corner(radius: TGPhotoPickerConfig.shared.takeWH * 0.2)
            .color(.clear)
            .border(color: UIColor.white.withAlphaComponent(0.7))
            .border(width: TGPhotoPickerConfig.shared.isShowBorder ? TGPhotoPickerConfig.shared.checkboxLineW : 0)
            .image
            .with({ context in
                context.setLineCap(.round)
                UIColor.white.setStroke()
                context.setLineWidth(TGPhotoPickerConfig.shared.checkboxLineW)
                let WH = TGPhotoPickerConfig.shared.takeWH * 0.4
                context.move(to: CGPoint(x: WH * 0.6, y: WH * 0.2))
                context.addLine(to: CGPoint(x: WH * 0.35, y: WH * 0.5))
                context.move(to: CGPoint(x: WH * 0.35, y: WH * 0.5))
                context.addLine(to: CGPoint(x: WH * 0.6, y: WH * 0.8))
                context.strokePath()
            }), for: .normal)
        backButton.contentMode = .scaleAspectFit
        backButton.addTarget(self, action: #selector(backAction), for: .touchUpInside)
        view.addSubview(backButton)
        
        showImageContainerView = UIView(frame: view.bounds)
        showImageContainerView?.backgroundColor = TGPhotoPickerConfig.shared.previewBGColor
        view.addSubview(showImageContainerView!)
        
        let height = showImageContainerView!.bounds.height - TGPhotoPickerConfig.shared.takeWH - TGPhotoPickerConfig.shared.buttonEdge.bottom - TGPhotoPickerConfig.shared.previewPadding * 2
        showImageView = UIImageView(frame: CGRect(x: TGPhotoPickerConfig.shared.previewPadding, y: TGPhotoPickerConfig.shared.previewPadding * 2, width: showImageContainerView!.bounds.width - 2 * TGPhotoPickerConfig.shared.previewPadding, height: height))
        showImageView?.contentMode = .scaleAspectFit
        showImageContainerView?.addSubview(showImageView!)
        showImageContainerView?.isHidden = true
        
        let giveupButton = createImageOperatorButton(nil, CGPoint(x: TGPhotoPickerConfig.shared.takeWH * 1.5, y: showImageContainerView!.bounds.height - TGPhotoPickerConfig.shared.takeWH * 1.5), TGPhotoPickerConfig.shared.getCheckboxImage(true, true, .circle, TGPhotoPickerConfig.shared.takeWH * 0.7).unselect)
        giveupButton.addTarget(self, action: #selector(giveupImageAction), for: .touchUpInside)
        showImageContainerView?.addSubview(giveupButton)
        
        let ensureButton = createImageOperatorButton(nil, CGPoint(x: showImageContainerView!.bounds.width - TGPhotoPickerConfig.shared.takeWH * 1.5, y: showImageContainerView!.bounds.height - TGPhotoPickerConfig.shared.takeWH * 1.5), TGPhotoPickerConfig.shared.getCheckboxImage(true, false, .circle, TGPhotoPickerConfig.shared.takeWH * 0.7).select)
        ensureButton.addTarget(self, action: #selector(useImageAction), for: .touchUpInside)
        showImageContainerView?.addSubview(ensureButton)
    }
    
    private func createImageOperatorButton(_ title: String?, _ center: CGPoint, _ img: UIImage?) -> UIButton {
        let btn = UIButton(frame: CGRect(x: 0, y: 0, width: TGPhotoPickerConfig.shared.takeWH * 0.7, height: TGPhotoPickerConfig.shared.takeWH * 0.7))
        btn.center = center
        btn.setTitle(title, for: .normal)
        btn.setImage(img, for: .normal)
        btn.contentMode = .scaleAspectFit
        return btn
    }
    
    @objc private func flashChangeAction() {
        switch flashMode {
        case .auto:
            flashMode = .on
            flashButton?.setImage(TGPhotoPickerConfig.getImageNo2x3xSuffix("flash"), for: .normal)
        case .on:
            flashMode = .off
            flashButton?.setImage(TGPhotoPickerConfig.getImageNo2x3xSuffix("flashno"), for: .normal)
        case .off:
            flashMode = .auto
            flashButton?.setImage(TGPhotoPickerConfig.getImageNo2x3xSuffix("flashauto"), for: .normal)
        }
    }
    
    @objc private func backAction() {
        dismiss(animated: true, completion: nil)
    }
    
    @objc private func takePhotoAction() {
        let connection = imageOutput?.connection(withMediaType: AVMediaTypeVideo)
        guard connection != nil else {
            return
        }
        let photoSettings = AVCapturePhotoSettings()
        photoSettings.flashMode = flashMode
        imageOutput?.capturePhoto(with: photoSettings, delegate: self)
    }
    
    @objc private func changeCameraPositionAction() {
        let animation = CATransition()
        animation.duration = 0.5
        animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
        animation.type = TGPhotoPickerConfig.shared.transitionType
        
        let newDevice: AVCaptureDevice!
        let newInput: AVCaptureDeviceInput?
        let position = input?.device.position
        if position == .front {
            newDevice = cameraWithPosistion(.back)
            animation.subtype = kCATransitionFromLeft
        } else {
            newDevice = cameraWithPosistion(.front)
            animation.subtype = kCATransitionFromRight
        }
        newInput = try? AVCaptureDeviceInput(device: newDevice)
        guard newInput != nil else{
            return
        }
        
        previewLayer?.add(animation, forKey: nil)
        
        session?.beginConfiguration()
        session?.removeInput(input)
        if session!.canAddInput(newInput) {
            session?.addInput(newInput!)
            input = newInput
        } else {
            session?.addInput(input)
        }
        session?.commitConfiguration()
    }
    
    @objc private func giveupImageAction() {
        showImageView?.image = UIImage()
        showImageContainerView?.isHidden = true
    }
    
    @objc private func useImageAction() {
        callbackPicutureData?(picData)
        dismiss(animated: true, completion: nil)
    }
}

@available(iOS 10.0, *)
extension TGCameraVC: AVCapturePhotoCaptureDelegate {
    func capture(_ captureOutput: AVCapturePhotoOutput, didFinishProcessingPhotoSampleBuffer photoSampleBuffer: CMSampleBuffer?, previewPhotoSampleBuffer: CMSampleBuffer?, resolvedSettings: AVCaptureResolvedPhotoSettings, bracketSettings: AVCaptureBracketedStillImageSettings?, error: Error?) {
        if error != nil {
            print("error = \(String(describing: error?.localizedDescription))")
        } else {
            if let imageData = AVCapturePhotoOutput.jpegPhotoDataRepresentation(forJPEGSampleBuffer: photoSampleBuffer!, previewPhotoSampleBuffer: previewPhotoSampleBuffer){
                picData = imageData
                showImageContainerView?.isHidden = false
                showImageView?.image = UIImage(data: imageData)
                if TGPhotoPickerConfig.shared.saveImageToPhotoAlbum{
                    self.saveImageToPhotoAlbum(UIImage(data: imageData)!)
                }
            }
        }
    }
    
    fileprivate func saveImageToPhotoAlbum(_ savedImage:UIImage){
        UIImageWriteToSavedPhotosAlbum(savedImage, self, #selector(imageDidFinishSavingWithErrorContextInfo), nil)
    }
    
    @objc fileprivate func imageDidFinishSavingWithErrorContextInfo(image:UIImage,error:NSError?,contextInfo:UnsafeMutableRawPointer?){
        if canUseAlbum(){
            let msg = (error != nil) ? TGPhotoPickerConfig.shared.saveImageFailTip : TGPhotoPickerConfig.shared.saveImageSuccessTip
            let alert =  UIAlertView(title: TGPhotoPickerConfig.shared.saveImageTip, message: msg, delegate: self, cancelButtonTitle: TGPhotoPickerConfig.shared.confirmTitle)
            alert.show()
        }
    }
    
    fileprivate func canUseAlbum()-> Bool{
        if PHPhotoLibrary.authorizationStatus() != PHAuthorizationStatus.authorized {
            let alertView = UIAlertView(title: TGPhotoPickerConfig.shared.PhotoLibraryUsage, message: TGPhotoPickerConfig.shared.PhotoLibraryUsageTip, delegate: nil, cancelButtonTitle: TGPhotoPickerConfig.shared.confirmTitle, otherButtonTitles: TGPhotoPickerConfig.shared.cancelTitle)
            alertView.tag = TGPhotoPickerConfig.shared.alertViewTag
            alertView.show()
            return false
        }else{
            return true
        }
    }
}
           
效果如下
iOS10自定义相机(swift)
完整使用DEMO见https://github.com/targetcloud/TGPhotoPicker

点击打开链接