In my app, I've got a simulation of an analog readout dial, like you might find on a car dashboard or a control panel. Run the following snippet for an example of what I mean:
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>
(I'm not using SVGs in my app--I just threw the example together using an SVG because I'm primarily a web developer)
Here's how I'm animating the dial movement right now:
UIView.animate(
withDuration: 0.1,
delay: 0.0,
options: .curveEaseOut,
animations: {
self.dialPointer.transform = CGAffineTransform.init(rotationAngle: radians);
},
completion: nil
)
I want to animate the pointer's direction between the two extremes (270 degrees). But I DON'T want the pointer to rotate across the shorter 90 degree path outside the dial's bounds; ie this is WRONG:
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>
How can I force the UIView's rotation to rotate the long way around, instead of rotating over the shortest direction?
As you have discovered, by default UIView animation takes the shortest path when interpolating angle values. You have a couple of options:
Playground example:
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
You can do it like this
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
)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.