传递数据 MVVM 和 RxSwift

Posted

技术标签:

【中文标题】传递数据 MVVM 和 RxSwift【英文标题】:Passing data MVVM and RxSwift 【发布时间】:2020-03-13 10:55:04 【问题描述】:

我目前正在学习 MVVM 和 RxSwift。在我的主视图控制器中,我有一张我想传入的图像。我成功地使用了 mvc 和 RxSwift,但是因为 MVVM 对我来说是新的。我不知道如何在 MVVM 中实现。这是我的代码。

// This is the work version MVC RxSwift
    class GProfilePickAlertVC: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate 

    private let selectedSubjectPhoto = PublishSubject<UIImage>()
    var selectedPhoto: Observable<UIImage> 
        return selectedSubjectPhoto.asObservable()
    

    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) 
        if let editedImage     = info[.editedImage] as? UIImage 
            self.selectedSubjectPhoto.onNext(editedImage)
            dismiss(animated: true, completion: nil)
        
        dismiss(animated: true, completion: nil)
    


    class BasicInfoViewController: UITableViewController 
    let profileImageView    = GProfileImage(img: #imageLiteral(resourceName: "ic-tab-profile"), renderingMode: .alwaysTemplate)

    @objc private func handlePickPhoto() 
        let pickPhotoVC = GProfilePickAlertVC()
        pickPhotoVC.selectedPhoto.subscribe(onNext:  image in
            self.profileImageView.clipsToBounds = true
            self.profileImageView.image = image
            ).disposed(by: disposeBag)
        pickPhotoVC.modalPresentationStyle = .overFullScreen
        pickPhotoVC.modalTransitionStyle = .crossDissolve
        present(pickPhotoVC, animated: true)
    


// This is my MVVM setup
    struct BasicInfoViewModel 

    let selectedImage: BehaviorRelay<UIImage>


    class BasicInfoViewController: UITableViewController 

    let disposeBag          = DisposeBag()

    var BasicInfoViewModel: BasicInfoViewModel!

【问题讨论】:

【参考方案1】:

这就是我最终的结果。在这种情况下,视图模型真的没什么可做的,因为只有一行逻辑。

代码的基本结构与您的 MVC 示例相同,只是我的代码使用 RxImagePickerDelegateProxy 而不是 GProfilePickAlertVC。前者是一个通用委托,它会根据需要由 Rx 系统自动附加到任何 UIImagePickerController。

imagePickerScene(on:modalPresentationStyle:modalTransitionStyle:) 函数负责显示和关闭图像选择器。它充当协调器。

castOrThrow(_:_:) 函数是处理强制转换的通用助手。你可以在任何地方使用它。

final class BasicInfoViewController: UIViewController 

    private let profileImageView = GProfileImage(img: #imageLiteral(resourceName: "ic-tab-profile"), renderingMode: .alwaysTemplate)
    private let disposeBag = DisposeBag()

    override func viewDidLoad() 
        super.viewDidLoad()

        let imagePicker = imagePickerScene(
            on: self, 
            modalPresentationStyle: .overFullScreen, 
            modalTransitionStyle: .crossDissolve
        )

        pickPhotoButton.rx.tap
            .flatMapLatest  Observable.create(imagePicker) 
            .compactMap  $0[.editedImage] as? UIImage 
            .bind  image in
                self.profileImageView.clipsToBounds = true
                self.profileImageView.image = image
            
            .disposed(by: disposeBag)
    


func imagePickerScene(on presenter: UIViewController, modalPresentationStyle: UIModalPresentationStyle? = nil, modalTransitionStyle: UIModalTransitionStyle? = nil) -> (_ observer: AnyObserver<[UIImagePickerController.InfoKey: AnyObject]>) -> Disposable 
    return  [weak presenter] observer in
        let controller = UIImagePickerController()
        if let presentationStyle = modalPresentationStyle 
            controller.modalPresentationStyle = presentationStyle
        
        if let transitionStyle = modalTransitionStyle 
            controller.modalTransitionStyle = transitionStyle
        
        presenter?.present(controller, animated: true)
        return controller.rx.didFinishPickingMediaWithInfo
            .do(onNext:  _ in
                presenter?.dismiss(animated: true)
            )
            .subscribe(observer)
    


final class RxImagePickerDelegateProxy: DelegateProxy<UIImagePickerController, UINavigationControllerDelegate & UIImagePickerControllerDelegate>, DelegateProxyType, UINavigationControllerDelegate & UIImagePickerControllerDelegate 

    static func currentDelegate(for object: UIImagePickerController) -> (UIImagePickerControllerDelegate & UINavigationControllerDelegate)? 
        return object.delegate
    

    static func setCurrentDelegate(_ delegate: (UIImagePickerControllerDelegate & UINavigationControllerDelegate)?, to object: UIImagePickerController) 
        object.delegate = delegate
    

    static func registerKnownImplementations() 
        self.register  RxImagePickerDelegateProxy(parentObject: $0, delegateProxy: RxImagePickerDelegateProxy.self) 
     


extension Reactive where Base: UIImagePickerController 

    var didFinishPickingMediaWithInfo: Observable<[UIImagePickerController.InfoKey: AnyObject]> 
        return RxImagePickerDelegateProxy.proxy(for: base)
            .methodInvoked(#selector(UIImagePickerControllerDelegate.imagePickerController(_:didFinishPickingMediaWithInfo:)))
            .map( (a) in
                return try castOrThrow(Dictionary<UIImagePickerController.InfoKey, AnyObject>.self, a[1])
            )
    


func castOrThrow<T>(_ resultType: T.Type, _ object: Any) throws -> T 
    guard let returnValue = object as? T else 
        throw RxCocoaError.castingError(object: object, targetType: resultType)
    

    return returnValue

【讨论】:

嘿,谢谢@DanielT。所以并不是所有的属性都需要在视图模型中创建,我想学习 MVVM 和 RxSwift。有什么推荐吗? 查看github.com/danielt1263/RxEarthquake 以获得良好的样本。另外,加入 slack 社区rxslack.herokuapp.com 并阅读整个#rxmvvm 频道。

以上是关于传递数据 MVVM 和 RxSwift的主要内容,如果未能解决你的问题,请参考以下文章

使用 rxSwift 中的 tableView 单元将数据从视图模型传递到视图控制器

使用委托从iOS中的ViewModel传递数据到UI的任何替代方法?

MVVM + RXSwift+ Coordinator 如何设置数据?

RxSwift(24)——MVVM双向绑定

Xamarin MVVM 将数据传递到其他视图

用于搜索屏幕的 MVVM 和 RxSwift