[iOS] UIImagePickerController

728x90
반응형

 

UIImagePickerController를 통해 사진,동영상 촬영/저장에 대해 알아보겠습니다.

 

UIImagePickerController

사진, 동영상 촬영, 저장에 관해 포스트 해보도록 하겠습니다.
먼저 간단하게 앨범카메라딜레이촬영동영상Editing변경버튼을 만들어 간단한 기능 소개를 해보겠습니다.

만들어보죠

 

UIImagePickerController 객체 생성

private let imagePickerController = UIImagePickerController()

 

앨범 접근

@IBAction private func pickImage(_ sender: Any) {
    print("\n---------- [ pickImage ] ----------\n")
    
    //간단히 present만으로 imagePickerController를 띄울수 있다.
    //present(imagePickerController, animated: true, completion: nil)
    
    /*
    public enum UIImagePickerControllerSourceType : Int {
        case photoLibrary // 사진 앨범들
        case camera // 카메라를 띄우는 타입
        case savedPhotosAlbum // 특별한 순간
    }
     */
    
    let type = UIImagePickerControllerSourceType.photoLibrary
    guard UIImagePickerController.isSourceTypeAvailable(type) else { return } // 현재 기기에서 가능한지 확인하는 부분
    
    imagePickerController.sourceType = type
    present(imagePickerController, animated: true, completion: nil)
  }

간단하게 present만으로 imamgePickerController를 띄울 수 있습니다.

UIImagePickerControllerSourceType에는 3가지의 타입이 있습니다. 해당 타입에 맞게 imagePickerController.sourceType = type을 넣어주어 3가지의 ViewController present를 할 수 있습니다.

 

photoLibrary 타입

 

 

카메라 접근

카메라의 촬영화면을 띄우기 위해선 sourceType camera로 설정하고 present를 하면 쉽게 촬영할 수 있다.

@IBAction private func takePicture(_ sender: Any) {
    print("\n---------- [ takePicture ] ----------\n")
    
    //카메라 사용가능한지 체크
    guard UIImagePickerController.isSourceTypeAvailable(.camera) else { return }
    imagePickerController.sourceType = .camera
    
    //imagePickerController.allowsEditing = true // 촬영 후 편집할 수 있는 부분이 나온다.
    
    present(imagePickerController, animated: true, completion: nil)
}

하지만! 이 상태로 바로 실행을 하게 되면 에러가 납니다!

This app has crashed because it attempted to access privacy-sensitive data without a usage description. The app’s Info.plist must contain an NSCameraUsageDescription key with a string value explaining to the user how the app uses this data.

 

해결방법

plist에서 Privacy - Camera Usage Description를 넣어 주어야 된다.

 

allowsEditing은 촬영후 편집 화면을 말합니다.

네모난 라인? 보이시나요

 

 

딜레이 촬영

@IBAction private func takePictureWithDelay(_ sender: Any) {
    print("\n---------- [ takePictureWithDelay ] ----------\n")

    //카메라 사용가능한지 체크
    guard UIImagePickerController.isSourceTypeAvailable(.camera) else { return }
    imagePickerController.sourceType = .camera

    // 클로저를 이용해서 3초 뒤에 나오게 처리
    // 클로저 안에 클로저를 쓸때 self를 쓰게되면 강한 참조가 발생한다.
    // 그래서 이럴때는 weak self 캡처링을 이용해서 약한참조로 만들어준다.
    present(imagePickerController, animated: true) { [weak self] in
        
        // 클로저를 이용한 방법
        // 화면이 올라오자마자 바로 찍히게됨.
        // self.imagePickerController.takePicture()
        
        //3초 뒤에 찍히게.
        DispatchQueue.main.asyncAfter(deadline: .now() + 3, execute: {
            self?.imagePickerController.takePicture() // 여기서는 강한 참조가 발생해서 weak self를 캡처링
        })
    }
}

present의 파마리터에는 completion의 클로저가 있습니다.

