Bas*_*ian 5 key-value-observing reactive-cocoa swift reactive-cocoa-3
我正在研究ReactiveCocoa来改进我们的Swift代码.作为一个起点,我想将标签的文本绑定到属性的转换值.基本上,我想替换一些KVO代码.所以,我有以下变量:
@IBOutlet weak var myLabel: UILabel!
var myModel: MyModel
Run Code Online (Sandbox Code Playgroud)
由于我们的模型是在Objective-C中开发的,所以MyModel看起来像这样:
@interface MyModel : NSManagedObject
@property (nonatomic, retain) NSNumber * value;
@end
Run Code Online (Sandbox Code Playgroud)
因此,我想做这样的事情:
myLabel.text <~ myProperty.rac_signalForSelector("value")
|> map { (value: NSNumber) in
return "\(value.integerValue + 1)"
}
Run Code Online (Sandbox Code Playgroud)
但是,这显然不起作用.我该如何处理这个问题?KVO如何通常用正常的模型属性实现?
我已经找到了关于这个主题的类似帖子.接受的答案建议使用ViewModels.但是,我不想用ViewModels替换现有的NSManagedObject模型,而是希望保留我的模型,因为我喜欢通过XCode管理它们并通过CoreData存储它们.或者这对ViewModels来说是否可行?我错过了什么关键吗?
像这样的东西怎么样(使用 RAC 3.0.0 使用 Xcode 6.4 构建):
/// Send changes in the property value of a source object to another property on a target object,
/// optionally using a mapping function to convert values as necessary.
func bind<S, T>(propertyWithKey targetKey: String, on target: NSObject, toPropertyWithKey sourceKey: String, on source:NSObject, usingValueConversion sourceToTargetValueConversion: ((S) -> T)? ) {
var (source_signal, source_sink) = Signal<T, NSError>.pipe()
var sourceSignalProducer = RACObserve(source, sourceKey).toSignalProducer()
|> start(next: {value in
if (value != nil) {
if (sourceToTargetValueConversion == nil) {
sendNext(source_sink, value as! T)
} else {
let mappedValue = sourceToTargetValueConversion!(value as! S)
sendNext(source_sink, mappedValue)
}
}
})
var convertedValueRACSignal = toRACSignal(
source_signal
|> map {(value: T) in value as! AnyObject}
)
convertedValueRACSignal ~> RAC(target, targetKey)
}
/// Send changes in the property value of a source object to another property on a target object.
/// No conversion of values or value types is performed between source and target.
func bind(propertyWithKey targetKey: String, on target: NSObject, toPropertyWithKey sourceKey: String, on source:NSObject) {
var (source_signal, source_sink) = Signal<AnyObject, NSError>.pipe()
var sourceSignalProducer = RACObserve(source, sourceKey).toSignalProducer()
|> start(next: {value in
if (value != nil) {
sendNext(source_sink, value!)
}
})
var convertedValueRACSignal = toRACSignal(source_signal)
convertedValueRACSignal ~> RAC(target, targetKey)
}
// From Colin Eberhardt's post at http://blog.scottlogic.com/2014/07/24/mvvm-reactivecocoa-swift.html
// a struct that replaces the RAC macro
struct RAC {
var target : NSObject!
var keyPath : String!
var nilValue : AnyObject!
init(_ target: NSObject!, _ keyPath: String, nilValue: AnyObject? = nil) {
self.target = target
self.keyPath = keyPath
self.nilValue = nilValue
}
func assignSignal(signal : RACSignal) {
signal.setKeyPath(self.keyPath, onObject: self.target, nilValue: self.nilValue)
}
}
// From Colin Eberhardt's post at http://blog.scottlogic.com/2014/07/24/mvvm-reactivecocoa-swift.html
infix operator ~> {}
func ~> (signal: RACSignal, rac: RAC) {
rac.assignSignal(signal)
}
// From Colin Eberhardt's post at http://blog.scottlogic.com/2014/07/24/mvvm-reactivecocoa-swift.html
func RACObserve(target: NSObject!, keyPath: String) -> RACSignal {
return target.rac_valuesForKeyPath(keyPath, observer: target)
}
Run Code Online (Sandbox Code Playgroud)
在您的示例中,您可以将其称为:
bind(propertyWithKey: "text", on: myTextField, toPropertyWithKey: "value", on: myProperty)
{ (number: NSNumber) in
return "\(number.integerValue)" as NSString
}
Run Code Online (Sandbox Code Playgroud)
我对 ReactiveCocoa 有点陌生,所以上面的实现可能是一个幼稚的实现,但它可能会为您指明正确的方向。
更新
这种使用通道的实现bind更加紧凑,并利用了 RAC 内置的 KVO 支持:
func bind<S, T>(propertyWithKey targetKey: String, on target: NSObject, toPropertyWithKey sourceKey: String, on source:NSObject, usingValueConversion sourceToTargetValueConversion: ((S) -> T)? ) {
var kvoChannelSource = RACKVOChannel(target: source, keyPath: sourceKey, nilValue: nil)
var sourceSignalWithoutNils = kvoChannelSource.followingTerminal
.filter { (var value:AnyObject?) -> Bool in
return (value != nil)
}
var mappedSourceSignal = sourceSignalWithoutNils
if (sourceToTargetValueConversion != nil)
{
mappedSourceSignal = sourceSignalWithoutNils.map { (var s: AnyObject!) -> AnyObject! in
var result : T = sourceToTargetValueConversion!(s as! S)
return result as! AnyObject
}
}
var kvoChannelTarget = RACKVOChannel(target: target, keyPath: targetKey, nilValue: nil)
mappedSourceSignal.subscribe(kvoChannelTarget.followingTerminal)
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
338 次 |
| 最近记录: |