我有两层。如图所示,一层绘制了半透明路径。将渐变层绘制到已触摸圆的点。每个图层均使用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
}
}
我希望上层被逐渐增加的动画填充到接触点。 请帮忙。