【问题标题】:Interpolate animations and bindings插入动画和绑定
【发布时间】:2014-03-11 14:49:00
【问题描述】:

注意:我将ReactiveCocoaLayout 用于基于信号的动画。

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

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

足够简单。但是,现在我想添加一些精美的动画。当status 发生变化时,我想连续发生什么:

  1. 淡出标签(alpha 从 1 -> 0)
  2. 将新文本应用到 UILabel
  3. 将标签淡入(alpha from 1 -> 0)

这是我目前所能想到的:

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]]];

这行得通,但我无法摆脱它有点……设计的感觉。大部分复杂性来自我的基本情况 - 初始文本应该只是淡入,随后的文本更改应该淡出然后淡入。

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

【问题讨论】:

    标签: ios objective-c reactive-cocoa


    【解决方案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 的机会。

    【讨论】:

    • 关于 if:then:else 用法的伟大呼吁 - 我会更新。关于 animateWithDuration: 调用,如果不连接两个 animateWithDuration: 调用的结果并在第二个调用中包含一个 delay: 运算符,我无法使此代码正常工作。我认为这是因为 animateWithDuration: 在通过 subscribeNext: 接收到值时立即将 UIView 动画排入队列。没有延迟:第一个动画(淡出)似乎丢失了。
    • 嗯。最好在 RCL repo 上询问。 docs for -animateWithDuration: (和代码)表明 concat/sequential 排序是默认的。
    • 是的,我会在那里打开一个问题。对于此示例,我能够通过在 [RACSignal return] 之间添加延迟来删除 concat:(并使用单个 animateWithDuration:)。这似乎更接近于 RCL 文档的建议。谢谢!
    • 我想我搞砸了你建议的编辑(我以前从未使用过)。我会根据您的想法进行编辑,如果正确,请告诉我。
    【解决方案2】:

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

    [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];
    }];
    

    【讨论】:

      猜你喜欢
      • 2011-07-20
      • 1970-01-01
      • 2016-04-02
      • 1970-01-01
      • 2022-07-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-03-23
      相关资源
      最近更新 更多