我正在实现一个命运之轮,灵感来自 stackoverflow 中的其他帖子,至少它在旋转。目前我的问题是,如果我向下滑动图像的右侧,轮子会朝错误的方向旋转。
有人能看出哪里出了问题吗?
class WOFView: UIView {
@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){
startAngle = atan2(self.frame.width - touchPoint.y, self.frame.height - touchPoint.x)
rotation = startAngle
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
wheelImage.transform = CGAffineTransform(rotationAngle: circleRotationOffset + rotation)
}
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
print("S: \(strength)")
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
wheelImage.layer.removeAllAnimations()
wheelImage.layer.add(anim, forKey: "rotate")
}
编辑 我使用 UIPanGestureRecognizer 简化了事情并想分享结果:
enum SpinningDirection{
case clockwise
case antiClockwise
}
enum MajorDirection{
case up
case down
case left
case right
}
enum Quadrant{
case ul
case ur
case ll
case lr
}
class WOFView: UIView, CAAnimationDelegate {
@IBOutlet weak var wheelImage: UIImageView!
private var maxSpeed = 0
private var majorDirection = MajorDirection.right
private var quadrant = Quadrant.ul
private var spinningDirection = SpinningDirection.clockwise
private var winner = ""
func setup(){
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(self.respondToPanGesture))
addGestureRecognizer(panGesture)
}
func respondToPanGesture(gesture: UIPanGestureRecognizer){
let velocity = gesture.velocity(in: self)
let vX = abs(velocity.x)
let vY = abs(velocity.y)
let speed = Int(vX + vY)
if speed > maxSpeed{
maxSpeed = speed
}
let location = gesture.location(in: self)
if vX > vY{
majorDirection = (velocity.x > 0) ? .right : .left
}
else{
majorDirection = (velocity.y > 0) ? .down : .up
}
if location.x < self.frame.width / 2 {
quadrant = (location.y < self.frame.height / 2 ) ? .ul : .ll
}
else {
quadrant = (location.y < self.frame.height / 2 ) ? .ur : .lr
}
switch quadrant {
case .ul:
switch majorDirection {
case .down, .left:
spinningDirection = .antiClockwise
case .up, .right:
spinningDirection = .clockwise
}
case .ur:
switch majorDirection {
case .down, .right:
spinningDirection = .clockwise
case .up, .left:
spinningDirection = .antiClockwise
}
case .lr:
switch majorDirection {
case .down, .left:
spinningDirection = .clockwise
case .up, .right:
spinningDirection = .antiClockwise
}
case .ll:
switch majorDirection {
case .down, .right:
spinningDirection = .antiClockwise
case .up, .left:
spinningDirection = .clockwise
}
}
if gesture.state == .began{
maxSpeed = 0
self.isUserInteractionEnabled = false
}
if gesture.state == .ended{
print("Ended")
print("MaxSpeed: \(maxSpeed)")
print("direction: \(spinningDirection)")
startAnimation(speed: maxSpeed, direction: spinningDirection)
}
}
private func startAnimation(speed: Int, direction : SpinningDirection){
var duration = Double(speed) / 10
if duration > 10{
duration = 10
}
if duration < 3{
duration = 3
}
print("duration: \(duration)")
let multiplier = (direction == .clockwise) ? -1.0 : 1.0
let normalizedSpeed = Double(speed) / 10 * multiplier
let goal = Double((speed * 100) % Int(2 * Double.pi * 100)) / 100.0
print("goal: \(goal)")
let halfPi = Double.pi/2
switch goal {
case 0*halfPi...1*halfPi:
winner = "1"
case 1*halfPi...2*halfPi:
winner = "4"
case 2*halfPi...3*halfPi:
winner = "3"
case 3*halfPi...4*halfPi:
winner = "2"
default:
print("?")
}
let anim = CAKeyframeAnimation(keyPath: "transform.rotation.z")
anim.duration = duration
anim.isCumulative = true
anim.values = [NSNumber(value: Float(normalizedSpeed)), Float(goal)]
anim.keyTimes = [NSNumber(value: Float(0)),NSNumber(value: Float(1))]
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) {
print("The winner is \(winner)")
self.isUserInteractionEnabled = true
}
}
最佳答案
介绍一种找出哪一面被触摸的方法:
func touch(_ touch:UITouch, isInLeftHalfOf view: UIView) -> Bool {
let positionInView = touch.location(in: view)
return positionInView.x < view.frame.midX
}
如果不是左侧,则反转旋转:
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
// your code ...
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)
}
即使这解决了您问题中提到的情况(向下滑动图像的右侧),您很可能至少需要一些微调,但您明白了。
结果:
关于swift - 实现命运之轮,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41616582/