简体   繁体   English

如何仅在特定角度范围内为UIView的旋转设置动画?

[英]How can I animate a UIView's rotation only within a certain degree range?

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) (我没有在我的应用程序中使用SVG,我只是使用SVG将示例放在一起,因为我主要是Web开发人员)

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). 我想为两个极端(270度)之间的指针方向设置动画。 But I DON'T want the pointer to rotate across the shorter 90 degree path outside the dial's bounds; 但是我不希望指针在表盘边界之外的较短的90度路径上旋转; 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? 如何强制UIView的旋转沿长距离旋转,而不是沿最短方向旋转?

As you have discovered, by default UIView animation takes the shortest path when interpolating angle values. 您已经发现,默认情况下,在插入角度值时,UIView动画会采用最短路径。 You have a couple of options: 您有两种选择:

  1. You can decompose the animation into several shorter steps, or keyframe it. 您可以将动画分解为几个较短的步骤,或对其进行关键帧设置。
  2. You can use a CABasicAnimation instead and animate your layer's transform using a cumulative animation 您可以改用CABasicAnimation并使用累积动画来动画化图层的变换

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
)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM