ios - RxSwift,将 Action 绑定(bind)到 UIButton 在 View 转换后停止生成事件

标签 ios swift mvvm action rx-swift

我正在尝试使用 RxSwift 实现 MVVM 登录屏幕,但遇到了一些困难。

我的登录屏幕(用户、密码、登录按钮)转换为相机 View 屏幕,应用程序会在其中检查相机权限,如果这些权限未获批准,则将用户注销并返回到登录屏幕。

我有一个 loginAction在我的 LoginViewModel返回 Action<Void, LoginResult> , 其中LoginResult Result<Bool, Error> 和我的 loginProvider服务返回 Observable<LoginResult> :

struct LoginViewModel {

let sceneCoordinator: SceneCoordinatorType
let loginProvider: LoginProviderType

var usernameText = Variable<String>("")
var passwordText = Variable<String>("")

var isValid: Observable<Bool> {
    return Observable.combineLatest(usernameText.asObservable(), passwordText.asObservable()) { username, password in
        username.count > 0 && password.count > 0
    }
}

init(loginProvider: LoginProvider, coordinator: SceneCoordinatorType) {
    self.loginProvider = loginProvider
    self.sceneCoordinator = coordinator
}

lazy var loginAction: Action<Void, LoginResult> = { (coordinator: SceneCoordinatorType, service: LoginProviderType, username: String, password: String) in
    return Action<Void, LoginResult>(enabledIf: self.isValid) { _ in
        return service.login(username: username, password: password)
            .observeOn(MainScheduler.instance)
            .do(onNext: { result in
                guard let loggedIn = result.value else { return }
                if loggedIn {
                    let cameraViewModel = CameraViewModel(coordinator: coordinator)
                    coordinator.transition(to: Scene.camera(cameraViewModel), type: .modal)
    }
}(self.sceneCoordinator, self.loginProvider, self.companyText.value, self.usernameText.value, self.passwordText.value)
}

一切正常,有效输入成功登录(loginProvider 向我的服务器发送请求,获取响应并相应地处理所有其他步骤)。

如果用户没有授予相机权限,我有一个 Observable为此在我的 CameraViewModel我绑定(bind)到我的 CameraViewController , 订阅并在需要时注销用户并使用 CocoaAction 将 View 弹出回登录屏幕弹出当前 View (使用场景协调器类)。

问题是,当我在转换回登录屏幕后尝试再次登录时,订阅 loginAction 发出的元素不接收任何元素。

这是 LoginViewController 的代码:

class LoginViewController: UIViewController, BindableType {

var viewModel: LoginViewModel!

private let disposeBag = DisposeBag()

private var loginAction: Action<Void, LoginResult>!

@IBOutlet weak var usernameTextField: UITextField!
@IBOutlet weak var passwordTextField: UITextField!
@IBOutlet weak var loginButton: UIButton!
@IBOutlet weak var loadingIndicator: UIActivityIndicatorView!


private var usernameObservable: Observable<String> {
    return usernameTextField.rx.text
        .throttle(0.5, scheduler: MainScheduler.instance)
        .map() { text in
            return text ?? ""
    }
}

private var passwordObservable: Observable<String> {
    return passwordTextField.rx.text
        .throttle(0.5, scheduler: MainScheduler.instance)
        .map() { text in
            return text ?? ""
    }
}

override func viewDidLoad() {
    super.viewDidLoad()

}

func bindViewModel() {

    usernameObservable
        .bind(to: viewModel.usernameText)
        .disposed(by: disposeBag)

    passwordObservable
        .debug()
        .bind(to: viewModel.passwordText)
        .disposed(by: disposeBag)

    loginAction = viewModel.loginAction

    loginAction.elements
        .subscribe(onNext: { [weak self] result in
            self?.loadingIndicator.stopAnimating()
            var message = ""
            switch result {
            case  .failure(.unknownError):
                message = "unknown error"
            case .failure(.wrongCredentials):
                message = "wrong credentials"
            case .failure(.serviceUnavailable):
                message = "service unavailable"
            case let .success(loggedIn):
                return
            }
            self?.errorMessage(message: message)
    }).disposed(by: disposeBag)

    loginButton.rx.tap
        .subscribe(onNext: { [unowned self] in
                self.loadingIndicator.startAnimating()
                self.loginAction.execute(Void())
            })
    .disposed(by: disposeBag)

    viewModel.isValid
        .bind(to: loginButton.rx.isEnabled)
        .disposed(by: disposeBag)
}

我可以看到点击登录按钮会产生点击事件,但是 Action 本身会停止调用。知道我错过了什么吗?

最佳答案

我不确定 ScreenCoordinator 是如何工作的,但我会从将 usernameObservable/passwordObservable 的创建移至 viewDidLoad() 开始,因为这个问题似乎与生命周期相关。此外,您可以添加更多的 .debug() 调用(带有参数以便能够在日志中区分它们)。

关于ios - RxSwift,将 Action 绑定(bind)到 UIButton 在 View 转换后停止生成事件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51363282/

相关文章:

ios - Array/NSMutableArray 的最大限制是多少?我没有在日志中获取完整的 JSON 数据

ios - objective-c 中的音频文件格式问题

swift - 在 Swift 中的同一个 VC 上点击按钮后,有没有办法显示以前隐藏在早期代码行中的 UIelement?

c# - 如何在 Avalonia MVVM 应用程序中使用 InputGesture 实现 MenuItem?

.net - 在 WPF MVVM 中绑定(bind)到没有代理对象的派生属性

ios - 如何在代码中更新Core Data数据库中的NSManagedObjects?

ios - 在 iOS 中强制使用 "On demand"VPN?

arrays - 检查我的数组是否包含字典

ios - 设置 TouchID "Enter Password"回退以开始编辑 UITextField

c# - Window.FindName 找不到我通过附加属性命名的边框元素