iOS8自定义相机(swift)

来源:互联网 发布:各种域名区别 编辑:程序博客网 时间:2024/06/04 08:21
iOS不同版本的自定义有所不同的,本文介绍iOS8下的自定义相机,所用语言为swift3+

////  TGCameraVCForiOS8.swift//  TGPhotoPicker////  Created by targetcloud on 2017/8/11.//  Copyright © 2017年 targetcloud. All rights reserved.//import UIKitimport AVFoundationimport Photosclass TGCameraVCForiOS8: UIViewController {    var callbackPicutureData: ((Data?) -> ())?        fileprivate var device: AVCaptureDevice?    fileprivate lazy var session : AVCaptureSession = AVCaptureSession()    fileprivate lazy var previewLayer : AVCaptureVideoPreviewLayer = AVCaptureVideoPreviewLayer(session: self.session)    fileprivate lazy var imageOutput : AVCaptureStillImageOutput = AVCaptureStillImageOutput()    fileprivate var input : AVCaptureDeviceInput?    fileprivate var showImageContainerView: UIView?    fileprivate var showImageView: UIImageView?    fileprivate var flashMode: AVCaptureFlashMode = .auto    fileprivate var picData: Data?    fileprivate var image: UIImage?        fileprivate lazy var takeButton: UIButton = {        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)        return takeButton    }()        fileprivate lazy var cameraChangeButton: UIButton = {        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: self.takeButton.center.y)        cameraChangeButton.addTarget(self, action: #selector(changeCameraPositionAction), for: .touchUpInside)        cameraChangeButton.contentMode = .scaleAspectFit        return cameraChangeButton    }()        fileprivate lazy var flashButton: UIButton = {        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: self.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        return flashChangeButton    }()        @objc fileprivate func flashChangeAction(){        guard let device = device  else {            return        }        do {            try device.lockForConfiguration()            switch flashMode {            case .auto:                if device.isFlashModeSupported(.on) {                    device.flashMode = .on                    flashMode = .on                    flashButton.setImage(TGPhotoPickerConfig.getImageNo2x3xSuffix("flash"), for: .normal)                }            case .on:                if device.isFlashModeSupported(.off) {                    device.flashMode = .off                    flashMode = .off                    flashButton.setImage(TGPhotoPickerConfig.getImageNo2x3xSuffix("flashno"), for: .normal)                }            case .off:                if device.isFlashModeSupported(.auto) {                    device.flashMode = .auto                    flashMode = .auto                    flashButton.setImage(TGPhotoPickerConfig.getImageNo2x3xSuffix("flashauto"), for: .normal)                }            }            device.unlockForConfiguration()        } catch {            return        }    }        fileprivate func canUseCamera()-> Bool{        let authStatus = AVCaptureDevice.authorizationStatus(forMediaType: AVMediaTypeVideo)        if authStatus == .denied{            let alertView = UIAlertView(title: TGPhotoPickerConfig.shared.cameraUsage, message: TGPhotoPickerConfig.shared.cameraUsageTip, delegate: self, cancelButtonTitle: TGPhotoPickerConfig.shared.confirmTitle, otherButtonTitles: TGPhotoPickerConfig.shared.cancelTitle)            alertView.tag = TGPhotoPickerConfig.shared.alertViewTag            alertView.show()            return false        }else{            return true        }    }        fileprivate func canUseAlbum()-> Bool{        if PHPhotoLibrary.authorizationStatus() != PHAuthorizationStatus.authorized {            let alertView = UIAlertView(title: TGPhotoPickerConfig.shared.PhotoLibraryUsage, message: TGPhotoPickerConfig.shared.PhotoLibraryUsageTip, delegate: self, cancelButtonTitle: TGPhotoPickerConfig.shared.confirmTitle, otherButtonTitles: TGPhotoPickerConfig.shared.cancelTitle)            alertView.tag = TGPhotoPickerConfig.shared.alertViewTag            alertView.show()            return false        }else{            return true        }    }        fileprivate lazy var focusView: UIView = {        let focusView =  UIView(frame: CGRect(x: 0, y: 0, width: TGPhotoPickerConfig.shared.focusViewWH, height: TGPhotoPickerConfig.shared.focusViewWH))        focusView.layer.borderWidth = 1.0        focusView.layer.borderColor = TGPhotoPickerConfig.shared.tinColor.cgColor        focusView.backgroundColor = .clear        focusView.isHidden = true        return focusView    }()        override func viewDidLoad() {        super.viewDidLoad()        if canUseCamera() {            setupCamera()            setupUI()        }else{            return        }                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    }        fileprivate func setupUI(){        self.view.addSubview(self.takeButton)        self.view.addSubview(self.focusView)        self.view.addSubview(self.cameraChangeButton)        self.view.addSubview(self.flashButton)                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: self.flashButton.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?.layer.masksToBounds = true        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)                let tapGesture = UITapGestureRecognizer(target: self, action: #selector(focusGesture))        self.view.addGestureRecognizer(tapGesture)    }        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 backAction() {        dismiss(animated: true, completion: nil)    }        @objc private func giveupImageAction() {        showImageView?.image = UIImage()        showImageContainerView?.isHidden = true    }        @objc private func useImageAction() {        callbackPicutureData?(picData)        dismiss(animated: true, completion: nil)    }        @objc fileprivate func focusGesture(gesture: UITapGestureRecognizer){        let point = gesture.location(in: gesture.view)        focusAtPoint(point)    }        fileprivate func focusAtPoint(_ point: CGPoint){        let size = self.view.bounds.size        let focusPoint = CGPoint(x: point.y/size.height, y: 1-point.x/size.width)        do {            try device?.lockForConfiguration()        } catch {            return        }        if device?.isFocusModeSupported(AVCaptureFocusMode.autoFocus) ?? false{            device?.focusPointOfInterest = focusPoint            device?.focusMode = .autoFocus        }        if device?.isExposureModeSupported(AVCaptureExposureMode.autoExpose) ?? false{            device?.exposurePointOfInterest = focusPoint            device?.exposureMode = .autoExpose        }        device?.unlockForConfiguration()        focusView.center = point        focusView.isHidden = false        UIView.animate(withDuration: 0.3, animations: {            self.focusView.transform = CGAffineTransform(scaleX: 1.25, y: 1.25)        }) { (finished) in            UIView.animate(withDuration: 0.5, animations: {                self.focusView.transform = .identity            }, completion: { (finished) in                self.focusView.isHidden = true            })        }    }        fileprivate func setupCamera(){        self.view.backgroundColor = .white        setupVideo()    }        @objc fileprivate func changeCameraPositionAction() {        let cameraCount = AVCaptureDevice.devices(withMediaType: AVMediaTypeVideo).count        guard cameraCount>0 else { return }                let rotaionAnim = CATransition()        rotaionAnim.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)        rotaionAnim.type = TGPhotoPickerConfig.shared.transitionType        rotaionAnim.duration = 0.5                guard let videoInput = input else { return }        let position : AVCaptureDevicePosition = videoInput.device.position == .front ? .back : .front        rotaionAnim.subtype = (position == .front) ? "fromRight" : "fromLeft"                guard let devices = AVCaptureDevice.devices(withMediaType: AVMediaTypeVideo) as? [AVCaptureDevice] else { return }        guard let newDevice = devices.filter({$0.position == position}).first else { return }        guard let newVideoInput = try? AVCaptureDeviceInput(device: newDevice) else { return }                previewLayer.add(rotaionAnim, forKey: nil)                session.beginConfiguration()        session.removeInput(videoInput)        if session.canAddInput(newVideoInput) {            session.addInput(newVideoInput)            self.input = newVideoInput        } else {            session.addInput(input)        }        session.commitConfiguration()    }        @objc fileprivate func takePhotoAction(){        guard let videoConnection = imageOutput.connection(withMediaType: AVMediaTypeVideo) else { return }        imageOutput.captureStillImageAsynchronously(from: videoConnection) { (imageDataSampleBuffer, error) in            if error != nil {                print("error = \(String(describing: error?.localizedDescription))")            } else {                guard imageDataSampleBuffer != nil  else {return}                guard let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(imageDataSampleBuffer) else {return}                                self.picData = imageData                self.showImageContainerView?.isHidden = false                self.image = UIImage(data: imageData)                self.showImageView?.image = self.image                                if TGPhotoPickerConfig.shared.saveImageToPhotoAlbum{                    self.saveImageToPhotoAlbum(self.image!)                }                                print("image size\(String(describing: self.image?.size))")            }        }    }        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 setupVideo() {        guard let devices = AVCaptureDevice.devices(withMediaType: AVMediaTypeVideo) as? [AVCaptureDevice] else {return}        guard let device = devices.filter({$0.position == .back}).first else {return}        guard let videoInput = try? AVCaptureDeviceInput(device: device) else {return}        self.input = videoInput        self.device = device                if session.canSetSessionPreset(TGPhotoPickerConfig.shared.sessionPreset) {            session.sessionPreset = TGPhotoPickerConfig.shared.sessionPreset        }        if session.canAddInput(input) {            session.addInput(input)        }else{            session.addInput(videoInput)        }        if session.canAddOutput(imageOutput) {            session.addOutput(imageOutput)        }else{            session.addOutput(imageOutput)        }                previewLayer.frame = view.bounds        self.previewLayer.videoGravity = TGPhotoPickerConfig.shared.videoGravity        view.layer.insertSublayer(previewLayer, at: 0)        session.startRunning()                do {            try device.lockForConfiguration()        } catch {            return        }        if device.isFlashModeSupported(AVCaptureFlashMode.auto){            device.flashMode = .auto        }                if device.isWhiteBalanceModeSupported(AVCaptureWhiteBalanceMode.autoWhiteBalance){            device.whiteBalanceMode = .autoWhiteBalance        }        device.unlockForConfiguration()    }}extension TGCameraVCForiOS8: UIAlertViewDelegate{    func alertView(_ alertView: UIAlertView, clickedButtonAt buttonIndex: Int) {        if buttonIndex == 0 && alertView.tag == TGPhotoPickerConfig.shared.alertViewTag {            guard let url = NSURL(string: UIApplicationOpenSettingsURLString) else {                return            }            if UIApplication.shared.canOpenURL(url as URL){                UIApplication.shared.openURL(url as URL)            }        }    }}

其实现了对焦、闪光灯等许多特性,效果如下


完整Demo见https://github.com/targetcloud/TGPhotoPicker
点击打开链接