圆形动画,可增加/减少触摸Swift 4

时间:2018-07-03 04:52:06

标签: ios swift core-animation

The View I want to create

我有两层。如图所示,一层绘制了半透明路径。将渐变层绘制到已触摸圆的点。每个图层均使用UIBezierPath绘制。

这是代码

import UIKit

@objc public protocol RingLevelDelegate {
    func selectedLevel(level: Int)
}

@objc  class Ring: UIView {
    var mTransletX: CGFloat?
    var mTransletY: CGFloat?
    let startAngle = (3*Double.pi)/4;
    let endAngle = (Double.pi)/4;
    var midpoint: CGPoint?
    let desiredLineWidth:CGFloat = 20   // desired stroke with
    var arcRadious: CGFloat?
    //    var mAngle = UserDefaults.standard.double(forKey: RING_GAME_ANGLE);                  // initial value of progress
    var mAngle = 2.46;
    var prevLevel = 0.0
    let progressLayer = CAShapeLayer()
    var i = 0
    var finalAngle = (3*Double.pi)/4;
    @objc var delegate: RingLevelDelegate?




override func draw(_ rect: CGRect)
{
    if mAngle == 0.0 {
        //initial value of user defaults is 0.0; it will show level 18.. show level 1
        mAngle = 2.46
    }
    let level = getLevel(angle: mAngle)
    print("level = ", level)

    drawRingFittingInsideView(mAngle: mAngle)
    //startCircleAnimation()
    //        prepareForEditing(editing: true)
}

@objc public func drawRingFittingInsideView(mAngle: Double)->()
{
    layer.sublayers?.forEach { $0.removeFromSuperlayer() }
    let halfSize:CGFloat = min( bounds.size.width/2, bounds.size.height/2)
    let center = CGPoint(x:halfSize + 10,y:halfSize)
    arcRadious = ((frame.size.width/2) - 42)
    let circlePath = UIBezierPath(
        arcCenter: center,
        radius: arcRadious!,
        startAngle: CGFloat(startAngle),
        endAngle:CGFloat(endAngle),
        clockwise: true)

    let progressPath = UIBezierPath(
        arcCenter: center,
        radius: arcRadious!,
        startAngle: CGFloat(startAngle),
        endAngle:CGFloat(mAngle),
        clockwise: true)
    print("Drawing the ARC")


    mTransletX = halfSize
    mTransletY = halfSize

    let shapeLayer = CAShapeLayer()


    shapeLayer.path = circlePath.cgPath
    progressLayer.path = progressPath.cgPath

    shapeLayer.fillColor = UIColor.clear.cgColor
    shapeLayer.strokeColor = UIColor.red.cgColor
    shapeLayer.lineWidth = desiredLineWidth
    shapeLayer.lineCap = "round"
    shapeLayer.cornerRadius = desiredLineWidth / 2

    let color0 = UIColor.init(red: 0/255, green: 255/255, blue: 239/255, alpha: 0.1)
    let color1 = UIColor.init(red: 0/255, green: 250/255, blue: 239/255, alpha: 0.1)
    let color2 = UIColor.init(red: 253/255, green: 112/255, blue: 138/255, alpha: 0.1)
    let color3 = UIColor.init(red: 255/255, green: 101/255, blue: 76/255, alpha: 0.1)
    let gradient = setGardienButtonBackground(colorZero: color0, colorOne: color1, colorTwo: color2, colorThree: color3)
    gradient.mask = shapeLayer

    let pcolor0 = UIColor.init(red: 0/255, green: 255/255, blue: 239/255, alpha: 0.6)
    let pcolor1 = UIColor.init(red: 0/255, green: 255/255, blue: 255/255, alpha: 0.4)
    let pcolor2 = UIColor.init(red: 253/255, green: 112/255, blue: 138/255, alpha: 0.5)
    let pcolor3 = UIColor.init(red: 255/255, green: 101/255, blue: 76/255, alpha: 1)
    let pgradient = setGardienButtonBackground(colorZero: pcolor0, colorOne: pcolor1, colorTwo: pcolor2, colorThree: pcolor3)
    pgradient.mask = progressLayer



    progressLayer.fillColor = UIColor.clear.cgColor
    progressLayer.strokeColor = UIColor.green.cgColor
    progressLayer.lineWidth = desiredLineWidth
    progressLayer.lineCap = "round"
    progressLayer.cornerRadius = desiredLineWidth / 2



    layer.addSublayer(gradient)
    layer.addSublayer(pgradient)
}



override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    //       if let touch = touches.first {
    for touch in touches {
        print("touchesBegan")
        let position = touch.location(in: self)
        print(position)
        if (ignoreTouch(position.x, position.y))
        {
            print("ignore touch")
        } else {
            print("touch on ring")
            //  startCircleAnimation()
            prevLevel = Double(getLevel(angle: mAngle))
            //                mAngle = ((2 * Double.pi) - getTouchInRad(position.x, position.y))
            //                setNeedsDisplay()

            print("mAngle  in rad = ", mAngle)

        }
    }

}



override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
    print("touchesMoved")
    let touch = touches.first
    let position = touch?.location(in: self)
    print(position)
    if (ignoreTouch((position?.x)!, (position?.y)!))
    {
        print("ignore touch")
    } else {
        //                print("touch on ring")
        //                startCircleAnimation()
        setNeedsDisplay()
        mAngle = ((2 * Double.pi) - getTouchInRad((position?.x)!, (position?.y)!))
        //            setNeedsDisplay()
    }
}



