我目前正在学习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!
}
以下是我最终得到的结果。在这种情况下,视图模型实际上没有什么可做的,因为只有一行逻辑。
代码的基本结构与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
}