Анимация линии CAShapeLayer по круговой траектории

Я пытаюсь оживить мой CAShapeLayer в форме линии по круговой траектории. Я создал начальный путь своего линейного слоя и конечный путь и добавил их в значения CABasicAnimation и обратно.

Однако анимация - это не то, что я ожидал. Я застрял здесь последние 2 дня.

func getAllLayers() -> [CALayer] 

   {

    var layers = []

    let pointerEndPoint = CGPoint(x: xPointOfPointer , y: yPointOfPointer)  // Final end point of pointer
    let radius = 5

    let pointerFinalPath = UIBezierPath()
    pointerFinalPath.addArc(withCenter: circleCenter, radius: radius, startAngle: 0, endAngle:2 * CGFloat.pi, clockwise: false)
    pointerFinalPath.move(to: circleCenter)
    pointerFinalPath.addLine(to: endPoint)
    pointerFinalPath.close()

    let pointerPathLayer = CAShapeLayer()
    pointerPathLayer.path = pointerFinalPath.cgPath
    pointerPathLayer.lineWidth = 2
    pointerPathLayer.fillColor = UIColor.black.cgColor
    pointerPathLayer.strokeColor = UIColor.black.cgColor
    layers.append(pointerPathLayer)

    let pointerInitialBezierPath = UIBezierPath()
    pointerInitialBezierPath.addArc(withCenter: circleCenter, radius: radius,startAngle: 0 , endAngle: 2 * CGFloat.pi , clockwise: false)
    pointerInitialBezierPath.move(to: circleCenter)
    pointerInitialBezierPath.addLine(to: CGPoint(x: circleCenter.x - 50 , y: centerY))
    pointerInitialBezierPath.close()


    let pointerInitialCGPath = pointerInitialBezierPath.cgPath
    let pointerFinalCGPath = pointerFinalPath.cgPath


                    // Pointer Animation
    let pointerAnimation = CABasicAnimation(keyPath: #keyPath(CAShapeLayer.path))
     pointerAnimation.fromValue = pointerInitialCGPath
     pointerAnimation.toValue = pointerFinalCGPath
      pointerAnimation.duration = 0.5
      pointerAnimation.isCumulative = true
      pointerPathLayer.add(pointerAnimation, forKey: "Animation")

return layers
}

Искал решение в Stack overflow, но не смог найти свою ошибку. Я хочу, чтобы моя анимация двигалась, как стрелка на часах.

Моя анимация

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

РЕДАКТИРОВАТЬ 1: Если я попробую transform.rotation animation, я получу вращение иглы, но это произойдет вокруг неправильной центральной точки.

      // Pointer Animation

            let rotateAnimation = CABasicAnimation(keyPath: "transform.rotation")
            rotateAnimation.fromValue = 0.0
            rotateAnimation.toValue = angleOfActualValueinRad
            rotateAnimation.duration = 2.0
            pointerPathLayer.add(rotateAnimation, forKey: "Animation")

Анимация вращения

РЕДАКТИРОВАТЬ 2: используйте эту ссылку, чтобы найти мой проект на github. Проблема с анимацией


person Jaffer Sheriff    schedule 23.07.2019    source источник
comment
что, если вы измените anchorPoint на pointerPathLayer и вместо этого добавите анимацию вращения?   -  person Mike Haydan    schedule 23.07.2019
comment
Отправляю ответ с кодом вращения.   -  person girish_pro    schedule 23.07.2019
comment
@girish_pro Вопрос обновлен   -  person Jaffer Sheriff    schedule 23.07.2019
comment
Вам необходимо установить точку привязки. проверьте мой ответ. добавить следующую строку. pointerPathLayer.anchorPoint = CGPoint (x: 0,5, y: 0,5)   -  person girish_pro    schedule 23.07.2019
comment
проверь себя на Github. Я публикую проблему с предложением кода.   -  person girish_pro    schedule 23.07.2019
comment
Люди Спасибо за вашу помощь и время. Проблема здесь в том, что я не устанавливал фрейм для CAShapeLayer. Эта практика возникла в результате использования ограничений с UIViews. Итак, как предложил @girish_pro, я исправил это, правильно выполнив грязную работу по вычислению кадра для каждого CAShapelayer и применив значение угла к значению угла в моем CABasicAnimation.   -  person Jaffer Sheriff    schedule 25.07.2019


Ответы (3)


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

let circleCenter = CGPoint(x: 150.0, y: 150.0)

let startPoint = CGPoint(x: 100, y: 150.0)
    // Start Path
    let pointerStartPath = UIBezierPath()
    pointerStartPath.move(to: circleCenter)
    pointerStartPath.addLine(to: startPoint)
    pointerStartPath.close()

    let pointerPathLayer = CAShapeLayer()
    pointerPathLayer.frame = CGRect(x: 0, y:0, width: 300, height: 300)
    pointerPathLayer.path = pointerStartPath.cgPath
    pointerPathLayer.lineWidth = 2
    pointerPathLayer.fillColor = UIColor.green.cgColor
    pointerPathLayer.strokeColor = UIColor.black.cgColor
    self.view.layer.addSublayer(pointerPathLayer)

// New : Set Anchor point
pointerPathLayer.anchorPoint = CGPoint(x: 0.5, y: 0.5)

    let rotateAnimation = CABasicAnimation(keyPath: "transform.rotation")
    rotateAnimation.fromValue = 0.0
    rotateAnimation.toValue = CGFloat.pi * 2.0
    rotateAnimation.duration = 2.0
    pointerPathLayer.add(rotateAnimation, forKey: "Animation")

Используя этот код, вы можете добиться вращения на 360 градусов.

person girish_pro    schedule 23.07.2019
comment
все еще такая же проблема? - person girish_pro; 23.07.2019
comment
да, братан, проверьте мой прикрепленный проект git, он показывает проблему. - person Jaffer Sheriff; 23.07.2019

Вы используете неправильное вращение трансформации, попробуйте сделать что-нибудь вроде этого

let rAnimation = CABasicAnimation(keyPath:"transform.rotation.z")
    rAnimation.duration = 1
    rAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
    rAnimation.fromValue = degree2radian(90)
    rAnimation.repeatCount = Float.infinity
    rAnimation.toValue = degree2radian(180)
    progressLineLayer.add(rAnimation, forKey:"rotate")

func degree2radian(_ a: CGFloat) -> CGFloat {
    return CGFloat.pi * a / 180
}

просмотрите эту ссылку http://sketchytech.blogspot.com/2014/11/swift-how-to-draw-clock-face-using_12.html, может быть, это будет полезно

ОБНОВЛЕНО:

Я написал код, чтобы показать, как это работает

private func animateProgress() {

    // black circle at center

    let blackCirclePath = UIBezierPath()
    blackCirclePath.addArc(withCenter: view.center, radius: 5, startAngle: 0, endAngle: CGFloat.pi * 2, clockwise: true)
    blackCirclePath.fill()
    UIColor.black.setFill()
    blackCirclePath.close()

    let circleLayer = CAShapeLayer()
    circleLayer.path = blackCirclePath.cgPath
    view.layer.addSublayer(circleLayer)

    // all scale gray background layer

    let grayHalfCirclePath = UIBezierPath()
    grayHalfCirclePath.addArc(withCenter: view.center, radius: 50, startAngle: 0, endAngle: CGFloat.pi, clockwise: false)

    let grayHalfCircleLayer = CAShapeLayer()
    grayHalfCircleLayer.frame = view.bounds
    grayHalfCircleLayer.path = grayHalfCirclePath.cgPath
    grayHalfCircleLayer.strokeColor = UIColor.lightGray.cgColor
    grayHalfCircleLayer.fillColor = nil
    grayHalfCircleLayer.lineWidth = 10
    view.layer.addSublayer(grayHalfCircleLayer)

    // animated progress scale

    let progressHalfCirclePath = UIBezierPath()
    progressHalfCirclePath.addArc(withCenter: view.center, radius: 50, startAngle: CGFloat.pi, endAngle: -CGFloat.pi / 2, clockwise: true)

    let progressLayer = CAShapeLayer()
    progressLayer.frame = view.bounds
    progressLayer.path = progressHalfCirclePath.cgPath
    progressLayer.strokeColor = UIColor.orange.cgColor
    progressLayer.fillColor = nil
    progressLayer.lineWidth = 10
    view.layer.addSublayer(progressLayer)

    // animation

    let pAnimation = CABasicAnimation(keyPath: "strokeEnd")
    pAnimation.duration = 1.0
    pAnimation.fromValue = NSNumber(floatLiteral: 0)
    pAnimation.toValue = NSNumber(floatLiteral: 1)
    progressLayer.add(pAnimation, forKey: "strokeEnd")

    // black arrow

    let progressLineLayer = CAShapeLayer()
    progressLineLayer.frame = view.bounds

    let progressLinePath = CGMutablePath()
    progressLinePath.move(to: CGPoint(x: view.center.x, y: view.center.y))
    progressLinePath.addLine(to: CGPoint(x: view.center.x - 30, y: view.center.y - 30))
    progressLineLayer.path = progressLinePath
    progressLineLayer.strokeColor = UIColor.green.cgColor
    progressLineLayer.lineWidth = 1
    progressLineLayer.strokeColor = UIColor.black.cgColor
    view.layer.addSublayer(progressLineLayer)

    let rAnimation = CABasicAnimation(keyPath:"transform.rotation.z")
    rAnimation.duration = 1
    rAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
    rAnimation.fromValue = degree2radian(-45)
    rAnimation.repeatCount = 1
    rAnimation.toValue = degree2radian(45)
    progressLineLayer.add(rAnimation, forKey:"rotate")
}

func degree2radian(_ a: CGFloat) -> CGFloat {
    return CGFloat.pi * a / 180
}
person Alexandr Kolesnik    schedule 23.07.2019
comment
Спасибо, чувак, за твою помощь. Я очень ценю ваше время, чтобы помочь мне. - person Jaffer Sheriff; 25.07.2019

Попробуйте эту библиотеку ABGuageViewKit

Легкий и настраиваемый с помощью анимации.

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

person Steve Chan    schedule 23.07.2019
comment
Спасибо за ваше время. Есть идеи, почему мой код не работает. - person Jaffer Sheriff; 23.07.2019