CABasicAnimation с CALayer path не анимируется

Я пытаюсь анимировать переход CALayer от нормальных углов к закругленным углам. Я хочу закруглить только верхние углы, поэтому использую UIBezierPath. Вот код:

UIRectCorner corners = UIRectCornerTopLeft | UIRectCornerTopRight;

UIBezierPath* newPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds byRoundingCorners:corners cornerRadii:CGSizeMake(8.0f, 8.0f)];
UIBezierPath* oldPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds byRoundingCorners:corners cornerRadii:CGSizeMake(0.0f, 0.0f)];

CAShapeLayer* layer = [CAShapeLayer layer];
layer.frame = self.bounds;
layer.path = oldPath.CGPath;

self.layer.mask = layer;

CABasicAnimation* a = [CABasicAnimation animationWithKeyPath:@"path"];
[a setDuration:0.4f];
[a setFromValue:(id)layer.path];
[a setToValue:(id)newPath.CGPath];

layer.path = newPath.CGPath;
[layer addAnimation:a forKey:@"path"];

Но когда я запускаю это, углы закругляются, а анимации нет - это происходит мгновенно. Я видел несколько похожих вопросов здесь, на SO, но они не решили мою проблему.

Анимация CALayer для iPhone

Анимация свойства shadowPath CALayer

Я уверен, что я просто делаю одну мелочь, которая вызывает проблему, но, хоть убей, я не могу этого понять.

Решение:

- (void)roundCornersAnimated:(BOOL)animated {
    UIRectCorner corners = UIRectCornerTopLeft | UIRectCornerTopRight;

    UIBezierPath* newPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds byRoundingCorners:corners cornerRadii:CGSizeMake(8.0f, 8.0f)];
    UIBezierPath* oldPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds byRoundingCorners:corners cornerRadii:CGSizeMake(0.0001f, 0.0001f)];

    CAShapeLayer* layer = [CAShapeLayer layer];
    layer.frame = self.bounds;

    self.layer.mask = layer;

    if(animated) {
        CABasicAnimation* a = [CABasicAnimation animationWithKeyPath:@"path"];
        [a setDuration:0.4f];
        [a setFromValue:(id)oldPath.CGPath];
        [a setToValue:(id)newPath.CGPath];

        layer.path = newPath.CGPath;
        [layer addAnimation:a forKey:@"path"];
    } else {
        layer.path = newPath.CGPath;
    }
}

На самом деле все, что мне нужно было сделать, это установить правильное значение «от» для анимации.


person eric.mitchell    schedule 13.07.2012    source источник
comment
Решение было бы неплохо в качестве ответа, а не части вопроса, но в любом случае это полезно, так что +1 :)   -  person buildsucceeded    schedule 11.03.2016


Ответы (3)


Мне кажется, что вы создаете слой-фигуру, у которого нет пути, а затем пытаетесь анимировать, добавляя к нему путь. Слои-фигуры - это особый случай, и они так не работают.

У вас должен быть путь в слое-фигуре до и после анимации, и путь должен иметь одинаковое количество точек как до, так и после.

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

Однако даже это может не сработать, потому что путь должен иметь точно такое же количество точек, и я подозреваю, что дуги упростятся до точек, когда радиус равен нулю.

Я только что нашел вызов UIBezierPath, который создаст прямоугольник, в котором вы можете выбрать углы для скругления. Вызов bezierPathWithRoundedRect: byRoundingCorners: cornerRadii:

Это создаст прямоугольник с любым количеством закругленных углов. Однако я не уверен, сработает ли это для анимации пути. Возможно, вам придется потребовать, чтобы все углы были скруглены, а для двух не закругленных углов задан нулевой радиус. Таким образом, вы МОЖЕТЕ получить контур с таким же количеством точек в нем, как и контур прямоугольника с закругленными углами, чтобы анимация контура работала правильно. Тебе придется попробовать.

person Duncan C    schedule 13.07.2012
comment
Спасибо. Ты был прав. Установка правильного значения "от" для анимации сделала это. - person eric.mitchell; 13.07.2012

Попробуйте поставить:

layer.path = newPath.CGPath;

после:

[layer addAnimation:a forKey:@"path"];
person demastyle    schedule 10.04.2014

Нашел действительно элегантное решение от Эндрю Вагнера здесь.

По сути, система будет обрабатывать все анимации смены пути, все, что вам нужно, это:

  1. Реализуйте подкласс CAShapeLayer
  2. Отменить и реализовать actionForKey
  3. Обновите путь как обычно, и вы получите свою анимацию!
person Harry Bloom    schedule 13.04.2018