如何在 RxSwift 中使用 1 个按钮创建登录和注销?
Posted
技术标签:
【中文标题】如何在 RxSwift 中使用 1 个按钮创建登录和注销?【英文标题】:How can I create SignIn and SignOut with 1 button in RxSwift? 【发布时间】:2021-11-19 03:28:03 【问题描述】:这是我更改按钮文本的代码:
func transform(input: Input) -> Output
let tappedSigninCheck = input.signinTrigger
.scan(false) lastState, _ in
return !lastState
let singupButtonTitle = tappedSigninCheck.map
return $0 == true ? "Sign Out" : "Sign In"
return Output(signinButtonTitle: singupButtonTitle)
现在,当我单击登录时,按钮文本将更改为“退出”,我希望在单击退出时,退出将显示 1 个警报,当在警报中单击是时,按钮文本将更改为登录。 我的问题:每当我单击按钮时,按钮文本都会更改文本:(((
func bindViewModel()
let signoutTrigger = signinButton.rx.tap.flatMap
return Observable<Void>.create (observer) -> Disposable in
let alert = UIAlertController(title: "Are you sure to sign out?",
message: nil,
preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "No",
style: .cancel,
handler: nil))
alert.addAction(UIAlertAction(title: "Yes",
style: .destructive,
handler: _ in
observer.onNext(Void())
))
self.present(alert, animated: true)
return Disposables.create()
let input = ProfileViewModel.Input(signinTrigger: signinButton.rx.tap.asDriver(),
signoutTrigger: signoutTrigger.asDriver(onErrorJustReturn: Void()))
let output = profileViewModel.transform(input: input)
output
.signinButtonTitle
.drive(signinButton.rx.title)
.disposed(by: disposeBag)
【问题讨论】:
【参考方案1】:为此,您需要一个反馈循环,这需要Subject
。我建议你像下面那样做。另请注意,您忘记在警报中发出 completed
事件。:
class ViewModel
struct Input
let logButton: Observable<Void>
let okayButton: Observable<Void>
struct Output
let buttonTitle: Observable<String>
let displayAlert: Observable<Void>
private enum Action
case tapped
case okay
private enum State
case offline
case online
case check
func transform(_ input: Input) -> Output
let state = Observable.merge(
input.logButton.map(to: ViewModel.Action.tapped),
input.okayButton.map(to: ViewModel.Action.okay)
)
.scan(ViewModel.State.offline) state, action in
switch (state, action)
case (.offline, .tapped):
return .online
case (.online, .tapped):
return .check
case (.check, .okay):
return .offline
case (.check, .tapped):
return .check
default:
assert(false)
return state
.share()
let buttonTitle = state
.map $0 == .offline ? "Log In" : "Log Out"
let displayAlert = state
.filter $0 == .check
.map(to: ())
return Output(
buttonTitle: buttonTitle,
displayAlert: displayAlert
)
class ViewController: UIViewController
var button: UIButton!
var viewModoel: ViewModel!
let disposeBag = DisposeBag()
func bind()
let logout = PublishSubject<Void>()
let input = ViewModel.Input(
logButton: button.rx.tap.asObservable(),
okayButton: logout
)
let output = viewModoel.transform(input)
output.buttonTitle
.bind(to: button.rx.title(for: .normal))
.disposed(by: disposeBag)
output.displayAlert
.flatMap [unowned self] _ in
self.areYouSure()
.bind(to: logout)
.disposed(by: disposeBag)
func areYouSure() -> Observable<Void>
Observable.create [unowned self] observer in
let alert = UIAlertController(
title: "Are you sure to sign out?",
message: nil,
preferredStyle: .alert
)
alert.addAction(UIAlertAction(
title: "Yes", style: .destructive, handler: _ in
observer.onNext(())
observer.onCompleted()
))
alert.addAction(UIAlertAction(
title: "No", style: .default, handler: _ in
observer.onCompleted()
))
self.present(alert, animated: true)
return Disposables.create()
【讨论】:
非常感谢。我也尝试过枚举,但我写错了。我真的很期待你的回复。再次感谢您^^scan
运算符代表Moore style state machine。如果您使用Mealy machine,您可以只使用两种状态,但对于摩尔机器,您需要三种状态。以上是关于如何在 RxSwift 中使用 1 个按钮创建登录和注销?的主要内容,如果未能解决你的问题,请参考以下文章