swift3.0 自定义相册相机

贡献者:赵大财
博客:https://my.oschina.net/zhaodacai GitHub:https://github.com/dacaizhao
邮箱: dacai_zhao@163.com  QQ:327532817
============================= 
地址:https://github.com/dacaizhao/swiftCameraAlbum 给我Star吧

使用方法:

//
//  ViewController.swift
//  swiftCameraAlbum
//
//  Created by point on 2016/11/28.
//  Copyright © 2016年 dacai. All rights reserved.
//

import UIKit

class ViewController: UIViewController {
    
    
    @IBOutlet weak var cameraView: UIView! //相机显示
    @IBOutlet weak var focusView: UIImageView! //聚焦图片
    @IBOutlet weak var takePicListCollectionView: TakePicListCollectionView!//展示列表
    fileprivate var imgArr: [UIImage] = [UIImage]() //拍照的图片
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        //启动照相机
        DCCameraAlbum.shareCamera.start(view: cameraView, frame: CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height-150))
        
        //设置聚焦图片
        DCCameraAlbum.shareCamera.focusView = focusView
        
        //
    }
    
    
    //获取指定的相册 需要一定回调时间
    @IBAction func getAlbumThumb(_ sender: UIButton) {
        //默认的就是第一个
        //DCCameraAlbum.shareCamera.getAlbumItemFetchResultsDefault(thumbnailSize: <#T##CGSize#>, finishedCallback: <#T##([UIImage]) -> ()#>)
        //你还可以这样做 你可以查看任何一个相册  需要一定的等待时间 所以是回调
        let itemArr = DCCameraAlbum.shareCamera.getAlbumItem()
        let resulet  = itemArr.first?.fetchResult
        let size = CGSize(width: 100, height: 100)
        DCCameraAlbum.shareCamera.getAlbumItemFetchResults(assetsFetchResults: resulet!, thumbnailSize: size) { [unowned self] (imgarr) in
            
            let vc = DCAlbumListViewController()
            vc.imgArr = imgarr
            self.present(vc, animated: true, completion: nil)
            
        }
    }
    
    // MARK:- 获取相册列表
    @IBAction func getAlbumListClick(_ sender: UIButton) {
        let vc = DCAlbumViewController()
        vc.dcAlbumItem =  DCCameraAlbum.shareCamera.getAlbumItem()
        present(vc, animated: true, completion: nil)
        
    }
    
    
    // MARK:- 拍摄照片
    @IBAction func takePicClick(_ sender: UIButton) {
        
        DCCameraAlbum.shareCamera.takePhoto { [unowned self] (image) in
            self.imgArr.append(image)
            self.takePicListCollectionView.imgArr = self.imgArr
            
        }
    }
    
    // MARK:- 闪光灯管理
    @IBAction func flashSegment(_ sender: UISegmentedControl) {
        switch sender.selectedSegmentIndex {
        case 0:
            DCCameraAlbum.shareCamera.flashLamp(mode: .auto)
        case 1:
            DCCameraAlbum.shareCamera.flashLamp(mode: .on)
        case 2:
            DCCameraAlbum.shareCamera.flashLamp(mode: .off)
        default:
            break
        }
    }
}

实现工具类

//
//  DCCameraAlbum.swift
//  DCCameraAlbum
//
//  Created by point on 2016/11/28.
//  Copyright © 2016年 dacai. All rights reserved.
//

import UIKit
import AVFoundation
import Photos

struct Platform {
    static let isSimulator: Bool = {
        var isSim = false
        #if arch(i386) || arch(x86_64)
            isSim = true
        #endif
        return isSim
    }()
}

enum flashMode:Int {
    case off
    case on
    case auto
}

class DCCameraAlbum: NSObject {
    
