我正在使用ReactiveCocoa更新UILabel
而UIProgressView
倒计时:
NSInteger percentRemaining = ...;
self.progressView.progress = percentRemaining / 100.0;
__block NSInteger count = [self.count];
[[[RACSignal interval:0.05 onScheduler:[RACScheduler mainThreadScheduler]]
take: percentRemaining]
subscribeNext:^(id x) {
count++;
self.countLabel.text = [NSString stringWithFormat:@"%d", count];
self.progressView.progress = self.progressView.progress - 0.01;
} completed:^{
// Move along...
}];
这很好用但是,我对count
变量或读取self.progressView.progress
的值不是特别满意,以便减少它。
我觉得我应该能够使用RAC
宏直接发出信号并绑定属性。类似的东西:
RACSignal *baseSignal = [[RACSignal interval:0.05 onScheduler:[RACScheduler mainThreadScheduler]]
take: percentRemaining]
RAC(self, countLabel.text) = [baseSignal
map: ...
...
RAC(self, progressView.progress) = [baseSignal
map: ...
...
...
显示我被卡住的地方。我无法理解如何编写RACSignal
这样我不需要依赖状态变量。
此外,我不确定在流完成时我需要注入// Move along...
副作用的位置/方式。
一旦你想到了正确的方法,我确信两者都很简单,但是,任何帮助都会非常感激。
答案 0 :(得分:38)
如有疑问,请查看 RACSignal+Operations.h 和 RACStream.h, 因为你想要做的事情肯定是一个运营商。在这种情况下, 基本缺失的部分是 -scanWithStart:reduce:
首先,让我们看一下baseSignal
。逻辑将保持不变
基本相同,只是我们应该发布a
connection
为它:
RACMulticastConnection *timer = [[[RACSignal
interval:0.05 onScheduler:[RACScheduler mainThreadScheduler]]
take:percentRemaining]
publish];
这样我们就可以在所有依赖项之间共享一个计时器
信号。虽然您提供的baseSignal
也可以使用,但这样做
为每个订户重新创建一个计时器(包括相关信号),这可能是
导致他们射击的微小差异。
现在,我们可以使用-scanWithStart:reduce:
来增加countLabel
和递减progressView
。此运算符采用以前的结果和
当前值,让我们根据需要转换或组合它们。
在我们的例子中,我们只想忽略当前值(发送的NSDate
通过+interval:
),我们可以操纵前一个:
RAC(self.countLabel, text) = [[[timer.signal
scanWithStart:@0 reduce:^(NSNumber *previous, id _) {
return @(previous.unsignedIntegerValue + 1);
}]
startWith:@0]
map:^(NSNumber *count) {
return count.stringValue;
}];
RAC(self.progressView, progress) = [[[timer.signal
scanWithStart:@(percentRemaining) reduce:^(NSNumber *previous, id _) {
return @(previous.unsignedIntegerValue - 1);
}]
startWith:@(percentRemaining)]
map:^(NSNumber *percent) {
return @(percent.unsignedIntegerValue / 100.0);
}];
上面的-startWith:
运算符可能看起来多余,但确实如此
确保在text
之前设置progress
和timer.signal
所必需的
送了什么。
然后,我们将使用正常订阅完成。这完全是 可能这些副作用也可以变成信号,但它是 很难知道没有看到代码:
[timer.signal subscribeCompleted:^{
// Move along...
}];
最后,因为我们上面使用了RACMulticastConnection
,所以实际上什么都没有
火了。必须手动启动连接:
[timer connect];
这将连接上述订阅的所有,并启动计时器,因此 值开始流向属性。
现在,这显然是比命令式等价物更多的代码,所以有人可能 问为什么这是值得的。有几个好处:
基本上,这是命令式与函数式编程的典型例子。
虽然命令性代码可以从较不复杂的开始,但它的复杂性会增加 呈指数。功能代码(尤其是功能性反应代码)可以 从更复杂的开始,然后它的复杂性增长线性 - 它是很多 随着应用程序的增长,更容易管理。