UIImagePickerController
搭建UI
構(gòu)建如下UI:
設(shè)置按鈕的是否可用:
cameraButton.isEnabled = UIImagePickerController.isSourceTypeAvailable(.camera)
UIImagePickerControllerDelegate的Rx實(shí)現(xiàn)
UIImagePickerController
的代理對象需要遵守UIImagePickerControllerDelegate
和UINavigationControllerDelegate
兩個(gè)協(xié)議的:
weak open var delegate: (UIImagePickerControllerDelegate & UINavigationControllerDelegate)?
由于RxCocoa
已經(jīng)實(shí)現(xiàn)UINavigationControllerDelegate
為RxNavigationControllerDelegateProxy
涂滴,沒有實(shí)現(xiàn)UIImagePickerControllerDelegate
柔纵,所以需要我們自己實(shí)現(xiàn)搁料。
構(gòu)建RxImagePickerDelegateProxy
從前面Delegate
章節(jié)的DelegateProxyType
講到系羞,擁有delegates
的視圖可能是繼承的郭计,RxCocoa
需要?jiǎng)?chuàng)建的那些代理也是繼承的。
由于UIImagePickerController
繼承自UINavigationController
椒振,定義RxImagePickerDelegateProxy
類繼承RxNavigationControllerDelegateProxy
基類昭伸,遵守UIImagePickerControllerDelegate
協(xié)議:
class RxImagePickerDelegateProxy: RxNavigationControllerDelegateProxy, UIImagePickerControllerDelegate {
public init(imagePicker: UIImagePickerController) {
super.init(navigationController: imagePicker)
}
}
首先由于繼承自RxNavigationControllerDelegateProxy
,所以實(shí)現(xiàn)了DelegateProxyType
協(xié)議中定義的函數(shù)澎迎。
UIImagePickerControllerDelegate
協(xié)議中定義的函數(shù)都沒有實(shí)現(xiàn)勋乾,最終會(huì)走消息轉(zhuǎn)發(fā)的方式實(shí)現(xiàn)Rx序列發(fā)送元素宋下。
擴(kuò)展Reactive
首先定義一個(gè)func dismissViewController(viewController: UIViewController, animated: Bool)
函數(shù),用來安全有效地關(guān)閉模態(tài)視圖辑莫,方便后面使用:
func dismissViewController(viewController: UIViewController, animated: Bool) {
/// 是否有控制器在進(jìn)程中沒有顯示或消失
if viewController.isBeingPresented || viewController.isBeingDismissed {
DispatchQueue.main.async {// 異步遞歸調(diào)用
dismissViewController(viewController: viewController, animated: animated)
}
} else if viewController.presentingViewController != nil {
viewController.dismiss(animated: animated, completion: nil)
}
}
定義一個(gè)private func castOrThrow<T>(resultType: T.Type, object: Any) throws -> T
函數(shù)学歧,用于將Any
類型數(shù)據(jù)轉(zhuǎn)換為特定類型,方便后面使用:
private func castOrThrow<T>(resultType: T.Type, object: Any) throws -> T {
guard let resultValue = object as? T else {
throw RxCocoaError.castingError(object: object, targetType: resultType)
}
return resultValue
}
為了方便使用各吨,基于UIImagePickerController
擴(kuò)展Reactive:
extension Reactive where Base: UIImagePickerController {
public var didCancel: Observable<()> {
return delegate.methodInvoked(#selector(UIImagePickerControllerDelegate.imagePickerControllerDidCancel(_:))).map { (_) -> () in }
}
public var didFinishPickingMediaWithInfo: Observable<[UIImagePickerController.InfoKey: AnyObject]> {
return delegate.methodInvoked(#selector(UIImagePickerControllerDelegate.imagePickerController(_:didFinishPickingMediaWithInfo:))).map { (a) in
return try castOrThrow(resultType: Dictionary<UIImagePickerController.InfoKey, AnyObject>.self, object: a[1])
}
}
/// 創(chuàng)建圖片選擇控制器Observable
/// - Parameters:
/// - parent: 父控制器
/// - animated: 動(dòng)畫
/// - configureImagePicker: 配置閉包
static func createWithParent(parent: UIViewController?, animated: Bool = true, configureImagePicker: @escaping (UIImagePickerController) throws -> Void) -> Observable<UIImagePickerController> {
return Observable.create { [weak parent] (observer) -> Disposable in
let imagePicker = UIImagePickerController()
// 取消操作
let dismissDisposable = imagePicker.rx.didCancel.subscribe(onNext: { [weak imagePicker] (_) in
guard let imagePicker = imagePicker else {
return
}
dismissViewController(viewController: imagePicker, animated: animated)
})
// 處理配置閉包
do {
try configureImagePicker(imagePicker)
} catch let error {
observer.onError(error)
return Disposables.create()
}
guard let parent = parent else {
observer.onCompleted()
return Disposables.create()
}
parent.present(imagePicker, animated: animated, completion: nil)
observer.on(.next(imagePicker))
return Disposables.create(dismissDisposable, Disposables.create {
dismissViewController(viewController: imagePicker, animated: animated)
})
}
}
}
代碼分析:
- 使用消息轉(zhuǎn)發(fā)的方式實(shí)現(xiàn)
didCancel
序列,并使用map
操作符更改序列元素為空 - 使用消息轉(zhuǎn)發(fā)的方式實(shí)現(xiàn)
didFinishPickingMediaWithInfo
序列欺冀,并使用map
操作符更改序列元素 -
createWithParent
序列的實(shí)現(xiàn)是使用create
操作符創(chuàng)建,在創(chuàng)建的閉包中創(chuàng)建UIImagePickerController
职车、執(zhí)行配置閉包、present
控制器积瞒、訂閱didCancel
序列進(jìn)行關(guān)閉控制器等
綁定UI
在iOS中使用相機(jī)庐船、相冊資源,需要在info.plist
文件中配置相機(jī)李破、相冊權(quán)限詢問框的描述嗤攻,如下所示:
[圖片上傳失敗...(image-f2b960-1604572278262)]
因?yàn)樵谑褂?code>rx.*之前(例如appDidFinishLaunching)暴区,在registerKnownImplementations
或程序的其他一些地方使用執(zhí)行注冊Rx
實(shí)現(xiàn)的代理房交。
然而RxImagePickerDelegateProxy
沒有實(shí)現(xiàn)registerKnownImplementations
函數(shù)口猜,也沒有再父類中注冊川抡,所以選擇在程序啟動(dòng)時(shí)的appDidFinishLaunching
中注冊RxImagePickerDelegateProxy
:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
RxImagePickerDelegateProxy.register { (p) -> RxImagePickerDelegateProxy in
return RxImagePickerDelegateProxy(imagePicker: p)
}
return true
}
數(shù)據(jù)綁定
/// 拍照是否可用
cameraButton.isEnabled = UIImagePickerController.isSourceTypeAvailable(.camera)
cameraButton.rx.tap.flatMapLatest { [weak self] _ in
return UIImagePickerController.rx.createWithParent(parent: self, animated: true) { (picker) in
picker.sourceType = .camera
picker.allowsEditing = false
}.flatMap { $0.rx.didFinishPickingMediaWithInfo }.take(1)
}.map { $0[.originalImage] as? UIImage }.bind(to: imageView.rx.image).disposed(by: bag)
galleryButton.rx.tap.flatMapLatest { [weak self] (_) in
return UIImagePickerController.rx.createWithParent(parent: self) { (picker) in
picker.sourceType = .photoLibrary
picker.allowsEditing = false
}.flatMap { $0.rx.didFinishPickingMediaWithInfo }.take(1)
}.map { $0[.originalImage] as? UIImage }.bind(to: imageView.rx.image).disposed(by: bag)
cropButton.rx.tap.flatMapLatest { [weak self] (_) in
return UIImagePickerController.rx.createWithParent(parent: self) { (picker) in
picker.sourceType = .photoLibrary
picker.allowsEditing = true
}.flatMap { $0.rx.didFinishPickingMediaWithInfo }.take(1)
}.map { $0[.editedImage] as? UIImage }.bind(to: imageView.rx.image).disposed(by: bag)