如何在 RxSwift 中设置属性并从 UIAlertController 获取回调?
How to set properties and get callback from UIAlertController in RxSwift?
我有一个 ViewModelType 来绑定 UIViewController 和 ViewModel。
import Foundation
protocol ViewModelType {
associatedtype Input
associatedtype Output
func transform(input: Input) -> Output
}
HomeViewModel 符合 ViewModelType 并定义所需的输入和输出,然后根据输入执行返回输出的工作。
为简单起见,我删除了存储库并始终返回 syncData 任务的失败。
import Foundation
import RxSwift
import RxCocoa
class HomeViewModel: ViewModelType {
struct Input {
let syncData: Driver<Void>
}
struct Output {
let message: Driver<String>
}
func transform(input: Input) -> Output {
let fetching = input.syncData.flatMapLatest { _ -> Driver<String> in
return Observable<String>.from(optional: "Choose below options to proceed") // This message will be returned by server.
.delay(.seconds(1), scheduler: MainScheduler.instance)
.asDriverOnErrorJustComplete()
}
return Output(message: fetching)
}
}
我有一个带字符串的警报活页夹。
UIAlertController
有一个重试按钮,点击重试按钮我想从 HomeViewModel
的 Input
调用 syncData
我该怎么做?
import UIKit
import RxSwift
import RxCocoa
class HomeViewController: UIViewController {
private let disposeBag = DisposeBag()
var viewModel = HomeViewModel()
override func viewDidLoad() {
super.viewDidLoad()
let viewDidAppear = rx.sentMessage(#selector(UIViewController.viewDidAppear(_:)))
.mapToVoid()
.asDriverOnErrorJustComplete()
// How to merge viewWillAppear & alert for callback of retry button?
let input = HomeViewModel.Input(syncData: viewDidAppear)
let output = viewModel.transform(input: input)
output.message.drive(alert)
.disposed(by: disposeBag)
}
var alert: Binder<String> {
return Binder(self) { (vc, message) in
let alert = UIAlertController(title: "Sync failed!",
message: message,
preferredStyle: .alert)
let okay = UIAlertAction(title: "Retry", style: .default, handler: { _ in
// how to call syncData of Input?
})
let dismiss = UIAlertAction(title: "Dismiss",
style: UIAlertAction.Style.cancel,
handler: nil)
alert.addAction(okay)
alert.addAction(dismiss)
vc.present(alert, animated: true, completion: nil)
}
}
}
首先,您应该使用一些 Coordinator 来调用 push/present 控制器。
并制作代表警报的功能。
例如:
class Router {
private let rootViewController: UIViewController
let retryAction = PublishSubject<Void>()
func showGalleryAlert() {
let alert = UIAlertController(title: "Your Title", message: "Your Message", preferredStyle: .alert)
let settings = UIAlertAction(title: "Name Action", style: .default) { _ in
// send here your action to PublishSubject
self.retryAction.send()
}
let cancel = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
alert.addAction(cancel)
alert.addAction(settings)
rootViewController.present(alert, animated: true)
}}
您需要将此路由器注入您的 ViewModel 并收听此 PublishSubject。
或者你可以使用Single/Maybe函数,这里是一个小例子如何使用它:
public func openList() -> Maybe<Void> {
return .create { observer -> Disposable in
let alert = UIAlertController(title: "Your Title", message: "YourMessage", preferredStyle: .alert)
let settings = UIAlertAction(title: "Name Action", style: .default) { _ in
// send here your action
observer.send()
}
let cancel = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
alert.addAction(cancel)
alert.addAction(settings)
rootViewController.present(alert, animated: true)
return Disposables.create {
DispatchQueue.main.async {
vc.dismiss(animated: true, completion: nil)
}
}
}
}
并通过 ViewModel 处理。
P.S: 您应该在 ViewModel 中使用输入主题,而不是驱动程序。驱动程序 必须 仅用于输出等视图。
有两种情况你应该使用 Subjects,当你将 non-RxCode 转换成 RxCode 时(比如使 UIAlertAction 响应式),当你必须创建一个循环时(比如提供视图的输出模型返回到它自己的输入。)
如果您发现自己经常这样做,那么您可能要考虑制作多个视图模型。
您还会注意到,我创建了警报它自己的、独立的协调器函数。这样它就可以在多个地方使用。如果需要,您可以对传递给 flatMapFirst
的闭包执行相同的操作。
class HomeViewModel {
struct Input {
let syncData: Observable<Void>
let retry: Observable<Void>
}
struct Output {
let message: Observable<String>
}
func transform(input: Input) -> Output {
let fetching = Observable.merge(input.syncData, input.retry)
.flatMapLatest { _ -> Observable<String> in
return Observable<String>.from(optional: "Choose below options to proceed") // This message will be returned by server.
.delay(.seconds(1), scheduler: MainScheduler.instance)
}
return Output(message: fetching)
}
}
class HomeViewController: UIViewController {
private let disposeBag = DisposeBag()
var viewModel = HomeViewModel()
override func viewDidLoad() {
super.viewDidLoad()
let viewDidAppear = rx.sentMessage(#selector(UIViewController.viewDidAppear(_:)))
.mapToVoid()
let retry = PublishSubject<Void>()
let input = HomeViewModel.Input(syncData: viewDidAppear, retry: retry.asObservable())
let output = viewModel.transform(input: input)
output.message
.flatMapFirst { [weak self] (message) -> Observable<Void> in
let (alert, trigger) = createAlert(message: message)
self?.present(alert, animated: true)
return trigger
}
.subscribe(retry)
.disposed(by: disposeBag)
}
}
func createAlert(message: String) -> (UIViewController, Observable<Void>) {
let trigger = PublishSubject<Void>()
let alert = UIAlertController(title: "Sync failed!",
message: message,
preferredStyle: .alert)
let okay = UIAlertAction(title: "Retry", style: .default, handler: { _ in
trigger.onNext(())
trigger.onCompleted()
})
let dismiss = UIAlertAction(title: "Dismiss",
style: UIAlertAction.Style.cancel,
handler: nil)
alert.addAction(okay)
alert.addAction(dismiss)
return (alert, trigger)
}
我有一个 ViewModelType 来绑定 UIViewController 和 ViewModel。
import Foundation
protocol ViewModelType {
associatedtype Input
associatedtype Output
func transform(input: Input) -> Output
}
HomeViewModel 符合 ViewModelType 并定义所需的输入和输出,然后根据输入执行返回输出的工作。
为简单起见,我删除了存储库并始终返回 syncData 任务的失败。
import Foundation
import RxSwift
import RxCocoa
class HomeViewModel: ViewModelType {
struct Input {
let syncData: Driver<Void>
}
struct Output {
let message: Driver<String>
}
func transform(input: Input) -> Output {
let fetching = input.syncData.flatMapLatest { _ -> Driver<String> in
return Observable<String>.from(optional: "Choose below options to proceed") // This message will be returned by server.
.delay(.seconds(1), scheduler: MainScheduler.instance)
.asDriverOnErrorJustComplete()
}
return Output(message: fetching)
}
}
我有一个带字符串的警报活页夹。
UIAlertController
有一个重试按钮,点击重试按钮我想从 HomeViewModel
的 Input
调用 syncData
我该怎么做?
import UIKit
import RxSwift
import RxCocoa
class HomeViewController: UIViewController {
private let disposeBag = DisposeBag()
var viewModel = HomeViewModel()
override func viewDidLoad() {
super.viewDidLoad()
let viewDidAppear = rx.sentMessage(#selector(UIViewController.viewDidAppear(_:)))
.mapToVoid()
.asDriverOnErrorJustComplete()
// How to merge viewWillAppear & alert for callback of retry button?
let input = HomeViewModel.Input(syncData: viewDidAppear)
let output = viewModel.transform(input: input)
output.message.drive(alert)
.disposed(by: disposeBag)
}
var alert: Binder<String> {
return Binder(self) { (vc, message) in
let alert = UIAlertController(title: "Sync failed!",
message: message,
preferredStyle: .alert)
let okay = UIAlertAction(title: "Retry", style: .default, handler: { _ in
// how to call syncData of Input?
})
let dismiss = UIAlertAction(title: "Dismiss",
style: UIAlertAction.Style.cancel,
handler: nil)
alert.addAction(okay)
alert.addAction(dismiss)
vc.present(alert, animated: true, completion: nil)
}
}
}
首先,您应该使用一些 Coordinator 来调用 push/present 控制器。 并制作代表警报的功能。
例如:
class Router {
private let rootViewController: UIViewController
let retryAction = PublishSubject<Void>()
func showGalleryAlert() {
let alert = UIAlertController(title: "Your Title", message: "Your Message", preferredStyle: .alert)
let settings = UIAlertAction(title: "Name Action", style: .default) { _ in
// send here your action to PublishSubject
self.retryAction.send()
}
let cancel = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
alert.addAction(cancel)
alert.addAction(settings)
rootViewController.present(alert, animated: true)
}}
您需要将此路由器注入您的 ViewModel 并收听此 PublishSubject。
或者你可以使用Single/Maybe函数,这里是一个小例子如何使用它:
public func openList() -> Maybe<Void> {
return .create { observer -> Disposable in
let alert = UIAlertController(title: "Your Title", message: "YourMessage", preferredStyle: .alert)
let settings = UIAlertAction(title: "Name Action", style: .default) { _ in
// send here your action
observer.send()
}
let cancel = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
alert.addAction(cancel)
alert.addAction(settings)
rootViewController.present(alert, animated: true)
return Disposables.create {
DispatchQueue.main.async {
vc.dismiss(animated: true, completion: nil)
}
}
}
}
并通过 ViewModel 处理。
P.S: 您应该在 ViewModel 中使用输入主题,而不是驱动程序。驱动程序 必须 仅用于输出等视图。
有两种情况你应该使用 Subjects,当你将 non-RxCode 转换成 RxCode 时(比如使 UIAlertAction 响应式),当你必须创建一个循环时(比如提供视图的输出模型返回到它自己的输入。)
如果您发现自己经常这样做,那么您可能要考虑制作多个视图模型。
您还会注意到,我创建了警报它自己的、独立的协调器函数。这样它就可以在多个地方使用。如果需要,您可以对传递给 flatMapFirst
的闭包执行相同的操作。
class HomeViewModel {
struct Input {
let syncData: Observable<Void>
let retry: Observable<Void>
}
struct Output {
let message: Observable<String>
}
func transform(input: Input) -> Output {
let fetching = Observable.merge(input.syncData, input.retry)
.flatMapLatest { _ -> Observable<String> in
return Observable<String>.from(optional: "Choose below options to proceed") // This message will be returned by server.
.delay(.seconds(1), scheduler: MainScheduler.instance)
}
return Output(message: fetching)
}
}
class HomeViewController: UIViewController {
private let disposeBag = DisposeBag()
var viewModel = HomeViewModel()
override func viewDidLoad() {
super.viewDidLoad()
let viewDidAppear = rx.sentMessage(#selector(UIViewController.viewDidAppear(_:)))
.mapToVoid()
let retry = PublishSubject<Void>()
let input = HomeViewModel.Input(syncData: viewDidAppear, retry: retry.asObservable())
let output = viewModel.transform(input: input)
output.message
.flatMapFirst { [weak self] (message) -> Observable<Void> in
let (alert, trigger) = createAlert(message: message)
self?.present(alert, animated: true)
return trigger
}
.subscribe(retry)
.disposed(by: disposeBag)
}
}
func createAlert(message: String) -> (UIViewController, Observable<Void>) {
let trigger = PublishSubject<Void>()
let alert = UIAlertController(title: "Sync failed!",
message: message,
preferredStyle: .alert)
let okay = UIAlertAction(title: "Retry", style: .default, handler: { _ in
trigger.onNext(())
trigger.onCompleted()
})
let dismiss = UIAlertAction(title: "Dismiss",
style: UIAlertAction.Style.cancel,
handler: nil)
alert.addAction(okay)
alert.addAction(dismiss)
return (alert, trigger)
}