Swift/기타

[SWIFT]갤러리 및 카메라 사용 UIImagePickerController(디폴트 UI)

삽질중 2023. 4. 20. 17:36

갤러리와 카메라 사진 촬영 이미지 사용을 위해 싱글톤 타입으로 컴포넌트화 했다.

import UIKit
import MobileCoreServices
import AVFoundation
import Photos

public typealias GlobalBoolCompleteClosure = (Bool) -> Void

enum PHPhotoLibraryAuthorizationError: Error {
    case error(PHAuthorizationStatus)
}

extension PHPhotoLibrary {
    @discardableResult class func syncRequestAuthorization() throws -> PHAuthorizationStatus {
        let status = PHPhotoLibrary.authorizationStatus()
        switch status {
        case .authorized:
            return status
        case .denied, .restricted:
            throw PHPhotoLibraryAuthorizationError.error(status)
        case .notDetermined:
            break
        case .limited:
            break
        default:
            break
        }
        
        let semaphore = DispatchSemaphore(value: 0)
        
        PHPhotoLibrary.requestAuthorization{ _ in
            semaphore.signal()
        }
        
        semaphore.wait()
        
        let newStatus = PHPhotoLibrary.authorizationStatus()
        
        switch newStatus {
        case .authorized:
            return newStatus
        case .denied, .restricted, .notDetermined:
            throw PHPhotoLibraryAuthorizationError.error(newStatus)
        default :
            throw PHPhotoLibraryAuthorizationError.error(newStatus)
        }
    }
}

enum MediaType {
    case CAMERA
    case PHOTO_LIBRARY
}

class ImagePickerInterface: NSObject {
    
    static let shared = ImagePickerInterface()
    
    // UIImagePickerController의 인스턴스 변수 생성
    let imagePicker: UIImagePickerController! = UIImagePickerController()
    // 사진을 저장할 변수
    var captureImage: UIImage!
    // 녹화한 비디오의 URL을 저장할 변수
    var videoURL: URL!
    // 사진 저장 여부를 나타낼 변수
    var flagImageSave = false
    
    var photoImageCompleteClosur: ((Bool, URL?, UIImage?) -> Void)!
    var imageCompleteClosur: ((Bool, UIImage?) -> Void)!
    
    override init() {
        super.init()
        
    }
    
    static func showImagePickerFileUpload(mediaType: MediaType, completion: @escaping (Bool, URL?, UIImage?) -> Void) {
        
        switch mediaType {
        case .PHOTO_LIBRARY:
            ImagePickerInterface.shared.checkPhotoLibraryPermission { (isAuthorize) in
                if isAuthorize == true {
                    ImagePickerInterface.shared.photoImageCompleteClosur = { (isSuccess, file_url, image) in
                        print("Photo Image : \(isSuccess) / \(String(describing: file_url)) / \(String(describing: image))")
                        
                        completion(isSuccess, file_url, image)
                    }
                    
                    ImagePickerInterface.shared.usePhotoLibrary()
                } else {
                    ImagePickerInterface.shared.requestPermissionSettingAlertAction()
                }
                
            }
        case .CAMERA:
            ImagePickerInterface.shared.checkPhotoLibraryPermission { (isAuthorize) in
                
                if isAuthorize == true {
                    ImagePickerInterface.shared.photoImageCompleteClosur = { (isSuccess, file_url, image) in
                        print("Camera Image : \(isSuccess) / \(String(describing: file_url)) / \(String(describing: image))")
                        
                        completion(isSuccess, file_url, image)
                    }
                    ImagePickerInterface.shared.useCamera()
                }
                else {
                    ImagePickerInterface.shared.requestPermissionSettingAlertAction()
                }
            }
            
        }
    }
    
    ///마이크 권한 요청
    private func requestMicPermission(completion:@escaping (Bool)->Void){
        DispatchQueue.main.async {
            AVAudioSession.sharedInstance().requestRecordPermission { (isGranted) in
                completion(isGranted)
            }
        }
    }
    