override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
    let touch = touches.first
    print("touchesEnded")


    for touch in touches {
        let position = touch.location(in: self)
        print(position)
        if (ignoreTouch(position.x, position.y))
        {
            print("ignore touch")
        } else {
            print("touch on ring")
            finalAngle = ((2 * Double.pi) - getTouchInRad(position.x, position.y))
            startCircleAnimation()
            setNeedsDisplay()
            print("mAngle  in rad = ", mAngle)

        }
    }
}



func ignoreTouch(_ xPos: CGFloat, _ yPos: CGFloat) -> Bool {
    var ignore = false

    let x = xPos - mTransletX!
    let y = yPos - mTransletY!
    let touchRadius = ((x * x) + (y * y)).squareRoot()
    let angle = (Double(atan2(y, x)) + Double.pi)
    print("****", angle)
    if ((touchRadius < (arcRadious! / 1.5)) || (touchRadius >  (arcRadious! + desiredLineWidth + 20))) {
        ignore = true
    }

    if  3.80 <= angle && angle <= 5.60  {
        ignore = true
    }

    return ignore;
}

func getTouchInRad(_ xPos: CGFloat, _ yPos: CGFloat) -> Double {
    var x = xPos - mTransletX!
    let y = yPos - mTransletY!
    x = -x
    let angle = (Double(atan2(y, x)) + Double.pi);
    return angle
}

func setGardienButtonBackground(colorZero:UIColor, colorOne: UIColor, colorTwo:UIColor, colorThree:UIColor)-> CAGradientLayer {

    let gradientLayer = CAGradientLayer()
    gradientLayer.frame = bounds
    gradientLayer.colors = [colorZero.cgColor, colorOne.cgColor, colorTwo.cgColor, colorThree.cgColor]
    //        gradientLayer.locations = [0, 0.2, 0.37, 1]
    gradientLayer.locations = [0, 0.22, 0.42, 1]
    gradientLayer.startPoint = CGPoint(x: 0, y: 0.5)
    gradientLayer.endPoint = CGPoint(x: 1, y: 0.5)

    return gradientLayer
}

//    private func startCircleAnimation() {
//        progressLayer.strokeEnd  = 1
//        let animation = CABasicAnimation(keyPath: "strokeEnd")
//        animation.fromValue = 0
//        animation.toValue = 1
//        animation.fillMode = kCAFillModeForwards
//        animation.duration = 0.5
//        progressLayer.add(animation, forKey: nil)
//    }



private func startCircleAnimation() {
    /*progressLayer.strokeEnd  = 1
     let animation = CABasicAnimation(keyPath: "strokeEnd")
     animation.fromValue = 0.4
     //        print("## prevangle =", prevLevel)
     animation.toValue = 1
     //        print("## mAngle = ", mAngle)
     animation.fillMode = kCAFillModeBoth
     animation.duration = 0.2
     progressLayer.add(animation, forKey: nil)*/


    // Animate the transition
    UIView.animate(withDuration:0.02, delay: 0, animations: {

        // Animations
        if self.mAngle > self.finalAngle{
            self.mAngle -= 0.2
        } else {
            self.mAngle += 0.2
            print(self.mAngle)
            self.setNeedsDisplay()
            print("animate")
        }

    }, completion: { finished in
        //if reached finalAngle
        print("complete")
        if self.mAngle < self.finalAngle {
            self.startCircleAnimation()
        }
        else {
            return;
        }
    })
}



func prepareForEditing(editing:Bool){

    progressLayer.strokeEnd  = 1
    let animation = CABasicAnimation(keyPath: "strokeEnd")
    animation.duration = 2

    // Your new shape here
    animation.toValue = mAngle
    animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)

    // The next two line preserves the final shape of animation,
    // if you remove it the shape will return to the original shape after the animation finished
    animation.fillMode = kCAFillModeRemoved
    //        animation.isRemovedOnCompletion = true
    progressLayer.add(animation, forKey: "strokeEnd")
}





func getLevel(angle: Double) -> Int {
    let levelArray = [2.46, 2.65, 2.90, 3.11, 3.32, 3.53, 3.74, 3.95, 4.16, 4.37, 4.58, 4.79, 5.0, 5.21, 5.42, 5.68, 5.90, 6.28, 0.0, 0.36, 0.58, 0.90]
    var j = 0
    for i in (0...17) {
        print(i, angle)
        if angle > levelArray[i] && angle < levelArray[i + 1]{
            mAngle = levelArray[i]
            print("mAngle = ", mAngle, " i = " , i)
            j = i + 1
        }
    }

    for i in (18...20) {
        if angle >= levelArray[i] && angle < levelArray[i + 1]{
            mAngle = levelArray[i]
            if angle >= 0.58 {
                mAngle = levelArray[i + 1]
            }
            print("mAngle = ", mAngle, " i = " , i)
            j = i
        }
    }
    if j > 0 {
        self.delegate?.selectedLevel(level: j)
        print("delgate sent is", j)
    }

    return j
}
}

我希望上层被逐渐增加的动画填充到接触点。 请帮忙。

0 个答案:

没有答案