我同时是一个
Swift和ReactiveCocoa noob.使用MVVM和Reactive Cocoa v3.0-beta.4框架,我想实现此设置,以了解新RAC 3框架的基础知识.
我有一个文本字段,我希望文本输入包含超过3个字母,以进行验证.如果文本通过验证,则应启用下面的按钮.当按钮收到触地事件时,我想使用视图模型的属性触发操作.
由于目前关于RAC 3.0测试版的资源非常少,我通过阅读框架的Github repo上的QA来实现以下内容.这是我到目前为止所能提出的:
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.到现在为止还挺好.
我的问题是:在视图模型的操作中,我想使用其存储的文本属性并更新代码以使用它来发出网络请求.所以,我不需要视图控制器端的输入.我怎么能不要求我的Action属性输入?
解决方法
从
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.
我想你可以通过制作AnyObject来声明你不关心输入吗?并使用方便初始化器创建CocoaAction:
cocoaAction = CocoaAction(viewmodel.action)
补充说明
>我不喜欢使用AnyObject?而不是Bool for validatedTextProducer.我想你更喜欢它,因为绑定到buttonEnabled属性需要AnyObject?.我宁愿把它扔在那里,而不是牺牲我的视图模型的类型清晰度(见下面的例子).
>您可能希望在视图模型级别以及UI上限制Action的执行,例如:
class viewmodel { var text = MutableProperty<String>("") let action: Action<AnyObject?,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 } //... }