Анимация кругового 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
сокращается с момента нашего последнего обновления. Проблемы, с которыми я сталкиваюсь, на самом деле несколько:
- Форма всегда начинает рисоваться при 45º (если я не поворачиваю вид). Я не нашел способа изменить это, и настройка
startAngle
на -45º на самом деле не имеет значения, потому что он всегда "всплывает" на 45. Могу ли я что-нибудь с этим сделать, или я должен прибегнуть к другим методам рисования? - Есть ли другой способ оживить эти вещи? Я много читал об использовании
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.