비동기 형식이여 카메라의 화면이 보이고 난 후(completion) 동작되는 부분입니다. 

딜레이가 3초가 있다고 생각하고 자동으로 촬영하고 싶다면(딜레이의 방법은 여러가지가 있지만), DispatchQueue를 통해 3초 늘리는 방식을 사용.
takePickture()메서드는 촬영하는 기능입니다.

 

동영상 촬영

1번주석이 되있는부분은 설명이 필요한 부분입니다.

@IBAction private func recordingVideo(_ sender: Any) {
    print("\n---------- [ recordingVideo ] ----------\n")

    guard UIImagePickerController.isSourceTypeAvailable(.camera) else { return }
    imagePickerController.sourceType = .camera

    //1번!!!!!!!
    imagePickerController.cameraCaptureMode = .video

    present(imagePickerController, animated: true, completion: nil)
}

cameraCaptureMode에는 .video, .photo 2가지가 존재하는데 defaults로는 .photo 입니다.
.video를 사용하면 동영상촬영모드가 됩니다.

 

하지만 에러가 납니다!

reason: ‘cameraCaptureMode 1 not available because mediaTypes does contain public.movie’

 

1번

video는 public.movie가 포함되있어야되는데 포함해주지 않아서 그렇습니다.
public.movie같이 public.image, public.item의 자세한 내용은 Apple Documentation UTIs를 참조 하시기 바랍니다.

 

print를 한번 찍어보겠습니다.

//현재 imagePickerController의 mediaTypes
print(imagePickerController.mediaTypes) // public.image
//사용가능한 imagePickerController의 mediaTypes
print(UIImagePickerController.availableMediaTypes(for: .camera) ?? []) // ["public.image", "public.movie"]

사용가능한 imagePickerController의 mediaTypes은 [“public.image”, “public.movie”]입니다.

 

즉, 동영상 촬영을 할때는 meteaTypes에 public.movies를 넣어주면 됩니다.

@IBAction private func recordingVideo(_ sender: Any) {
    print("\n---------- [ recordingVideo ] ----------\n")

    guard UIImagePickerController.isSourceTypeAvailable(.camera) else { return }
    imagePickerController.sourceType = .camera

    imagePickerController.mediaTypes = ["public.movie"] // 추가
    imagePickerController.cameraCaptureMode = .video

    // 카메라 정면, 후면 설정
    imagePickerController.cameraDevice = .rear // 후면     .rear, .front
    // 플래쉬 작동 모드
    imagePickerController.cameraFlashMode = .on // FlashMode  .on, .off, .auto


    present(imagePickerController, animated: true, completion: nil)
}

실행이 되시나요? 하지만, 이번엔 다른 에러가 날껍니다!

This app has crashed because it attempted to access privacy-sensitive data without a usage description. The app’s Info.plist must contain an NSMicrophoneUsageDescription key with a string value explaining to the user how the app uses this data.


왜냐하면, 동영상 촬영은 소리도 같이 들어가죠? 그래서 마이크를 쓰기위해 접근해줘야됩니다.

 

해결방법
plist에 Privacy - Microphone Usage Description를 추가해주어야된다.


Editing변경

위에서 설명했지만 찍힌 사진을 편집 할 수 있는 화면을 나타나게 한겁니다.

간단하게 토글의 기능으로 Editing을 누르면 편집창이 나타나고, 다시 누르면 정상적인 이미지만 나옵니다.

@IBAction private func toggleAllowsEditing(_ sender: Any) {
    print("\n---------- [ toggleAllowsEditing ] ----------\n")
    
    // 누를때 마다 반대가 되게
    imagePickerController.allowsEditing = !imagePickerController.allowsEditing
}

기능은 다 설명했지만 맨 위에 보이는 이미지뷰는 무엇이고, 사진,동영상 저장하는 방법에 대해 알아 보겠습니다! 위에서 했던 기능들은 촬영을 해도 저장이 되지 않았을껍니다. 앨범에 들어가서 사진을 눌러도 그냥 종료됬을겁니다.
앨범에서 사진들의 정보를 가져오거나 촬영후 저장하기 위해서는 Delegate를 구성해야야합니다.
하지만 UIImagePickerControllerDelegate만 구성할 수 없습니다.

