Есть ли проблема с этим использованием kCAMediaTimingFunctionEaseIn?

Я использую CALayer, CAReplicatorLayer а также CABasicAnimation оживить гистограмму на место. Гистограмма состоит из "сегментов", которые я представляю как маленькие прямоугольники с CALayer, Затем я добавляю их в качестве подслоев CAReplicatorLayer в петле. Наконец, я добавляю анимацию для каждого маленького прямоугольника, который помещает прямоугольник на свое место в диаграмме.

Вот окончательное состояние анимации:

Правильное конечное состояние анимации

И вот как это выглядит в середине анимации:

Анимация состояния

Вот код, который делает это возможным:

#define BLOCK_HEIGHT 6.0f
#define BLOCK_WIDTH 8.0f

@interface TCViewController ()

@property (nonatomic, strong) NSMutableArray * blockLayers;         // blocks that fall from the sky
@property (nonatomic, strong) NSMutableArray * replicatorLayers;    // replicator layers that will replicate the blocks into position

@end

@implementation TCViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor lightGrayColor];

    self.blockLayers = [NSMutableArray array];
    self.replicatorLayers = [NSMutableArray array];

    NSArray * dataBlocksData = @[@1,@2,@3,@1,@5,@2,@11,@14,@5,@12,@10,@3,@7,@6,@3,@4,@1];

    __block CGFloat currentXPosition = 0.0f;

    [dataBlocksData enumerateObjectsUsingBlock:^(NSNumber * obj, NSUInteger idx, BOOL *stop) {

        currentXPosition += BLOCK_WIDTH + 2.0f;

        if (obj.integerValue == 0) return;

        // replicator layer for data blocks in a column
        CAReplicatorLayer * replicatorLayer = [[CAReplicatorLayer alloc] init];
        replicatorLayer.frame = CGRectMake(currentXPosition, 0.0f, BLOCK_WIDTH, 200.0f);

        // single data block layer (must be new for each replicator layer. can't reuse existing layer)
        CALayer * blockLayer = [[CALayer alloc] init];
        blockLayer.frame = CGRectMake(0, 200-BLOCK_HEIGHT, BLOCK_WIDTH, BLOCK_HEIGHT);
        blockLayer.backgroundColor = [UIColor whiteColor].CGColor;
        [replicatorLayer addSublayer:blockLayer];

        replicatorLayer.instanceCount = obj.integerValue;
        replicatorLayer.instanceDelay = 0.1f;
        replicatorLayer.instanceTransform = CATransform3DMakeTranslation(0, -BLOCK_HEIGHT, 0);    // negative height moves back up the column

        [self.blockLayers addObject:blockLayer];                
        [self.replicatorLayers addObject:replicatorLayer];      
    }];
}

- (void)viewDidLayoutSubviews {
    // add replicator layers as sublayers
    [self.replicatorLayers enumerateObjectsUsingBlock:^(CAReplicatorLayer * obj, NSUInteger idx, BOOL *stop) {
        [self.view.layer addSublayer:obj];
    }];
}

- (void)viewDidAppear:(BOOL)animated {

    // add animations to each block layer
    [self.blockLayers enumerateObjectsUsingBlock:^(CALayer * obj, NSUInteger idx, BOOL *stop) {
        // position animation
        CABasicAnimation * animation = [CABasicAnimation animationWithKeyPath:@"transform.translation.y"];
        animation.fromValue = @(-300);
        animation.toValue = @(0);
        animation.duration = 1.0f;
        animation.fillMode = kCAFillModeBoth;
        animation.removedOnCompletion = NO;
        animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault];
        [obj addAnimation:animation forKey:@"falling-block-animation"];
    }];
}

Наконец, вот подвох. Если вы укажете нет timingFunction, Linear функция или EaseIn Функция анимации не полностью завершена. Как блоки не падают полностью на место. Похоже, они близки к завершению, но не совсем. Просто кадр или два. Поменяйте местами назначение функции синхронизации в -viewDidAppear: и вы получите эту странность

введите описание изображения здесь

Давай, попробуй. Я даже пытался указать функцию с контрольными точками ( ссылка), которые точно так же, как EaseIn функция:

// use control points for an EaseIn timing:
animation.timingFunction = [CAMediaTimingFunction functionWithControlPoints:0.42 :0.00 :1.0 :1.0];

// or simply specify the ease in timing function:
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];

По какой-то причине EaseOut а также Default функции хронометража работают нормально, но простота в функции стиля, кажется, занимает больше времени, визуально, возможно, из-за длины кривой интерполяции?

Я пробовал много вещей, чтобы сузить вопрос:

  1. Не зацикливаться на этих структурах и использовать только 1 слой и 1 слой репликатора с одинаковой анимацией. Под этим я подразумеваю анимацию только одного столбца. Неудачно. Циклы не проблема.
  2. Поместите код создания, добавления подслоя и добавления анимации в -viewDidAppear, Похоже, ничего не изменилось, если анимации были добавлены раньше или позже в жизненном цикле представления.
  3. Использование свойств для моих структур. Нет разницы.
  4. Не используя свойства для моих структур. Опять без разницы.
  5. Добавлены обратные вызовы для завершения анимации, чтобы удалить анимацию, чтобы столбцы оставались в правильном "конечном" состоянии. Это было не хорошо, но стоит попробовать.
  6. Эта проблема присутствует в iOS 6 и 7.

Я действительно хочу использовать EaseIn функция синхронизации, потому что это лучшая анимация для чего-то, что встало на свои места. Я также хочу использовать CAReplicatorLayer потому что он делает именно то, что я хочу сделать.

Так что не так с EaseIn функции времени, как я их использую? Это ошибка?

1 ответ

Решение

Я согласен с тем, что это, похоже, ошибка в комбинации функции синхронизации CAReplicatorLayer и kCAMediaTimingFunctionEaseIn.

В качестве обходного пути я предлагаю использовать анимацию ключевых кадров с тремя ключевыми кадрами. Первые два ключевых кадра определяют желаемую анимацию с легкостью синхронизации. Последний ключевой кадр определен как идентичный второму ключевому кадру, но дает анимации дополнительное время, чтобы сесть на последний ключевой кадр, что, по-видимому, позволяет слою репликатора анимировать последний реплицированный слой в конечное состояние.

Поэтому мое предложение таково:

CAKeyframeAnimation * animation = [CAKeyframeAnimation animationWithKeyPath:@"transform.translation.y"];

//  extra keyframe duplicating final state
animation.values = @[@-300.0f, @0.0f, @0.0f];

//  double length of original animation, with last half containing no actual animation
animation.keyTimes = @[@0.0, @0.5, @1.0];
animation.duration = 2.0f;

animation.fillMode = kCAFillModeBoth;
animation.removedOnCompletion = NO;

//  ease-in for first half of animation; default after
animation.timingFunctions = @[[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn], [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault]];
[obj addAnimation:animation forKey:@"falling-block-animation"];
Другие вопросы по тегам