眾所周知,想要獲取系統(tǒng)相冊照片和視頻,也就是調(diào)起UIImagePickerController,創(chuàng)建控制器、遵守代理、實現(xiàn)代理方法、彈出/關(guān)閉控制器,拿張照片都如此繁瑣。為了更快獲取系統(tǒng)相冊照片和視頻,于是封裝了一個系統(tǒng)相冊的工具類ImagePicker,內(nèi)部實現(xiàn)好代理和彈出/關(guān)閉的操作,方便自己平時調(diào)試。
- ??
ImagePicker封裝的方法其一:打開相冊,通過閉包返回照片圖片:
func getPhoto() {
ImagePicker.openAlbumForImage { [weak self] result in
guard let self = self else { return }
switch result {
case let .success(image):
self.setupImage(image)
case let .failure(pickError):
pickError.log()
}
}
}
這么一看好像已經(jīng)夠簡潔了,不過Swift現(xiàn)在已經(jīng)有async/await的特性了,那是不是可以通過一句代碼就能獲取到圖片呢?
答案是肯定可以的啦。
從Swift中的async/await代碼實例詳解這篇文章中得知,可以通過withCheckedThrowingContinuation將【基于閉包異步處理結(jié)果】轉(zhuǎn)換成【結(jié)構(gòu)化并發(fā)同步處理結(jié)果】,實現(xiàn)一句代碼獲取系統(tǒng)相冊照片:
func getPhoto() async {
let image: UIImage? = try? await ImagePicker.openAlbum()
imgView.image = image
}
這樣看上去就真的超簡潔了~
- 主要實現(xiàn)方式:
// MARK: - Pick object handle
private extension ImagePicker.Controller {
// 以前的方式:
// - 保存閉包,直至代理方法的調(diào)起,然后通過該閉包以返回結(jié)果
// - 外部調(diào)用:picker.pickObject() { result in ...... },通過閉包異步獲取結(jié)果
func pickObject(completion: @escaping ImagePicker.Completion<T>) {
self.completion = completion
}
// 現(xiàn)在的方式:
// - 通過`withCheckedThrowingContinuation`將【基于閉包異步處理結(jié)果】轉(zhuǎn)換成【結(jié)構(gòu)化并發(fā)同步處理結(jié)果】
// - 外部調(diào)用:let object: T? = try? await picker.pickObject(),等待并同步獲取結(jié)果
func pickObject() async throws -> T {
try await withCheckedThrowingContinuation { continuation in
// 在以前的方式中這個閉包是給外部使用的,現(xiàn)在的方式修改成【內(nèi)部使用】:
// 外部先卡住函數(shù),等待代理方法的調(diào)起,然后通過`continuation`將結(jié)果返回給外部,實現(xiàn)同步處理。
pickObject() { result in
continuation.resume(with: result)
}
}
}
// MARK: - UIImagePickerControllerDelegate
// 用戶選擇了照片/視頻
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
// 1.獲取結(jié)果
let result: Result<T, ImagePicker.PickError>
do {
result = .success(try T.fetchFromPicker(info))
} catch let pickError as ImagePicker.PickError {
result = .failure(pickError)
} catch {
result = .failure(.other(error))
}
// 2.返回結(jié)果
completion?(result)
// 3.關(guān)閉控制器
dismiss(animated: true)
}
// 用戶點擊了取消
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
// 1.返回結(jié)果:用戶點擊取消
completion?(.failure(.userCancel))
// 2.關(guān)閉控制器
dismiss(animated: true)
}
}
- 通過泛型和重載的特性擴展一下,返回更多類型:
// 相冊 -> 圖片
let image: UIImage? = try? await ImagePicker.openAlbum()
// 相冊 -> 二進制數(shù)據(jù)(圖片、GIF)
let imageData: Data? = try? await ImagePicker.openAlbum()
// 相冊 -> 視頻路徑
let videoURL: URL? = try? await ImagePicker.openAlbum()
// 拍照 -> 圖片
let image: UIImage? = try? await ImagePicker.photograph()
PS:當然啦,肯定會有拿不到的情況,所有失敗的場景我都使用了ImagePicker.PickError拋出,可通過do {} catch {}捕獲。
至此,封裝的ImagePicker可以很方便地讓我獲取系統(tǒng)相冊的照片和視頻。
不過獲取相冊數(shù)據(jù)一般都會用第三方庫來做,我這個工具類只是更多的用來平時的調(diào)試,最主要是熟悉一下async/await的特性。
OK,本文就到此為止,祝大家新年快樂~