    ///카메라 권한 요청
    private func requestCameraPermission(completion: @escaping (Bool)->Void ){
        DispatchQueue.main.async {
            if AVCaptureDevice.authorizationStatus(for: .video) == .authorized{
                completion(true)
            }else{
                AVCaptureDevice.requestAccess(for: .video) { (granted) in
                    completion(granted)
                }
            }
        }
    }
    
    private func goToPermissionSetting(){
        if let url = URL(string: UIApplication.openSettingsURLString){
            UIApplication.shared.open(url, options: [:]) { (finish) in
            }
        }
    }
}

extension ImagePickerInterface: UINavigationControllerDelegate, UIImagePickerControllerDelegate {
    // MARK: 앨범 사진 사용
    /// 앨범 사용권한 확인
    private func checkPhotoLibraryPermission(complete: @escaping GlobalBoolCompleteClosure) {
        let status = PHPhotoLibrary.authorizationStatus()
        switch status {
        case .authorized:
            complete(true)
        case .denied :
            complete(false)
        case .notDetermined:    // 선택안함.
            requestPhotoLibraryAuthorization { (isAuthorize) in
                complete(isAuthorize)
            }
            
        default: break
        }
    }
    
    /// 앨범 사용권한 요청
    private func requestPhotoLibraryAuthorization(complete: @escaping GlobalBoolCompleteClosure ) {
        
        DispatchQueue.main.async {
            PHPhotoLibrary.requestAuthorization { (status) in
                switch status{
                case .authorized:
                    complete(true)
                case .denied:
                    complete(false)
                case .restricted:
                    complete(false)
                default:
                    complete(false)
                }
            }
        }
    }
    
    /// 앨범  라이브러리 호출
    private func usePhotoLibrary() {
        if (UIImagePickerController.isSourceTypeAvailable(.photoLibrary)){
            flagImageSave = true
            
            DispatchQueue.main.async { [self] in
                imagePicker.delegate = self
                // 이미지 피커의 소스 타입을 PhotoLibrary로 설정
                imagePicker.sourceType = .photoLibrary
                
                imagePicker.mediaTypes = [kUTTypeImage as String]
                // 편집을 허용
                imagePicker.allowsEditing = false
                
                guard let topVC = UIApplication.currentTopViewController()  else {
                    return
                }
                
                topVC.present(imagePicker, animated: true, completion: nil)
            }
        }
    }
    
    // MARK: 카메라 촬영 사진 사용
    /// 카메라 사용권한 확인
    private func checkCameraPermission(complete: @escaping GlobalBoolCompleteClosure) {
        let status = AVCaptureDevice.authorizationStatus(for: .video)
        switch status {
        case .authorized:
            complete(true)
        case .denied :
            complete(false)
        case .notDetermined:    // 선택안함.
            requestCameraAuthorization { (isAuthorize) in
                complete(isAuthorize)
            }
            
        default: break
        }
    }
    
    /// 카메라 사용권한 요청
    private func requestCameraAuthorization(completion: @escaping GlobalBoolCompleteClosure ) {
        DispatchQueue.main.async {
            if AVCaptureDevice.authorizationStatus(for: .video) == .authorized{
                completion(true)
            }else{
                AVCaptureDevice.requestAccess(for: .video) { (granted) in
                    completion(granted)
                }
            }
        }
    }
    
    /// 카메라 사용
    private func useCamera() {
        if (UIImagePickerController.isSourceTypeAvailable(.camera)){
            flagImageSave = true
            
            DispatchQueue.main.async { [self] in
                imagePicker.delegate = self
                // 미디어 소스의 형태지정 (카메라)
                imagePicker.sourceType = .camera
                // 앱에서 처리할 미디어의 형태 MobileCoreServices 프레임워크에 포함된 값으로 사용할 수 있다.
                imagePicker.mediaTypes = [kUTTypeImage as String]
                // 편집을 허용
                imagePicker.allowsEditing = false
                
                guard let topVC = UIApplication.currentTopViewController()  else {
                    return
                }
                
                topVC.present(imagePicker, animated: true, completion: nil)
            }
        }
    }
    
    
    
