# 이미지 피커
사용자 핸드폰 앨범에 접근하여 사진 및 미디어를 불러와야 하는 경우가 있다. 이때 사용하는 것이 PHPickerViewController
이다.
PHPickerView 호환성
이미지 피커뷰를 구현하는 방식이 iOS 14버전 이후로 PHPickerView로 변경되면서 이전 버전에서는 에러가 발생하니 주의하자.
피커뷰를 사용하기 위한 과정은 아래와 같다.
PhotosUI
를 임포트한다.- 뷰 컨트롤러의 확장에
PHPickerViewControllerDelegate
델리게이트 프로토콜을 채택한다. - 위 프로토콜의 필수 구현 함수인
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult])
를 구현한다. (구현 코드는 아래에서 정리) - 피커뷰 컨트롤러를 세팅한다.
피커뷰 컨트롤러의 프로토콜 함수 코드는 아래와 같다.
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
// 피커뷰 사진 선택 환료 및 취소시 dismiss
picker.dismiss(animated: true)
// 아이템 프로바이더 객체 가져오기
let itemProvider = results.first?.itemProvider
// itemProvider가 nil이 아니고
// itemProvider가 UIImage 메타타입에 대해 오브젝트 로딩이 가능하면
// 아래 동작들을 실행한다.
if let itemProvider = itemProvider, itemProvider.canLoadObject(ofClass: UIImage.self){
// 아이템 프로바이더에서 불러온 객체를 로드한다.
// 콜백 형태로 받는다.
// 디스패치큐에 원하는 객체로 데이터 등록을 하는 코드를 삽입해놓으면 OS가 자동으로 해당 코드를 실행한다.
itemProvider.loadObject(ofClass: UIImage.self){ (image, error) in
DispatchQueue.main.async{
// image파라미터의 타입은 NSItemProviderReading? 이므로
// 타입 다운캐스팅이 필요하다.
self.detailView.profileImageView.image = image as? UIImage
}
}
} else {
print("Load Error")
}
}
아이템 프로바이더 객체는 객체 드래그앤드롭 및 복사 붙여넣기 시 파일을 중개해주는 객체라고 한다.
An item provider for conveying data or a file between processes during drag-and-drop or copy/paste activities, or from a host app to an app extension.
피커뷰를 세팅하는 코드는 아래와 같다.
func setupImagePicker(){
// 피커뷰 설정 관련 인스턴스
var configuration = PHPickerConfiguration()
// The default value is 1. Setting the value to 0 sets the selection limit to the maximum that the system supports.
// 디폴트는 1개를 가져올 수 있고 0개 선택시 무한대로 가져올 수 있다고 함
configuration.selectionLimit = 0
// 애셋 타입을 지정한다. Live Photo 등을 가져올 수도 있음
configuration.filter = .any(of: [.images, .videos])
// 피커뷰 객체 생성 시 파라미터에 설정을 전달
let picker = PHPickerViewController(configuration: configuration)
// 델리게이트 지정
picker.delegate = self
// 화면에 띄우기
present(picker, animated: true)
}
# 제스처 등록하기
웹 개발시 미리 정의된 특정 사용자 이벤트들을 (클릭, 드래그 등) 처리하는 방법으로 addEventListener
가 있었다. 이와 유사하게 스위프트에서는 addGestureRecognizer
가 있다. UITapGestureRecognizer
타입의 인스턴스를 생성한 뒤 해당 메서드의 파라미터로 전달하면 된다.
func setupGestures(){
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(touchUpImageView))
detailView.profileImageView.addGestureRecognizer(tapGesture)
// 제스처를 활성화 하는 속성이 있어야 동작함
detailView.profileImageView.isUserInteractionEnabled = true
}
@objc func touchUpImageView(){
setupImagePicker()
}
func setupImagePicker(){
var configuration = PHPickerConfiguration()
configuration.selectionLimit = 0
configuration.filter = .any(of: [.images, .videos])
let picker = PHPickerViewController(configuration: configuration)
picker.delegate = self
present(picker, animated: true)
}
info.plist
사용자 개인정보와 관련된 기능들을 사용하기 위해서는 사용권한 허용과 관련된 설정들이 모여있는 info.plist
에 기능 목록을 올려야한다.
Information Property List
에 마우스를 올려두면 우측에 작게 플러스 버튼이 노출되는데 이를 클릭하면 관련 허용 설정들이 나타난다. 검색할때 대소문자 구분이 엄격하므로 이에 주의하여 검색하자.
# 노티피케이션
각종 이벤트 처리 패턴으로 델리게이트 패턴도 있지만 NotificationCenter
객체를 활용한 방식도 존재한다. 델리게이트 패턴은 이벤트를 처리하는 대리자와 컨트롤러가 1대1로 소통한다고 하면 노티피케이션은 싱글톤 패턴에 따라 한 곳에서 이벤트를 송수신하는 형태로 이루어진다.
func setNotification(){
NotificationCenter.default.addObserver(self, selector: #selector(moveUpAction), name: UIResponder.keyboardWillShowNotification, object: nil)
}
// 셀렉터 함수 예시 코드이므로 아래부터는 원하는대로 정의한다.
@objc func moveUpAction(){
stackviewTopAnchor.constant = -20
UIView.animate(withDuration: 0.2) {
self.layoutIfNeeded()
}
}
NotificationCenter.default.addObserver
메서드를 통해 이벤트를 지정하고, 해당 이벤트 감지에 따라 어떤 함수를 실행시킬지 셀렉터를 등록한다. object
파라미터는 일반적으로 nil
로 설정한다.
노티피케이션을 등록했으면 반드시 뷰가 해제될때 (소멸자가 호출될때) 옵저버를 삭제해줘야 한다.
deinit{
NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillShowNotification, object: nil)
}