如何通过Combine框架更优雅地根据输入字符串控制登录按钮的启用状态
更优雅的Combine管道实现方案
嘿,我懂你想要摆脱额外类变量和方法,让Combine数据流直接控制按钮状态的需求!你的当前实现其实有点绕,而且还存在一个小隐患——用filter的话,只有当两个输入都非空时才会触发后续操作,如果用户之后清空了其中一个输入,按钮会保持启用状态不会变回禁用,这显然不是我们想要的。
下面给你两种更简洁的实现方式:
方案一:直接绑定按钮状态(无需额外类变量)
如果你的loginEntered和passEntered只是用来判断按钮状态的,那其实完全不需要这两个变量,直接用map生成启用状态并绑定到按钮即可:
Publishers.CombineLatest(loginTxtf.textView.$text, passwordTxtf.textView.$text) // 将两个输入文本转换为按钮是否启用的Bool值 .map { loginText, passwordText in !loginText.isEmpty && !passwordText.isEmpty } // 直接绑定到proceed按钮的isEnabled属性 .assign(to: \.isEnabled, on: proceed) .store(in: &subscriptions)
这个实现的好处是:
- 数据流全程连贯,没有额外的中间方法或变量
- 任何输入变化都会实时更新按钮状态,包括清空文本的情况
- 代码简洁易读,一眼就能看出逻辑
方案二:保留类变量同时绑定按钮状态
如果你确实需要保留loginEntered和passEntered这两个@Published属性(比如其他地方要用到),可以用handleEvents在数据流中顺便更新它们:
Publishers.CombineLatest(loginTxtf.textView.$text, passwordTxtf.textView.$text) // 顺便更新你的两个类变量 .handleEvents(receiveOutput: { [weak self] loginText, passwordText in self?.loginEntered = loginText self?.passEntered = passwordText }) // 生成按钮启用状态 .map { !$0.isEmpty && !$1.isEmpty } // 绑定到按钮 .assign(to: \.isEnabled, on: proceed) .store(in: &subscriptions)
这样既满足了保留变量的需求,又省去了额外的setProceedEnableState()方法,整个逻辑都在Combine管道内完成,非常优雅。
内容的提问来源于stack exchange,提问作者Evgeniy Kleban
相关产品推荐
相关产品推荐

