【问题标题】:Binding UITextField or button with viewModel使用 viewModel 绑定 UITextField 或按钮
【发布时间】:2018-07-15 06:48:52
【问题描述】:

我一直面临在 viewModel 中将 UITextField 或按钮与可观察对象绑定的问题。

class VM {
    var emailObservable: Observable<String?> = Observable.just("")
}

我的 viewModel 和控制器中有这个可观察到的电子邮件。当我尝试用它绑定我的文本字段时,它给了我错误

无法使用类型为“(to: Observable)”的参数列表调用“bind”。

但是当我用变量替换 observables 时,它工作正常。

有人可以帮我解决这个问题吗?我找到的答案主要包括在 viewModel 的 init 方法中传递 observable,但我不想在 init 方法中传递它。

这是我找到的绑定链接,但它是通过 init 方法。

How to bind rx_tap (UIButton) to ViewModel?

【问题讨论】:

  • 你能分享你的代码吗?
  • 嘿@pacification,我正在绑定这样的文本字段:emailTextfield.rx.text.asObservable().bind(to: viewModel.emailObservable).disposed(by: disposeBag)

标签: ios swift mvvm rx-swift


【解决方案1】:

代替

emailTextfield.rx.text.asObservable().bind(to: viewModel.emailObservable).disposed(by: disposeBag)

使用此代码

viewModel.emailObservable.bind(to: noteField.rx.text).disposed(by: disposeBag)

您可能想要双向绑定,请阅读更多相关信息here

【讨论】:

    【解决方案2】:

    我想你在找什么:

    final class ViewModel {
    
        private let bag = DisposeBag()
        let string = BehaviorSubject<String>(value: "")
    
        init() {
            string.asObservable().subscribe(onNext: { string in
                print(string)
            })
            .disposed(by: bag)
        }
    
    }
    
    final class ViewController: UIViewController {
    
        @IBOutlet weak var textField: UITextField!
    
        private let bag = DisposeBag()
        private var viewModel: ViewModel!
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            viewModel = ViewModel()
    
            textField.rx.text
                .orEmpty
                .bind(to: viewModel.string)
                .disposed(by: bag)
        }
    
    }
    

    注意,正如@MaximVolgin 提到的Variable is deprecated in RxSwift 4,因此您可以使用BehaviorSubject 或其他取决于您的方式。


    更新。

    仅使用 Observable 实现。

    final class ViewModel {
    
        private let bag = DisposeBag()
    
        var string = "" {
            didSet {
                print(string)
            }
        }
    
        init(stringObservable: Observable<String>) {
            stringObservable.subscribe(onNext: { string in
                self.string = string
            })
            .disposed(by: bag)
        }
    
    }
    
    final class ViewController: UIViewController {
    
        @IBOutlet weak var textField: UITextField!
    
        private let bag = DisposeBag()
        private var viewModel: ViewModel!
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            viewModel = ViewModel(stringObservable: textField.rx.text.orEmpty.asObservable())
        }
    
    }
    

    如您所见,您的解决方案可以使用Observable 来实现,而不是Variable 或任何类型的Subject。还应该提到的是,在大多数情况下,这不是最终逻辑(只是将 textField 或其他任何东西绑定到某个变量)。可以有一些验证、启用/禁用等逻辑。对于这种情况,RxSwift 提供Driver。关于在一个项目中使用 ObservableDriver 的差异的好例子也可以在 here (by RxSwift)中找到。

    【讨论】:

    • 嗨@pacification,感谢您的解决方案,但我已经阅读了许多博客以避免使用主题。所以我正在寻找 Observables 的解决方案。我可以使用主题并在视图模型中收听它,但我不想使用它。在示例中,我使用了变量,因为它不适用于可观察对象。因此,如果您可以为我提供可观察的解决方案,那就太好了,再次感谢您的宝贵时间。
    • 嗨@pacification。因此,在所有解决方案中,我只在控制器本身中找到视图模型的实例化,以便所有可观察对象都可以在 init 方法中传递。正如上面的评论,我说我不想在控制器中进行视图模型初始化。所以我想找到一种更好的方法来完成所有的绑定。
    • 感谢您的宝贵时间,我会进一步调查。再次感谢。
    【解决方案3】:

    方法.bind(to:)绑定到一个Observer,而不是Observable

    变量(在 RxSwift v4 中已弃用)是一个特殊用途的主题

    主题根据定义既是 Observer 又是 Observable

    这就是 .bind(to:) 在里面所做的 -

    public func bind<O: ObserverType>(to observer: O) -> Disposable where O.E == E {
        return self.subscribe(observer)
    }
    

    更新:

    如何避免在VM的.init()中传递observables:

    // inside VM:
    
    fileprivate let observableSwitch: BehaviorSubject<Observable<MyValue>>
    fileprivate let myValueObservable = observableSwitch.switchLatest()
    
    // instead of passing in init:
    
    public func switch(to observable: Observable<MyValue>) {
      self.observableSwitch.onNext(observable)
    }
    

    【讨论】:

    • 嗨@maxim-volgin 感谢您的回答。所以如果我不能那样绑定文本字段,你能告诉我一个更好的解决方法吗?在初始化方法中,视图模型在控制器内部实例化,而不是作为依赖项传递。我正在寻找更好的绑定方式。
    • 您可以使用 BehaviorRelay 代替 Variable
    • 好的,会调查的。您能否告诉我进行此类绑定的最佳方法,这将非常有帮助。再次感谢您的建议。
    • 你能提供更多的上下文吗?绑定只是语法糖,如果它们不可用,您不必使用它们 - 普通订阅非常好。就个人而言,我只对 RxDataSources 使用绑定。另外我不使用 MVVM,只使用单向数据流 - github.com/maxvol/RaspSwift
    • 嗨@maxim-volgin。所以我有一个带有文本字段的控制器,对于文本字段的每一次更改,我都想显示每个文本字段的验证状态。例如,如果文本字段有一些有效数据,则在文本字段的右视图中显示一些图像,否则显示其他无效状态的图像。所以为此我需要观察视图模型或演示者中每个文本字段的变化。所以为此我想知道如何将文本字段与视图模型或演示者绑定。
    【解决方案4】:

    在 ViewModel 类中取一个可变类型的主题:

    class ViewModel{
    
    //MARK: - local Variables
    var emailText = Variable<String?>("")
    }       
    

    现在在 viewController 类中创建 viewmodel 类的对象,并将这个 emailtext 变量绑定到 viewcontroller 中的 textfield。每当 textfield 文本发生变化时, viewmodel 的 emailText 就会获得价值。

    txtfield.rx.text
            .bindTo(viewModel.emailText).addDisposableTo(disposeBag)
    

    【讨论】:

    • 嗨@arnav。我在许多帖子中发现不要使用主题。那么你能告诉我一种将文本字段与可观察对象绑定的方法吗?我不希望通过 viewmodel 的 init 方法进行绑定(在我上面分享的链接中)。
    【解决方案5】:

    试试这个,

    override func viewDidLoad() {
            super.viewDidLoad()
            _ = userNameTextField.rx.text.map { $0 ?? "" }.bind(to: viewModel.userName)
        }
    

    在 viewModel 类中,

    class ViewModel{
       var userName: Variable<String> = Variable("")
    }
    

    【讨论】:

      猜你喜欢
      • 2017-12-27
      • 1970-01-01
      • 1970-01-01
      • 2020-02-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-21
      • 2011-09-17
      相关资源
      最近更新 更多