我正在使用CAKeyframeAnimation实现一个财富之轮,并在动画停止后尝试读取结果。但在这里,我没有得到确定性的结果。 动画停止后是否无法读取旋转?
这是我的动画代码:
let anim = CAKeyframeAnimation(keyPath: "transform.rotation.z")
anim.duration = max(strength / 2, 1.0)
anim.isCumulative = true
anim.values = [NSNumber(value: Float(p)), Float(circleRotationOffset)]
anim.keyTimes = [NSNumber(value: Float(0)),NSNumber(value: Float(1.0))]
anim.timingFunctions = [CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)]
anim.isRemovedOnCompletion = false
anim.fillMode = kCAFillModeForwards
anim.delegate = self
wheelImage.layer.removeAllAnimations()
wheelImage.layer.add(anim, forKey: "rotate")
在这里我如何阅读轮换:
func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
readImageOrientation()
}
func readImageOrientation(){
let radians:Double = atan2( Double(wheelImage.transform.b), Double(wheelImage.transform.a))
let degrees:CGFloat = CGFloat(radians) * (CGFloat(180) / CGFloat(M_PI))
sectionForDegrees(degree: degrees)
}
为了完整起见,我完整的课程。
class WOFView: UIView, CAAnimationDelegate {
@IBOutlet weak var wheelImage: UIImageView!
private var history = [Dictionary<String, Any>]()
private var rotation: CGFloat = 0
private var startAngle: CGFloat = 0
private var circleRotationOffset: CGFloat = 0
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
if let touchPoint = touches.first?.location(in: self){
if startAngle == 0{
startAngle = atan2(self.frame.width - touchPoint.y, self.frame.height - touchPoint.x)
}
rotation = startAngle
if !touch(touches.first!, isInLeftHalfOf: wheelImage) {
rotation = -rotation
}
history.removeAll()
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesMoved(touches, with: event)
guard let touchPoint = touches.first?.location(in: self) else {
return
}
let dic = ["time" : NSNumber(value: CFAbsoluteTimeGetCurrent()),
"point": NSValue(cgPoint: touchPoint),
"rotation": NSNumber(value: Float(circleRotationOffset + rotation))]
history.insert(dic, at: 0)
if history.count == 3{
history.removeLast()
}
rotation = atan2(self.frame.width - touchPoint.y, self.frame.height - touchPoint.x) - startAngle
if !touch(touches.first!, isInLeftHalfOf: wheelImage) {
rotation = -rotation
}
wheelImage.transform = CGAffineTransform(rotationAngle: circleRotationOffset + rotation)
print("offset: \(circleRotationOffset)")
readImageOrientation()
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesEnded(touches, with: event)
guard let touchPoint = touches.first?.location(in: self) else {
return
}
guard let lastObject = history.last else{
return
}
guard let pointValue = lastObject["point"] as? CGPoint else{
return
}
guard let timeValue = lastObject["time"] as? NSNumber else {
return
}
guard let rotationValue = lastObject["rotation"] as? NSNumber else {
return
}
let timeDif = CFAbsoluteTimeGetCurrent() - (timeValue.doubleValue)
circleRotationOffset = circleRotationOffset + rotation
let lastRotation = rotationValue.floatValue
let dist = sqrt(((pointValue.x - touchPoint.x) * (pointValue.x - touchPoint.x)) +
((pointValue.y - touchPoint.y) * (pointValue.y - touchPoint.y)))
let strength = max(Double(min(1.0, dist / 80.0)) * (timeDif / 0.25) * M_PI * 2, 0.3) * 30
let p = circleRotationOffset
let dif = circleRotationOffset - CGFloat(lastRotation)
var inc = dif > 0
if dif > 3 || dif < -3{
inc = !inc
}
if (inc){
circleRotationOffset += CGFloat(strength)
}else{
circleRotationOffset -= CGFloat(strength)
}
let anim = CAKeyframeAnimation(keyPath: "transform.rotation.z")
anim.duration = max(strength / 2, 1.0)
anim.isCumulative = true
anim.values = [NSNumber(value: Float(p)), Float(circleRotationOffset)]
anim.keyTimes = [NSNumber(value: Float(0)),NSNumber(value: Float(1.0))]
anim.timingFunctions = [CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)]
anim.isRemovedOnCompletion = false
anim.fillMode = kCAFillModeForwards
anim.delegate = self
wheelImage.layer.removeAllAnimations()
wheelImage.layer.add(anim, forKey: "rotate")
}
func touch(_ touch:UITouch, isInLeftHalfOf view: UIView) -> Bool {
let positionInView = touch.location(in: view)
return positionInView.x < view.frame.midX
}
func touch(_ touch:UITouch, isInUpperHalfOf view: UIView) -> Bool {
let positionInView = touch.location(in: view)
return positionInView.y < view.frame.midY
}
func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
readImageOrientation()
}
func readImageOrientation(){
let radians:Double = acos(Double(wheelImage.transform.a))
let degrees:CGFloat = CGFloat(radians) * (CGFloat(180) / CGFloat(M_PI))
sectionForDegrees(degree: degrees)
}
func sectionForDegrees(degree: CGFloat){
var result = "not defined"
switch degree{
case 0 ... 90:
result = "3 \(degree)"
case 90.1...180:
result = "2 \(degree)"
case 181.1...270:
result = "1 \(degree)"
case 270.1...360:
result = "4 \(degree)"
default:
result = "not defined: \(degree)"
}
print(result)
}
}
答案 0 :(得分:2)
根据this Objective-C answer,至少有四种方法可以获得轮换:
let view = UIImageView()
view.transform = CGAffineTransform(rotationAngle: 0.02);
let x = view.value(forKeyPath: "layer.transform.rotation.z")
let a = acos(view.transform.a)
let b = asin(view.transform.b)
let c = atan2(view.transform.b, view.transform.a)
print(a)
print(b)
print(c)
print(x!)
打印:
0.0200000000000011
0.02
0.02
0.02
答案 1 :(得分:1)
我认为你应该使用acos()
代替atan2()
,基于三维旋转矩阵的外观:
在这个Rz矩阵中,我们可以看到transform.a和transform.b将作为余弦θ给出。参考CGAffineTransform documentation:
如果不进行测试,我非常确定一个简单的acos(Double(wheelImage.transform.a))
足以让你进行θ旋转。编辑:这是一个糟糕的建议,用atan2
替换acos
意味着您必须检查您的答案是正值还是负余弦值,并且完全没有意义。另一件需要记住的事情是,如果对轮子应用任何缩放,变换也会发生变化。
如果我错了,您可以随时使用strength
值来计算允许滚轮旋转多长时间并从中找到角度。这将需要您了解kCAMediaTimingFunctionEaseOut
如何正确工作,所以我建议您更改动画以首先计算角度(您保存为参数)然后将其直接应用于{{1}而不是将持续时间用作决定性因素。
看到完整的代码后,我发现你只解释了错误的角度,这里是对你的layer.transform
函数的重写,它将给出正确的部分:
sectionForDegrees