如何使用ReactiveCocoa 3实现基本的UITextField输入+ UIButton操作场景? [英] How to implement a basic UITextField input + UIButton action scenario using ReactiveCocoa 3?
问题描述
我同时是Swift和ReactiveCocoa noob。使用MVVM和Reactive Cocoa v3.0-beta.4框架,我想实现此设置,以了解新RAC 3框架的基础知识。
I'm a Swift and ReactiveCocoa noob at the same time. Using MVVM and Reactive Cocoa v3.0-beta.4 framework, I'd like to implement this setup, to learn the basics of the new RAC 3 framework.
I有一个文本字段,我希望文本输入包含3个以上的字母,以进行验证。如果文本通过验证,则应启用下面的按钮。当按钮收到触地事件时,我想使用视图模型的属性触发一个动作。
I have a text field and I want the text input to contain more than 3 letters, for validation. If the text passes the validation, the button underneath should be enabled. When the button receives the touch down event, I want to trigger an action using the view model's property.
由于目前RAC 3.0测试版的资源非常少,我通过阅读框架的Github repo上的QAs来实现以下内容。以下是我到目前为止所能提出的建议:
Since there are very few resources about RAC 3.0 beta at the moment, I implemented the following by reading the QAs on the framework's Github repo. Here's what I could come up with so far:
ViewModel.swift
class ViewModel {
var text = MutableProperty<String>("")
let action: Action<String, Bool, NoError>
let validatedTextProducer: SignalProducer<AnyObject?, NoError>
init() {
let validation: Signal<String, NoError> -> Signal<AnyObject?, NoError> = map ({
string in
return (count(string) > 3) as AnyObject?
})
validatedTextProducer = text.producer.lift(validation)
//Dummy action for now. Will make a network request using the text property in the real app.
action = Action { _ in
return SignalProducer { sink, disposable in
sendNext(sink, true)
sendCompleted(sink)
}
}
}
}
ViewController.swift
class ViewController: UIViewController {
private lazy var txtField: UITextField = {
return createTextFieldAsSubviewOfView(self.view)
}()
private lazy var button: UIButton = {
return createButtonAsSubviewOfView(self.view)
}()
private lazy var buttonEnabled: DynamicProperty = {
return DynamicProperty(object: self.button, keyPath: "enabled")
}()
private let viewModel = ViewModel()
private var cocoaAction: CocoaAction?
override func viewDidLoad() {
super.viewDidLoad()
view.setNeedsUpdateConstraints()
bindSignals()
}
func bindSignals() {
viewModel.text <~ textSignal(txtField)
buttonEnabled <~ viewModel.validatedTextProducer
cocoaAction = CocoaAction(viewModel.action, input:"Actually I don't need any input.")
button.addTarget(cocoaAction, action: CocoaAction.selector, forControlEvents: UIControlEvents.TouchDown)
viewModel.action.values.observe(next: {value in
println("view model action result \(value)")
})
}
override func updateViewConstraints() {
super.updateViewConstraints()
//Some autolayout code here
}
}
RACUtilities.swift
func textSignal(textField: UITextField) -> SignalProducer<String, NoError> {
return textField.rac_textSignal().toSignalProducer()
|> map { $0! as! String }
|> catch {_ in SignalProducer(value: "") }
}
使用此设置,当视图模型的文本超过3个字符时,该按钮将启用。当用户点击按钮时,视图模型的动作运行,我可以将返回值设置为true。到目前为止一直很好。
With this setup, the button gets enabled when the view model's text is longer than 3 characters. When the user taps on the button, the view model's action runs and I can get the return value as true. So far so good.
我的问题是:在视图模型的操作中,我想使用其存储的文本属性并更新代码以使用它来发出网络请求。所以,我不需要视图控制器端的输入。我如何不需要输入我的Action属性?
My question is: Inside the view model's action, I want to use its stored text property and update the code to make a network request using it. So, I don't need an input from the view controller's side. How can I not require an input for my Action property?
推荐答案
来自 ReactiveCocoa / CHANGELOG.md :
一个动作必须指出它接受的输入类型,它产生的输出类型,以及可能发生的错误类型(如果有的话)。
An action must indicate the type of input it accepts, the type of output it produces, and what kinds of errors can occur (if any).
因此,目前无法在没有输入的情况下定义 Action
。
So currently there is no way to define an Action
without an input.
我想你可以声明你不关心输入 AnyObject?并创建 CocoaAction
带方便初始值:
I suppose you could declare that you don't care about input by making it AnyObject?
and creating CocoaAction
with convenience initialiser:
cocoaAction = CocoaAction(viewModel.action)
补充说明
-
我不喜欢使用
AnyObject?
代替Bool
forvalidatedTextProducer
。我想你更喜欢它,因为绑定到buttonEnabled
属性需要AnyObject?
。我宁愿把它放在那里,而不是牺牲我的视图模型的类型清晰度(见下面的例子)。Additional remarks
I dont't like using
AnyObject?
instead ofBool
forvalidatedTextProducer
. I suppose you preferred it because binding to thebuttonEnabled
property requiresAnyObject?
. I would rather cast it there though, instead of sacrificing type clarity of my view model (see example below).你可能想要限制执行<视图模型级别以及UI上的code>操作,例如:
You might want to restrict execution of the
Action
on the view model level as well as UI, e.g.:class ViewModel { var text = MutableProperty<String>("") let action: Action<AnyObject?, Bool, NoError> // if you want to provide outside access to the property var textValid: PropertyOf<Bool> { return PropertyOf(_textValid) } private let _textValid = MutableProperty(false) init() { let validation: Signal<String, NoError> -> Signal<Bool, NoError> = map { string in return count(string) > 3 } _textValid <~ text.producer |> validation action = Action(enabledIf:_textValid) { _ in //... } } }
绑定到
buttonEnabled
:func bindSignals() { buttonEnabled <~ viewModel.action.enabled.producer |> map { $0 as AnyObject } //... }
这篇关于如何使用ReactiveCocoa 3实现基本的UITextField输入+ UIButton操作场景?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!