RxSwift官方實(shí)例六(UIImagePickerController)

代碼下載

UIImagePickerController

搭建UI

構(gòu)建如下UI:


定位UI

設(shè)置按鈕的是否可用:

        cameraButton.isEnabled = UIImagePickerController.isSourceTypeAvailable(.camera)

UIImagePickerControllerDelegate的Rx實(shí)現(xiàn)

UIImagePickerController的代理對象需要遵守UIImagePickerControllerDelegateUINavigationControllerDelegate兩個(gè)協(xié)議的:

weak open var delegate: (UIImagePickerControllerDelegate & UINavigationControllerDelegate)?

由于RxCocoa已經(jīng)實(shí)現(xiàn)UINavigationControllerDelegateRxNavigationControllerDelegateProxy涂滴,沒有實(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)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末撩轰,一起剝皮案震驚了整個(gè)濱河市偎箫,隨后出現(xiàn)的幾起案子恶复,更是在濱河造成了極大的恐慌副硅,老刑警劉巖伶授,帶你破解...
    沈念sama閱讀 210,914評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件疮蹦,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)比肄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評論 2 383
  • 文/潘曉璐 我一進(jìn)店門妥色,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事惑折。” “怎么了?”我有些...
    開封第一講書人閱讀 156,531評論 0 345
  • 文/不壞的土叔 我叫張陵攻臀,是天一觀的道長。 經(jīng)常有香客問我,道長换团,這世上最難降的妖魔是什么洒扎? 我笑而不...
    開封第一講書人閱讀 56,309評論 1 282
  • 正文 為了忘掉前任邓线,我火速辦了婚禮瑰抵,結(jié)果婚禮上婿崭,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,381評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著戏羽,像睡著了一般酷宵。 火紅的嫁衣襯著肌膚如雪荣挨。 梳的紋絲不亂的頭發(fā)上口锭,一...
    開封第一講書人閱讀 49,730評論 1 289
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 38,882評論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼效斑,長吁一口氣:“原來是場噩夢啊……” “哼藏研!你這毒婦竟也來了业踏?” 一聲冷哼從身側(cè)響起伐脖,我...
    開封第一講書人閱讀 37,643評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,095評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,448評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了苍狰。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,566評論 1 339
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情境肾,我是刑警寧澤环鲤,帶...
    沈念sama閱讀 34,253評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,829評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦涣旨、人聲如沸穆律。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至砌左,卻和暖如春秤朗,著一層夾襖步出監(jiān)牢的瞬間常挚,已是汗流浹背折欠。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留昧捷,地道東北人罐寨。 一個(gè)月前我還...
    沈念sama閱讀 46,248評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親饿悬。 傳聞我的和親對象是個(gè)殘疾皇子蝎宇,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,440評論 2 348