    // MARK: 접근권한 설정 알림(권한설정 거부시)
    /// 접근권한 거부시 알림 얼럿 (설정 페이지 이동)
    private func requestPermissionSettingAlertAction() {
        
        let alert = UIAlertController(title: "", message: "앨범의 사진을 사용하려면 사진 권한이 필요합니다. 권한 설정 화면으로 이동 하시겠습니까?", preferredStyle: .alert)
        
        let ok = UIAlertAction(title: "OK", style: .destructive) { (action) in
            log(direction: .SCHEME, ofType: self, datas: "Action Yes")
            
            guard let settingsUrl = URL(string: UIApplication.openSettingsURLString) else {
                return
            }
            print("setting URL :  \(settingsUrl)")
            DispatchQueue.main.async {
                if UIApplication.shared.canOpenURL(settingsUrl) {
                    if #available(iOS 10.0, *) {
                        UIApplication.shared.open(settingsUrl, completionHandler:nil)
                    } else {
                        UIApplication.shared.openURL(settingsUrl)
                    }
                }
            }
        }
        
        let cancel = UIAlertAction(title: "CANCEL", style: .cancel) { (action) in
            log(direction: .SCHEME, ofType: self, datas: "Action Cancel")
        }
        
        alert.addAction(ok)
        alert.addAction(cancel)
        
        guard let topVC = UIApplication.currentTopViewController() else {
            return
        }
        
        topVC.present(topVC, animated: true)
    }
    
    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
        
        
        
        // 미디어 종류 확인
        let mediaType = info[UIImagePickerController.InfoKey.mediaType] as! NSString
        
        // 미디어 종류가 사진(Image)일 경우
        if mediaType.isEqual(to: kUTTypeImage as NSString as String){
            
            // 사진을 가져와 captureImage에 저장
            captureImage = info[UIImagePickerController.InfoKey.originalImage] as? UIImage
            
            if flagImageSave { // flagImageSave가 true이면
                
                // 현재의 뷰 컨트롤러를 제거. 즉, 뷰에서 이미지 피커 화면을 제거하여 초기 뷰를 보여줌
                picker.dismiss(animated: true) { [weak self] in
                    //                    self?.imageFileSave()
                    self?.photoImageCompleteClosur?(true, nil, self?.captureImage)
                }
            }
            
            // 미디어 종류가 비디오(Movie)일 경우
        } else if mediaType.isEqual(to: kUTTypeMovie as NSString as String) {
            
            if let image = info[.originalImage] as? UIImage {
                captureImage = image
            }
            
            if flagImageSave { // flagImageSave가 true이면
                // 촬영한 비디오를 옴
                picker.dismiss(animated: true) { [weak self] in
                    //                    self?.imageFileSave()
                    self?.photoImageCompleteClosur?(true, nil, self?.captureImage)
                }
            }
        }
    }
    
    func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
        picker.dismiss(animated: true, completion: nil)
    }
}

 

사용방법

** 클로저의 fileurl 사용 안함 주의

// 갤러리 라이브러리 사진 이미지 추출
ImagePickerInterface.showImagePickerFileUpload(mediaType: .PHOTO_LIBRARY) { isSuccess, fileurl, image in
    log(direction: .ETC, ofType: self, datas: isSuccess, fileurl)
}

// 카메라 촬영 이미지 추출
ImagePickerInterface.showImagePickerFileUpload(mediaType: .CAMERA) { isSuccess, fileurl, image in
	log(direction: .ETC, ofType: self, datas: isSuccess, fileurl)
}