Как я могу анимировать вращение UIView только в определенном диапазоне градусов?

В моем приложении у меня есть симулятор аналогового индикатора, который вы можете найти на приборной панели автомобиля или на панели управления. Запустите следующий фрагмент кода для примера того, что я имею в виду:

svg { height: 100vh; }
#dial {
  fill: white;
  stroke: #CCCCCC;
  stroke-dasharray: 405;
  transform: rotate(135deg);
  transform-origin: center;
}
#pointer {
  fill: #FF0000;
  animation: 2s infinite alternate dialrotate;
  transform-origin: bottom center;
  animation-timing-function: ease-in-out;
}
@keyframes dialrotate {
  from {
    transform: rotate(-135deg);
  }
  to {
    transform: rotate(135deg);
  }
}
<?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200">
  <circle id="dial" cx="99.5" cy="100.5" r="86"/>
 <polygon id="pointer" points="100.1,25.5 98.2,100 102,100"/>
</svg>

(Я не использую SVG в своем приложении - я просто собрал пример, используя SVG, потому что я в первую очередь веб-разработчик)

Вот как я сейчас оживляю движение циферблата:

UIView.animate(
    withDuration: 0.1,
    delay: 0.0,
    options: .curveEaseOut,
    animations: {
        self.dialPointer.transform = CGAffineTransform.init(rotationAngle: radians);
    },
    completion: nil
)

Я хочу анимировать направление указателя между двумя крайностями (270 градусов). Но я не хочу, чтобы указатель вращался по более короткому пути на 90 градусов за пределы циферблата; то есть это НЕПРАВИЛЬНО:

svg { height: 100vh; }
#dial {
  fill: white;
  stroke: #CCCCCC;
  stroke-dasharray: 405;
  transform: rotate(135deg);
  transform-origin: center;
}
#pointer {
  fill: #FF0000;
  animation: 2s infinite alternate dialrotate;
  transform-origin: bottom center;
  animation-timing-function: ease-in-out;
}
@keyframes dialrotate {
  from {
    transform: rotate(225deg);
  }
  to {
    transform: rotate(135deg);
  }
}
<?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200">
  <circle id="dial" cx="99.5" cy="100.5" r="86"/>
  <polygon id="pointer" points="100.1,25.5 98.2,100 102,100"/>
</svg>

Как я могу заставить вращение UIView вращаться по длинному пути, вместо того, чтобы вращаться по кратчайшему направлению?

2 ответа

Как вы обнаружили, по умолчанию анимация UIView использует кратчайший путь при интерполяции значений углов. У вас есть несколько вариантов:

  1. Вы можете разбить анимацию на несколько более коротких шагов или выделить ее по ключевым кадрам.
  2. Вместо этого вы можете использовать CABasicAnimation и анимировать преобразование вашего слоя с помощью накопительной анимации.

Пример детской площадки:

import PlaygroundSupport
import UIKit

let rect = CGRect(x: 0, y: 0, width: 300, height: 300)
let view = UIView(frame: rect)
let bar = UIView(frame: rect.insetBy(dx: 40, dy: 140))
bar.backgroundColor = .red
view.addSubview(bar)

let animation = CABasicAnimation(keyPath: "transform.rotation.z")
animation.toValue = 1.25 * CGFloat.pi
animation.isCumulative = true
animation.duration = 5
bar.layer.add(animation, forKey: "spin")
bar.layer.transform = CATransform3DMakeRotation(1.25 * CGFloat.pi, 0, 0, 1)
PlaygroundPage.current.liveView = view

Вы можете сделать это так

UIView.animate(
    withDuration: 0.1,
    delay: 0.0,
    options: .curveEaseOut,
    animations: {
        if radians > CGFloat.pi {
            self.dialPointer.transform = CGAffineTransform.init(rotationAngle: CGFloat.pi);
        }
        self.dialPointer.transform = CGAffineTransform.init(rotationAngle: radians);
    },
    completion: nil
)
Другие вопросы по тегам