// 같이 연결 되어있기 때문에 UIImagePickerControllerDelegate, UINavigationControllerDelegate같이 채택해야된다.
 weak open var delegate: (UIImagePickerControllerDelegate & UINavigationControllerDelegate)?

 

Delegate 구현

UIImagePickerControllerDelegate는 2가지가 있습니다.

//picking을 했을때
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) { }

//취소 했을때
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) { }

 

imagePickerController 구현

func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]
{
    //info에 정보들이 들어있다! Dictionary타입
    print("didFinishPickingMediaWithInfo,", info)

    let mediaType = info[UIImagePickerControllerMediaType] as! NSString

    // 1번!!!!!!!!!
    if UTTypeEqual(mediaType, kUTTypeMovie) {
        // 동영상 촬영 정보가 넘어 옴

        let urlPath = info[UIImagePickerControllerMediaURL] as! NSURL
        if let path = urlPath.path {
            // video를 저장하는 메서드
            UISaveVideoAtPathToSavedPhotosAlbum(path, nil, nil, nil)
        }
    } else {
        // 사진 촬영, 이미지 정보가 넘어옴
        let originalImage = info[UIImagePickerControllerOriginalImage] as! UIImage // as? UIImage를 해도된다.
            let editedImage = info[UIImagePickerControllerEditedImage] as? UIImage
            let selectedImage = editedImage ?? originalImage // editedImage가 nil이면 originalImage를 넣으라는 뜻
            imageView.image = selectedImage

            //2번!!!!!!!!!!!
            UIImageWriteToSavedPhotosAlbum(selectedImage, nil, nil, nil) // 이미지를 저장하는 메서드

            // Delegate 메서드가 구현되어 있지 않을 때는 기본적으로 선택시 종료되도록 기본 구현이 되어있음. ( 그래서 dismiss를 해주어야된다. )
        picker.dismiss(animated: true, completion: nil)
    }
}

Dictionary타입인 info에 정보들이 들어옵니다.

 

1번부분을 보시면 UTTypeEqual메서드를 사용하는데 import MobileCoreServices를 임포트 해주어야 사용가능합니다.
동영상 부분과 사진 촬영부분을 분기 처리 하기위해 사용했지만, info[UIImagePickerControllerOriginalImage]에서 pathExtension(확장자)를 가지고 분기 처리도 할 수 있습니다.

 

2번부분에서 에러가 발생 합니다.

This app has crashed because it attempted to access privacy-sensitive data without a usage description. The app’s Info.plist must contain an NSPhotoLibraryAddUsageDescription key with a string value explaining to the user how the app uses this data.

 

해결방법

plist에서 Privacy - Photo Library Additions Usage Description를 넣어 주어야 된다.

 

//이미지 앨범에 저장 하는 메서드
public func UIImageWriteToSavedPhotosAlbum(_ image: UIImage, _ completionTarget: Any?, _ completionSelector: Selector?, _ contextInfo: UnsafeMutableRawPointer?) 

//동영상 앨범에 저장하는 메서드
public func UISaveVideoAtPathToSavedPhotosAlbum(_ videoPath: String, _ completionTarget: Any?, _ completionSelector: Selector?, _ contextInfo: UnsafeMutableRawPointer?)
이제 사진 및 동영상 저장이 정상적으로 저장이 될겁니다.

 

반응형

'iOS' 카테고리의 다른 글

ARC(Automatic Reference Counting)  (0) 2019.07.24
[iOS] 단축키(Shortcuts)  (0) 2019.07.24
[iOS] TabbarController  (0) 2019.07.24
[iOS] NotificationCenter  (0) 2019.07.24
[iOS] UIDocumentInteractionController  (1) 2019.07.24