    // MARK:- 相机属性
    fileprivate lazy var session : AVCaptureSession = AVCaptureSession()
    fileprivate lazy var inputDevice : AVCaptureDeviceInput = AVCaptureDeviceInput() //输入源
    fileprivate lazy var imageOutput : AVCaptureStillImageOutput = AVCaptureStillImageOutput() //输出
    lazy var priviewLayer : AVCaptureVideoPreviewLayer = AVCaptureVideoPreviewLayer() //视
    fileprivate var currentView: UIView! //管理控制器
    fileprivate var isUsingBackCamera:Bool = true //是否正在使用后置摄像头
    fileprivate var videoInput : AVCaptureDeviceInput?
    fileprivate var currentD :AVCaptureDevice!
    var focusView :UIView! //聚焦的View
    fileprivate var effectiveScale:CGFloat = 1.0 //默认缩放
    fileprivate var beginGestureScale:CGFloat = 1.0 //
    fileprivate let maxScale:CGFloat = 2.0 //最大缩放
    fileprivate let minScale:CGFloat = 1.0 //最小缩放
    
    // MARK:- 相册属性
    fileprivate var dCAlbumItems:[DCAlbumItem] = [] //相册列表
    fileprivate var imageManager:PHCachingImageManager! //带缓存的图片管理对象
    
    //单例
    internal static let shareCamera:DCCameraAlbum = {
        let camera = DCCameraAlbum()
        return camera
    }()
    
    //初始化
    override init() {
        super.init()
        if Platform.isSimulator {
            print("请不要使用模拟器测试")
        }
        else {
            installCameraDevice() //初始化摄像机
            
        }
    }
}

// MARK:- =============相册
class DCAlbumItem {
    //相簿名称
    var title:String?
    //相簿内的资源
    var fetchResult:PHFetchResult<PHAsset>
    init(title:String?,fetchResult:PHFetchResult<PHAsset>){
        self.title = title
        self.fetchResult = fetchResult
    }
}

// MARK: - 获取相册集合
extension DCCameraAlbum {
    // MARK: - 获取指定图片
    func getOriginalPicture(picAsset:PHAsset , finishedCallback: @escaping (_ image: UIImage) -> ()) {
        PHImageManager.default().requestImage(for: picAsset,
                                              targetSize: PHImageManagerMaximumSize , contentMode: .aspectFit,
                                              options: nil, resultHandler: {
                                                (image, _: [AnyHashable: Any]?) in
                                                finishedCallback(image!)
        })
    }
    
    // MARK: - 获取指定的相册缩略图列表
    func getAlbumItemFetchResults(assetsFetchResults: PHFetchResult<PHAsset> , thumbnailSize: CGSize , finishedCallback: @escaping (_ result : [UIImage] ) -> ()){
        cachingImageManager()
        let imageArr = fetchImage(assetsFetchResults: assetsFetchResults, thumbnailSize: thumbnailSize)
        finishedCallback(imageArr)
    }
    
    // MARK: - 获取默认的照相机照片缩略图列表
    func getAlbumItemFetchResultsDefault(thumbnailSize: CGSize , finishedCallback: @escaping (_ result : [UIImage] ) -> ()) {
        cachingImageManager()
        let allPhotosOptions = PHFetchOptions()
        //按照创建时间倒序排列
        allPhotosOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate",ascending: false)]
        //只获取图片
        allPhotosOptions.predicate = NSPredicate(format: "mediaType = %d",PHAssetMediaType.image.rawValue)
        let assetsFetchResults = PHAsset.fetchAssets(with: .image, options: allPhotosOptions)
        let imageArr = fetchImage(assetsFetchResults: assetsFetchResults, thumbnailSize: thumbnailSize)
        finishedCallback(imageArr)
        
    }
    
    //缓存管理
    fileprivate func cachingImageManager(){
        imageManager = PHCachingImageManager()
        imageManager.stopCachingImagesForAllAssets()
    }
    
