插入动画和绑定

时间:2014-03-11 14:49:01

标签: ios objective-c reactive-cocoa

注意:我使用ReactiveCocoaLayout进行基于信号的动画。

我有一个UILabel,我想绑定到视图模型上的NSString *属性。

RACSignal* statusSignal = [RACObserve(self, viewModel.status) distinctUntilChanged];

足够简单。但是,现在我想添加一些奇特的动画。当status发生变化时,我想要连续发生的事情:

  1. 淡出标签(alpha从1 - > 0)
  2. 将新文本应用于UILabel
  3. 淡化标签(alpha中的1 - > 0)
  4. 这是我迄今为止能够提出的:

    RACSignal* statusSignal = [RACObserve(self, viewModel.status) distinctUntilChanged];
    
    // An animation signal that initially moves from (current) -> 1 and moves from (current) -> 0 -> 1 after that
    RACSignal* alphaValues = [[statusSignal flattenMap:^RACStream *(id _) {
    
        // An animation signal that moves towards a value of 1
        return [[[RACSignal return:@1]
                 delay:animationDuration]
                animateWithDuration:animationDuration];
    
    }] takeUntilReplacement:[[statusSignal skip:1] flattenMap:^RACStream *(id _) {
    
        // An animation signal that moves towards a value of 0, waits for that to complete, then moves towards a value of 1
        return [[[RACSignal return:@(0)]
                 animateWithDuration:animationDuration]
                concat:[[[RACSignal return:@1]
                         delay:animationDuration]
                        animateWithDuration:animationDuration]];
    }]];
    
    RAC(self, statusLabel.alpha) = alphaValues;
    
    // The initial status should be applied immediately.  Combined with the initial animation logic above, this will nicely fade in the first
    // status.  Subsequent status changes are delayed by [animationDuration] in order to allow the "fade" animation (alpha from 1 -> 0) to
    // finish before the text is changed.
    RAC(self, statusLabel.text) = [[statusSignal take:1]
                                   concat:[[statusSignal delay:animationDuration]
                                           deliverOn:[RACScheduler mainThreadScheduler]]];
    

    这很有效,但我不能感觉它有点...... 工程。大部分复杂性来自我的基本情况 - 初始文本应该淡入,随后的文本更改应淡出然后淡入。

    有关如何简化或优化的任何想法?

2 个答案:

答案 0 :(得分:1)

我还没有使用过RCL,所以如果我错误地使用了-animateWithDuration:,请告诉我。

首先,我定义了一个发送@YES而不是第一个状态的信号,以及@NO而不是所有后续状态。这是通过利用-bind:运算符来完成的,该运算符允许通过块捕获自定义每用户变量。

RACSignal *isFirstStatus = [statusSignal bind:^{
    __block BOOL first = YES;
    return ^(id _, BOOL *stop) {
        BOOL isFirst = first;
        first = NO;
        return [RACSignal return:@(isFirst)];
    };
}];

接下来,将isFirstStatus+if:then:else:放在一起以开始初始动画信号,然后切换到永久动画信号。

RAC(self, statusLabel.alpha) = [[RACSignal
    if:isFirstStatus
        // Initially, an animation signal to 1.
        then:[RACSignal return:@1]
        // Subsequently, an animation signal to 0, then, to 1.
        else:[[[RACSignal return:@1] delay:animationDuration] startWith:@0]]
    animateWithDuration:animationDuration];

我已经找到了一种通过完成动画来处理带内文本属性更新的方法,但没有找到我喜欢的内容。如果还没有一种好的方法,这可能是为这种类型的场景添加运算符到RCL的机会。

答案 1 :(得分:0)

以类似的方式实施解决方案。

[RACObserve(self, curChannel) subscribeNext:^(NSNumber* v) {
    BGChannel* ch = self.model.subscriptions[v.intValue];
    [UIView transitionWithView:self.channelLabel duration:0.75 options:UIViewAnimationOptionTransitionCrossDissolve animations:^{
        self.channelLabel.text = ch.name;
    } completion:nil];
}];