2017-02-10 29 views
5

У меня возникли проблемы, чтобы понять, почему анимация не работает, как ожидалось. То, что я делаю это:CAKeyFrameAnimation не Linear для значений больше, чем PI

  1. Создать UIBezierPath с дугой, чтобы переместить метку по этому пути и живой ход трактов.

    //Start Point is -.pi /2 to let the Arc start at the top. 
    //self.progress = Value between 0.0 and 1.0 
    let path : UIBezierPath = UIBezierPath.init(arcCenter: CGPoint.init(x: self.bounds.width * 0.5, y: self.bounds.height * 0.5), 
    radius: self.bounds.width * 0.5, startAngle: -.pi/2, endAngle: (2 * self.progress * .pi) - (.pi/2), clockwise: true) 
    return path 
    
  2. Добавить этот путь к CAShapeLayer

    circlePathLayer.frame = bounds 
    circlePathLayer.path = self.path.cgPath 
    circlePathLayer.strokeStart = 0 
    circlePathLayer.strokeEnd = 1 
    
  3. анимировать свойство strokeEnd с CABasicAnimation

    let animation = CABasicAnimation(keyPath: "strokeEnd") 
    animation.repeatCount = HUGE 
    animation.fromValue = 0.0 
    animation.toValue = 1.0 
    animation.duration = self.animationDuration 
    animation.isRemovedOnCompletion = false 
    animation.fillMode = kCAFillModeBoth 
    
  4. анимировать положение свойство моего лейбла с CAKeyFrameAnimation

    let animationScore = CAKeyframeAnimation(keyPath: "position") 
    //some things I tried to fix 
    //animationScore.timingFunctions = [CAMediaTimingFunction(controlPoints: 0.250, 0.250, 0.750, 0.750)] 
    //animationScore.timingFunction = CAMediaTimingFunction.init(name: kCAMediaTimingFunctionLinear) 
    animationScore.path = self.path.cgPath 
    animationScore.duration = self.animationDuration 
    animationScore.isRemovedOnCompletion = false 
    animationScore.fillMode = kCAFillModeBoth 
    animationScore.repeatCount = HUGE 
    
  5. Добавить мои анимации к слою и маркировать

    self.circlePathLayer.add(animation, forKey: nil) 
    self.scoreLabel.layer.add(animationScore, forKey: nil) 
    

Моя проблема: Для ProgressValues ​​больше, чем 0,75 моей этикетке не движется в линейной скорости. Значения, превышающие 0,75, означают, что моя дуга больше, чем PI. Для значений, меньших 0,75, моя анимация работает нормально, а метка и обводка имеют одинаковую скорость и находятся на вершине друг друга.

GIF: Gif showing the animation

Игнорируйте 100% в ярлыке в этом формате GIF мой прогресс был на значении 0,76.

Вы видите, что моя метка замедляется после трех четвертей моего круга.

Я надеюсь, что кто-то может мне помочь. Большое спасибо

+0

Вы отправите демо? Если вы не возражаете? Я проверю его и вернусь к вам с решением –

+0

Я не вижу, как вы устанавливаете 'расчетMode'. Но в любом случае ... Почему вы вообще используете анимацию ключевого кадра? Просто поверните этикетку вокруг центра круга как опорную точку, как планета. Подумайте о часовой стрелке (помните часы руками?). Разумеется, вам понадобится вторая ротация вокруг центра ярлыков, чтобы сохранить его в вертикальном положении. – matt

+0

Спасибо вам за ваши ответы. @matt установка 'calculateMode' ничего не меняла. Но поворот моего ярлыка вокруг anchorPoint, кажется, стоит попробовать. Как мне это сделать? Это просто «CABasicAnimation transform.rotation»? И мне нужно создать «CAAnimationGroup», чтобы объединить обе анимации, не так ли? –

ответ

3

Анимация ключевого кадра вводит ненужное осложнение. Просто поверните метку вокруг центра с такой же продолжительностью, как инсульт анимация формы слоя:

enter image description here

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

Итак, как это сделать? Это три анимации, все с той же продолжительностью:

  • Формы слой strokeEnd, как анимация.

  • «Рука», проходящая через центр круга, с меткой в ​​качестве подслоя на одном конце (чтобы метка появилась в радиусе круга). Рука делает анимацию преобразования вращения.

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

Это весь код анимации:

let anim = CABasicAnimation(keyPath: "transform.rotation.z") 
    anim.fromValue = 0 
    anim.toValue = 5 
    anim.duration = 10 
    self.arm.layer.add(anim, forKey:nil) 

    let anim2 = CABasicAnimation(keyPath: "transform.rotation.z") 
    anim2.fromValue = 0 
    anim2.toValue = -5 
    anim2.duration = 10 
    self.lab.layer.add(anim2, forKey:nil) 

    let anim3 = CABasicAnimation(keyPath: "strokeEnd") 
    anim3.fromValue = 0 
    anim3.toValue = 1 
    anim3.duration = 10 
    self.shape.add(anim3, forKey:nil) 
+0

Прежде всего, благодарю вас за предоставление этого прекрасного ответа и объяснений. Полюбите аналогию с колесом обозрения. Только один вопрос: я понимаю цель «руки», но у меня есть некоторые проблемы с выяснением того, как я буду реализовывать что-то подобное. Не могли бы вы еще немного объяснить? Отмечая это как принятый ответ. –

+0

«Рука» - это просто вид или слой. Вы не можете видеть это, потому что я дал ему ясный фон. Его цель - просто перемещать этикетку на орбиту вокруг центра, позволяя ярлыку поворачиваться в своем собственном центре, чтобы оставаться в вертикальном положении (например, колесо обозрения, как я сказал). Подумайте о том, как рука «говорила» о колесе. – matt

+0

Хорошо, я думаю, у меня появилась идея. Еще раз спасибо за быстрый ответ. Вы мне очень помогли! Btw: У вас есть идея, почему после пути с keyFrameAnimation не работает так, как предполагалось? –