Я пытаюсь воссоздать кольца Apple Workout Rings в своем приложении WatchOS. Я использую SpriteKit и GameScene для анимации. Однако я не могу понять, как реализовать перекрывающиеся кольца и включить градиент.
Я попытался использовать SKShader, чтобы включить эффект градиента. Однако SKShapeNode игнорирует окончание строки, когда присутствует SKShader, поэтому я не могу получить закругленные края.
Я также рассмотрел другие подходы, такие как: Circle Progress View как приложение активности
Однако я не знаю, как использовать этот подход для watchOS, поскольку SpriteKit работает с концепцией узлов, а этот подход имеет дело с CGContext.
class GameScene: SKScene {
func circle(radius:CGFloat, percent:CGFloat) -> CGPath {
let start:CGFloat = 0
let end = ((CGFloat.pi * 2)) * percent
let center = CGPoint.zero
let corePath = CGMutablePath()
corePath.addArc(center: center, radius: radius, startAngle: start, endAngle: end, clockwise: true)
return corePath
}
// Animated Timer for Progress Circle
func countdownCircle(circle:SKShapeNode, steps:Int, duration:TimeInterval, completion:@escaping ()->Void) {
guard let path = circle.path else {
return
}
let radius = path.boundingBox.width/2
let timeInterval = duration/TimeInterval(steps)
let increment = 1 / CGFloat(steps)
var percent = CGFloat(1.0)
let animate = SKAction.run {
percent -= increment
circle.path = self.circle(radius: radius, percent:percent)
}
let wait = SKAction.wait(forDuration:timeInterval)
let action = SKAction.sequence([wait, animate])
run(SKAction.repeatForever(action)) {
self.run(SKAction.wait(forDuration:timeInterval/2)) {
circle.path = nil
completion()
}
//(action,count:steps-1)
}
}
// Animated Timer for Shadow Circle
func countdownShadow(circle:SKShapeNode, steps:Int, duration:TimeInterval, completion:@escaping ()->Void) {
guard let path = circle.path else {
return
}
let radius = path.boundingBox.width/2
let timeInterval = duration/TimeInterval(steps)
let increment = 1 / CGFloat(steps)
var percent = CGFloat(1.0)
let animate = SKAction.run {
percent -= increment
circle.path = self.circle(radius: radius, percent:percent)
}
let wait = SKAction.wait(forDuration:timeInterval)
let action = SKAction.sequence([wait, animate])
run(SKAction.repeatForever(action)) {
self.run(SKAction.wait(forDuration:timeInterval)) {
circle.path = nil
completion()
}
}
}
//(action,count:steps-1)
override func sceneDidLoad() {
let pathForCircle = CGMutablePath()
pathForCircle.addArc(center: CGPoint.zero, radius: 100, startAngle: 0, endAngle: CGFloat(Double.pi * 2), clockwise: true)
// This is the circle that indicates the progress.
let progressCircle = SKShapeNode()
progressCircle.lineCap = .round
progressCircle.position = CGPoint(x: 0, y: 15)
progressCircle.strokeColor = SKColor.green
progressCircle.lineWidth = 20
progressCircle.path = pathForCircle
progressCircle.zPosition = 4
self.addChild(progressCircle)
countdownCircle(circle: progressCircle, steps: 400, duration: 5){
print("Done")
}
// This is the circle that gives the ring the shadow effect.
let shadowCircle = SKShapeNode()
shadowCircle.lineCap = .round
shadowCircle.position = CGPoint(x: 0, y: 15)
shadowCircle.strokeColor = SKColor.black
shadowCircle.glowWidth = 30
shadowCircle.zPosition = 3
shadowCircle.path = pathForCircle
self.addChild(shadowCircle)
countdownShadow(circle: shadowCircle, steps: 400, duration: 5){
print("Done")
}
// This is the bottommost circle.
let bottommostCircle = SKShapeNode()
bottommostCircle.position = CGPoint(x: 0, y: 15)
bottommostCircle.lineCap = .butt
bottommostCircle.strokeColor = SKColor.green
bottommostCircle.alpha = 0.2
bottommostCircle.lineWidth = 20
bottommostCircle.path = pathForCircle
bottommostCircle.zPosition = 0
self.addChild(bottommostCircle)
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
}
}