Анимация кругового UIBezierPath

У меня есть проект, в котором я анимирую UIBezierPath на основе заданного прогресса. BezierPath имеет форму круга и находится в UIView, а анимация выполняется в drawRect с использованием CADisplayLink прямо сейчас. Проще говоря, на основе установленного прогресса x путь должен продолжаться в радиальном направлении (если xбольше, чем раньше) или сжиматься (если x меньше).

self.drawProgress = (self.displayLink.timestamp - self.startTime)/DURATION;

CGFloat startAngle = -(float)M_PI_2;
CGFloat stopAngle = ((self.x * 2*(float)M_PI) + startAngle);
CGFloat currentEndAngle = ((self.oldX * 2*(float)M_PI) + startAngle);
CGFloat endAngle = currentEndAngle-((currentEndAngle-stopAngle)*drawProgress);

UIBezierPath *guideCirclePath = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES];

Это в случае x сокращается с момента нашего последнего обновления. Проблемы, с которыми я сталкиваюсь, на самом деле несколько:

  1. Форма всегда начинает рисоваться при 45º (если я не поворачиваю вид). Я не нашел способа изменить это, и настройка startAngleна -45º на самом деле не имеет значения, потому что он всегда "всплывает" на 45. Могу ли я что-нибудь с этим сделать, или я должен прибегнуть к другим методам рисования?
  2. Есть ли другой способ оживить эти вещи? Я много читал об использовании CAShapeLayer но я не совсем понял реальную разницу (с точки зрения недостатков и преимуществ) в использовании этих двух методов. Если бы кто-нибудь мог уточнить, я был бы очень признателен!

ОБНОВЛЕНИЕ: вместо этого я перенес код в CAShapeLayer, но теперь я столкнулся с другой проблемой. Лучше всего описать это изображение:

Происходит то, что, когда слой должен сжиматься, тонкая внешняя линия все еще там (независимо от направления движения). И когда бар сжимается, дельта 1-xне удаляется, если я явно не нарисую новую белую фигуру. Код для этого следует. Есть идеи?

UIBezierPath *circlePath = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startAngle endAngle:stopAngle clockwise:YES];
CAShapeLayer *circle = [CAShapeLayer layer];
circle.path = [circlePath CGPath];
circle.strokeStart = 0;
circle.strokeEnd = 1.0*self.progress;

// Colour and other customizations here.

if (self.progress > self.oldProgress) {
    drawAnimation.fromValue = @(1.0*self.oldProgress);
    drawAnimation.toValue = @(circle.strokeEnd);
} else { 
    drawAnimation.fromValue = @(1.0*self.oldProgress);
    drawAnimation.toValue = @(1.0*self.progress);
    circle.strokeEnd = 1.0*self.progress;
}

drawAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; //kCAMediaTimingFunctionEaseIn
[circle addAnimation:drawAnimation forKey:@"strokeEnd"];

ОБНОВЛЕНИЕ 2: я исправил большинство других ошибок. Выяснилось, что только я был довольно глупым все время и слишком усложнял всю анимацию (не говоря уже о умножении на 1 везде, что?). Я сделал gif ошибки, которую не могу решить:

глюк радиального контроля

Есть идеи?

ОБНОВЛЕНИЕ 3: (и закрытие). Мне удалось избавиться от ошибки, позвонив

[self.layer.sublayers makeObjectsPerformSelector:@selector(removeFromSuperlayer)];

И теперь все работает как надо. Спасибо за помощь!

1 ответ

Решение

Использование CAShapeLayer намного проще и чище. Причина в том, что CAShapeLayer включает свойства strokeStart и strokeEnd. Эти значения варьируются от 0 (начало пути) до 1 (конец пути) и являются анимируемыми.

Изменяя их, вы можете легко нарисовать любую дугу вашего круга (или любую часть произвольного пути, если на то пошло). Свойства являются анимируемыми, поэтому вы можете создать анимацию растущего / уменьшающегося фрагмента пирога или участка в форме кольца., Это намного проще и производительнее, чем реализация кода в drawRect.

Другие вопросы по тегам