gpt4 book ai didi

ios - RxCocoa - 当出现延迟时防止多个 View Controller 推送

转载 作者:行者123 更新时间:2023-11-29 00:12:11 26 4
gpt4 key购买 nike

就像响应式和非响应式 iOS 项目一样,如果你有一个 UI 元素(例如,一个按钮或一个被选中的表格 View 单元格)将 View Controller 推送到导航堆栈上,如果有由于某种原因(尤其是在较旧的设备上)而出现延迟,重复点击可能会导致重复推送,从而导致糟糕的用户体验。

通常,您可以在第一次点击后禁用该元素。

例如:

@IBAction func myButtonTap() { 
button.isEnabled = false
doTheRestOfTheAction()
}

我对 RxSwift 比较陌生。我正在尝试找出一种适当的响应式方法来实现此目的,以修复我的应用程序中重复推送 View 的一些错误。

一些想法:

可以使用debouncethrottle,但看起来像是创可贴,不一定能解决所有情况。

我目前认为最好的方法是在预期事件发生后处理订阅。

let disposable = tableView.rx.itemSelected
.subscribe(onNext: { [weak self] indexPath in
self?.performSegue(withIdentifier: "MySegueIdentifier", sender: self)
})

...

func prepareForSegue() {
myDisposable.dispose()
finishPrepareForSegue()
}

尽管如果您想在订阅 block 内取消订阅,编译器会提示在其自身初始值内使用变量,这是有道理的。我想有解决方法,但我想知道是否有更好的方法?也许我缺少一个响应式(Reactive)运算符(operator)?

尝试搜索类似的示例,但结果有限。

谢谢

编辑:也许是 takeUntil 运算符?

Possibly relevant SO question .

最佳答案

我在公司经常看到的一件事是使用 Rx Variable类似于 loginInFlight这是 Variable<boolean> .默认为 false,当运行命令登录时,我们将其设置为 true。该 bool 值还与登录按钮相关联,因此一旦用户单击登录,任何后续单击都不会执行任何操作。只要用户可以单击某些内容来更改屏幕,您就可以实现此功能,以确保没有正在进行的调用/事件。

我们遵循 MVVM,因此这里有一个基于它的示例。我试图只显示其下方的准系统,因此希望下面的一切仍然有意义。

登录 View Controller

class LoginViewController: UIViewController {
@IBOutlet weak var signInButton: UIButton!

override func viewDidLoad() {
super.viewDidLoad()

...

// This commandAvailable is what I was talking about above
viewModel?
.loginCommandAvailable
.subscribe(onNext: {[unowned self] (available: Bool) in
self.signInButton.isEnabled = available
})
.addDisposableTo(disposeBag)

signInButton.rx.tap
.map {
// Send Login Command
return viewModel?.loginCommand()
}.subscribe(onNext: { (result: LoginResult)
// If result was successful we can send the user to the next screen
}).addDisposableTo(disposeBag)
}
}

登录 View 模型

enum LoginResult: Error {
case success
case failure
}

class LoginViewModel {
private let loginInFlight = Variable<Bool>(false)

private var emailAddressProperty = Variable<String>("")
var emailAddress: Driver<String> {
return emailAddressProperty
.asObservable()
.subscribeOn(ConcurrentDispatchQueueScheduler(queue: DispatchQueue.global()))
.asDriver(onErrorJustReturn: "")
}

...

var loginCommandAvailable: Observable<Bool> {
// We let the user login if login is not currently happening AND the user entered their email address
return Observable.combineLatest(emailAddressProperty.asObservable(), passwordProperty.asObservable(), loginInFlight.asObservable()) {
(emailAddress: String, password: String, loginInFlight: Bool) in
return !emailAddress.isEmpty && !password.isEmpty && !loginInFlight
}
}

func loginCommand() -> Driver<LoginResult> {
loginInFlight.value = true

// Make call to login
return authenticationService.login(email: emailAddressProperty.value, password: passwordProperty.value)
.map { result -> LoginResult in
loginInFlight.value = false
return LoginResult.success
}
}
}

根据可用性编辑切换命令

登录 View Controller

class LoginViewController: UIViewController {
@IBOutlet weak var signInButton: UIButton!

override func viewDidLoad() {
super.viewDidLoad()

...

// This commandAvailable is what I was talking about above
viewModel?
.loginCommandAvailable
.subscribe(onNext: {[unowned self] (available: Bool) in
self.signInButton.isEnabled = available
})
.addDisposableTo(disposeBag)

signInButton.rx.tap
.map {
return viewModel?.loginCommandAvailable
}.flatmap { (available: Bool) -> Observable<LoginResult>
// Send Login Command if available
if (available) {
return viewModel?.loginCommand()
}
}.subscribe(onNext: { (result: LoginResult)
// If result was successful we can send the user to the next screen
}).addDisposableTo(disposeBag)
}
}

关于ios - RxCocoa - 当出现延迟时防止多个 View Controller 推送,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46163965/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com