    //获取图片
    fileprivate func fetchImage(assetsFetchResults:  PHFetchResult<PHAsset> , thumbnailSize: CGSize) -> [UIImage] {
        var imageArr:[UIImage] = []
        for i in 0..<assetsFetchResults.count {
            print(i)
            let asset = assetsFetchResults[i]
            self.imageManager.requestImage(for: asset, targetSize: thumbnailSize,
                                           contentMode: PHImageContentMode.aspectFill,
                                           options: nil) { (image, nfo) in
                                            imageArr.append(image!)
            }
        }
        return imageArr
    }
    
}
// MARK: - 获取相册列表
extension DCCameraAlbum {
    func getAlbumItem() -> [DCAlbumItem]{
        dCAlbumItems.removeAll()
        let smartOptions = PHFetchOptions()
        let smartAlbums = PHAssetCollection.fetchAssetCollections(with: .smartAlbum,
                                                                  subtype: PHAssetCollectionSubtype.albumRegular,
                                                                  options: smartOptions)
        self.convertCollection(smartAlbums as! PHFetchResult<AnyObject>)
        
        //列出所有用户创建的相册
        let userCollections = PHCollectionList.fetchTopLevelUserCollections(with: nil)
        self.convertCollection(userCollections as! PHFetchResult<AnyObject>)
        
        //相册按包含的照片数量排序(降序)
        self.dCAlbumItems.sort { (item1, item2) -> Bool in
            return item1.fetchResult.count > item2.fetchResult.count
        }
        return dCAlbumItems
        
    }
    
    //转化处理获取到的相簿
    fileprivate func convertCollection(_ collection:PHFetchResult<AnyObject>){
        
        for i in 0..<collection.count{
            //获取出但前相簿内的图片
            let resultsOptions = PHFetchOptions()
            resultsOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate",
                                                               ascending: false)]
            resultsOptions.predicate = NSPredicate(format: "mediaType = %d",
                                                   PHAssetMediaType.image.rawValue)
            guard let c = collection[i] as? PHAssetCollection else { return }
            let assetsFetchResult = PHAsset.fetchAssets(in: c,options: resultsOptions)
            //没有图片的空相簿不显示
            if assetsFetchResult.count > 0{
                self.dCAlbumItems.append(DCAlbumItem(title: c.localizedTitle, fetchResult: assetsFetchResult ))
            }
        }
    }
}

// MARK:- =============相机
// MARK: - 开始
extension DCCameraAlbum {
    func start(view: UIView , frame: CGRect){
        currentView = view
        addPrviewLayerToView(frame: frame)
        setUpGesture() //添加手势
        if session.isRunning == false {
            session.startRunning() //不要用,模拟器测试-_-!
        }
    }
}

// MARK: - 结束
extension DCCameraAlbum {
    func stop(){
        if session.isRunning == true {
            session.stopRunning()
        }
    }
}

// MARK: - 拍照
extension DCCameraAlbum {
    func takePhoto(finishedCallback :  @escaping (_ result : UIImage ) -> ()){
        let captureConnetion = imageOutput.connection(withMediaType: AVMediaTypeVideo)
        captureConnetion?.videoScaleAndCropFactor = effectiveScale
        imageOutput.captureStillImageAsynchronously(from: captureConnetion) { (imageBuffer, error) in
            let jpegData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(imageBuffer)
            let jpegImage = UIImage(data: jpegData!)
            //图片入库
            UIImageWriteToSavedPhotosAlbum(jpegImage!, self,nil, nil)
            finishedCallback(jpegImage!)
        }
    }
}

// MARK: - 闪光灯
extension DCCameraAlbum {
    func flashLamp(mode:flashMode){
        do{ try currentD.lockForConfiguration() }catch{ }
        if currentD.hasFlash == false { return }
        if mode.rawValue == 0 { currentD.flashMode = .off}
        if mode.rawValue == 1 { currentD.flashMode = .on}
        if mode.rawValue == 2 { currentD.flashMode = .auto}
        
        currentD.unlockForConfiguration()
    }
}

