问题描述:

I'm implementing a wheel of fortune inspired by other post in stackoverflow and at least it is spinning. Currently my problem is that if I swipe on the right side of the image in the down direction the wheel spins in the wrong direction.

Can someone see what's wrong?

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")

}

}

EDIT

I simplified things with a UIPanGestureRecognizer and want to share the results:

 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

}

}

网友答案:

Introduce a method to find out which side was touched:

func touch(_ touch:UITouch, isInLeftHalfOf view: UIView) -> Bool {
    let positionInView = touch.location(in: view)

    return positionInView.x < view.frame.midX
}

and invert the rotation if it is not the left side:

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)
}

Even this fixes the situation mentioned in your question (swipe on the right side of the image in the down direction), you most probably need at least some fine tuning, but you get the idea.

Result:

相关阅读:
Top