如何在 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 个按钮创建登录和注销?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 RxSwift 中为整个应用程序中的单元格和按钮设置动画?

使用 RxSwift 链接登录操作

如何在 RxSwift 中过滤带有按钮标题的按钮点击?

将 3 个按钮的点击事件与 rxSwift 相结合

RxSwift - 如何限制缓冲区的时间跨度

Rxswift 更新 Tableview 单元格值