// MARK: - 前后摄像头
extension DCCameraAlbum {
    func beforeAfterCamera(){
        //获取之前的镜头
        guard var position = videoInput?.device.position else { return }
        //获取当前应该显示的镜头
        position = position == .front ? .back : .front
        //创建新的device
        guard let devices = AVCaptureDevice.devices(withMediaType: AVMediaTypeVideo) as? [AVCaptureDevice] else { return }
        // 1.2.取出获取前置摄像头
        let d = devices.filter({ return $0.position == position }).first
        currentD = d
        //input
        guard let videoInput = try? AVCaptureDeviceInput(device: d) else { return }
        
        //切换
        session.beginConfiguration()
        session.removeInput(self.videoInput!)
        session.addInput(videoInput)
        session.commitConfiguration()
        self.videoInput = videoInput
    }
}

// MARK: - 初始化相机相关
extension DCCameraAlbum:UIGestureRecognizerDelegate{
    
    fileprivate func installCameraDevice(){
        // 1.创建输入
        // 1.1.获取所有的设备(包括前置&后置摄像头)
        guard let devices = AVCaptureDevice.devices(withMediaType: AVMediaTypeVideo) as? [AVCaptureDevice] else { return }
        // 1.2.取出获取前置摄像头
        guard let d = devices.filter({ return $0.position == .back }).first else{ return}
        currentD = d
        // 1.3.通过前置摄像头创建输入设备
        guard let inputDevice = try? AVCaptureDeviceInput(device: d) else { return }
        self.videoInput = inputDevice
        
        //输出
        imageOutput = AVCaptureStillImageOutput()
        imageOutput.outputSettings = [AVVideoCodecKey:AVVideoCodecJPEG]
        
        
        //加入
        if session.canAddInput(inputDevice) == true {
            session.addInput(self.videoInput)
        }
        if session.canAddOutput(imageOutput) == true {
            session.addOutput(imageOutput)
        }
        
        //视图
        priviewLayer = AVCaptureVideoPreviewLayer(session:session)
        priviewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
        
        //闪光灯
        do{ try d.lockForConfiguration() }catch{ }
        if d.hasFlash == false { return }
        d.flashMode = AVCaptureFlashMode.auto
        d.unlockForConfiguration()
    }
    
    //显示View
    fileprivate func addPrviewLayerToView(frame:CGRect) -> Void{
        priviewLayer.frame = frame
        //currentView.layer.masksToBounds = true
        currentView.layer.insertSublayer(priviewLayer, at: 0)
    }
    
    
    //添加手势 + 缩放 + 聚焦
    fileprivate func setUpGesture() {
        let pinGesutre = UIPinchGestureRecognizer(target: self, action: #selector(pinFunc(_:)))
        pinGesutre.delegate = self
        currentView.addGestureRecognizer(pinGesutre)
        
        let tapGesutre = UITapGestureRecognizer(target: self, action: #selector(tipFunc(_:)))
        currentView.addGestureRecognizer(tapGesutre)
    }
    
    //添加上聚焦
    @objc func tipFunc(_ ges:UITapGestureRecognizer) {
        let currentPoint  = ges.location(in: currentView)
        currentView.isUserInteractionEnabled = false
        
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.9) {
            self.currentView.isUserInteractionEnabled = true
        }
        
        do{ try currentD.lockForConfiguration() }catch{ }
        if currentD.isFocusModeSupported(.autoFocus) {
            currentD.focusPointOfInterest = currentPoint
            currentD.focusMode = .autoFocus
        }
        if currentD.isExposureModeSupported(.autoExpose) {
            currentD.exposurePointOfInterest = currentPoint
            currentD.exposureMode = .autoExpose
        }
        currentD.unlockForConfiguration()
        focusView.center = currentPoint
        focusView.isHidden = false
        UIView.animate(withDuration: 0.3, animations: {
            self.focusView.transform = CGAffineTransform(scaleX: 1.25, y: 1.25);
        }) { (_) in
            UIView.animate(withDuration: 0.5, animations: {
                self.focusView.transform = CGAffineTransform.identity
            }) { (_) in
                self.focusView.isHidden = true
            }
        }
        
        
    }
    
    func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
        if gestureRecognizer .isKind(of: UIPinchGestureRecognizer.classForCoder()) {
            beginGestureScale = self.effectiveScale
        }
        return true
    }
    
    //添加缩放
    @objc func pinFunc(_ recognizer:UIPinchGestureRecognizer) {
        self.effectiveScale = self.beginGestureScale * recognizer.scale;
        if (self.effectiveScale < 1.0) {
            self.effectiveScale = 1.0;
        }
        let maxScaleAndCropFactor = imageOutput.connection(withMediaType: AVMediaTypeVideo).videoMaxScaleAndCropFactor
        if  self.effectiveScale > maxScaleAndCropFactor {
            self.effectiveScale = maxScaleAndCropFactor;
        }
        CATransaction.begin()
        CATransaction.setAnimationDuration(0.025)
        priviewLayer.setAffineTransform(CGAffineTransform(scaleX: effectiveScale, y: effectiveScale))
        CATransaction.commit()
    }
}

// MARK:- =============权限
extension DCCameraAlbum {
    //必须info.plist 配置上这2句
    //Privacy - Camera Usage Description
    //Privacy - Photo Library Usage Description
    /** 相机权限检测 */
    func cameraPermissions() -> Bool{
        let authStatus:AVAuthorizationStatus = AVCaptureDevice.authorizationStatus(forMediaType: AVMediaTypeVideo)
        
        switch authStatus {
        case .denied , .restricted:
            return false
        case .authorized:
            return true
        case .notDetermined:
            AVCaptureDevice.requestAccess(forMediaType: AVMediaTypeVideo, completionHandler: nil)
            return true
        }
    }
    
    /** 相册权限检测 */
    func photoPermissions() -> Bool{
        let authStatus:PHAuthorizationStatus = PHPhotoLibrary.authorizationStatus()
        switch authStatus {
        case .denied , .restricted:
            return false
        case .authorized:
            return true
        case .notDetermined:
            let vc = UIImagePickerController()
            vc.mediaTypes = UIImagePickerController.availableMediaTypes(for: .photoLibrary)!
            return true
        }
    }
}

 

之上代码兼容9.0 你也可以自己尝试替换10
 

import UIKit
import AVFoundation

class ViewController: UIViewController, AVCapturePhotoCaptureDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
    @IBOutlet weak var cameraView: UIView!
    var captureSession = AVCaptureSession();
    var sessionOutput = AVCapturePhotoOutput();
    var sessionOutputSetting = AVCapturePhotoSettings(format: [AVVideoCodecKey:AVVideoCodecJPEG]);
    var previewLayer = AVCaptureVideoPreviewLayer();

    override func viewWillAppear(_ animated: Bool) {
        let deviceDiscoverySession = AVCaptureDeviceDiscoverySession(deviceTypes: [AVCaptureDeviceType.builtInDuoCamera, AVCaptureDeviceType.builtInTelephotoCamera,AVCaptureDeviceType.builtInWideAngleCamera], mediaType: AVMediaTypeVideo, position: AVCaptureDevicePosition.unspecified)
        for device in (deviceDiscoverySession?.devices)! {
            if(device.position == AVCaptureDevicePosition.front){
                do{
                    let input = try AVCaptureDeviceInput(device: device)
                    if(captureSession.canAddInput(input)){
                        captureSession.addInput(input);

                        if(captureSession.canAddOutput(sessionOutput)){
                            captureSession.addOutput(sessionOutput);
                            previewLayer = AVCaptureVideoPreviewLayer(session: captureSession);
                            previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
                            previewLayer.connection.videoOrientation = AVCaptureVideoOrientation.portrait;
                            cameraView.layer.addSublayer(previewLayer);
                        }
                    }
                }
                catch{
                    print("exception!");
                }
            }
        }
    }

 

转载于:https://my.oschina.net/zhaodacai/blog/795707

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值