在本章中,你将学会使用Shape形状和Animation
动画创建一个圆形进度条。
如果你恰巧拥有一块AppleWatch
,你一定会注意到AppleWatch
上的运动圆环,在你走路、呼吸、爬楼三个方面记录你每天的运动量。
AppleWatch
通过圆环进度条的方式告知用户运动进度情梯度公式况,当你达标后,AppleWatch
三个圆环就会闭合。初始化失败是怎么解决
那么本运动的好处章节,我们就尝试创建一个类似的圆形进度条。
项目创建
首先,创梯度下降法原理建一个新项目,命名为SwiftUIProgress
。
我们先来分析结构。
先来看一个圆,我们发现健身记录的圆环是由2个圆环层叠而运动健康成的,我们可以创建2个圆环,然后使用ZStack
叠加在一起。
在那之前,因为本次用到的颜色比较多,我们可以将颜色组抽离出来,然后在视图中直接引用,初始化电脑这样做可以使得代码更加简化。
颜色组
我们新建一个Swift
文件,命名为ColorExt.swift
。
import SwiftUI
extension Color {
public init(red: Int, green: Int, blue: Int, opacity: Double = 1.0) {
let redValue = Double(red) / 255.0
let greenValue = Double(green) / 255.0
let blueValue = Double(blue) / 255.0
self.init(red: redValue, green: greenValue, blue: blueValue, opacity: opacity)
}
public static let gradientPink = Color(red: 210, green: 153, blue: 194)
public static let gradientYellow = Color(red: 254, green: 249, blue: 215)
}
我们创建了appreciate一个init
方法,它接受r梯度下降法原理ed
红色、green
绿色和blue
蓝色的值。然后我们初始化Color
实例。
这里我们定义了两个颜色,一个是比较好看的粉色gradientPink
,运动的好处一个是比较好看的黄色gradientYellow
,我们待会儿要使用这两个颜色作为圆环的渐变颜色。
基础样式-外环
我们回到ContentView
主页中,先创建背部的圆环背景。
import SwiftUI
struct ContentView: View {
var thickness: CGFloat = 30.0
var width: CGFloat = 250.0
var body: some View {
ZStack {
Circle()
.stroke(Color(.systemGray6),lineWidth: thickness)
}
.frame(width: width, height: width, alignment: .center)
}
}
我们首梯度洗脱先定义了Circl梯度下降e
圆环的厚度thickappointmentness
和圆环的宽度width
,然后使用stroke
给圆环描边,边框加个了淡淡的systemGray6
灰色。
然后设置圆环的大运动小。这样我们就获梯度得了第一个圆环:背景圆环。
基础样式-内环
接下来,我们来完成内环,这是一个跟随进度变动的圆环,我们就不能直梯度下降法接用Circle
圆运动完多久可以洗澡形绘制。
还记得之前的章节appear,我们使用Shape
形状绘制各种各样的图形么,这里我们也使用Shape
形状的方法绘制内环。
我们创建一个新的结构体梯度稀释,命名为RingShape
。
//内环
struct RingShape: Shape {
var progress: Double = 0.0
var thickness: CGFloat = 30.0
var startAngle: Double = -90.0
func path(in rect: CGRect) -> Path {
var path = Path()
path.addArc(center: CGPoint(x: rect.width / 2.0, y: rect.height / 2.0), radius: min(rect.width, rect.height) / 2.0,startAngle: .degrees(startAngle),endAngle: .degrees(360 * progress+startAngle), clockwise: false)
return path.strokedPath(.init(lineWidth: thickness, lineCap: .round))
}
}
我们创建了RingShape
结构体,它遵循Shape
协议。然后我们运动员和外环一样,定义了它的厚度thickness运动员
,另外还有内环的进度百分比progress
参数。
startAngle
开始角度为-90
,这是因为我们圆放在坐标轴上,它的开始点是圆右边中间的位置,而我们进度的圆环是从圆的顶部的顶点开始,所以startAngle
开始角度需要设置为-90
度。
内环的绘制我们使用add梯度下降法Arc
的方法,起始角度为0
,结束角度用360
度乘以prog运动品牌ress
进度的值,而且要加上startAngle
开始角度来计算。
最后返回画好的圆,我们可以在ContentView
中引运动世界校园用它看看效果。
ZStack {
//外环
Circle()
.stroke(Color(.systemGray6), lineWidth: thickness)
//内环
RingShape(progress: 0.3, thickness: thickness)
}
.frame(width: width, height: width, alignment: .center)
基础样式APP-渐变色
接下来,我们给我们的进度圆环附梯度下降法原理上渐变色Gradient
。
科普一个知识点。
AngularGradient
角梯度,AngularGradi初始化是什么意思ent
角梯度是SwiftUI
提供的一种绘制渐变色的方法,可以跟随不同角度变化,从起点到终点,颜色按顺时针运动员做扇形渐变
。
AngularGradient(gradient: Gradient(colors: [.gradientPink, .gradientYellow]), center: .center, startAngle: .degrees(startAngle), endAngle: .degrees(360 * 0.3 + startAngle))
这里,我们在AngularGradient
角梯度的框架里,指定渲染颜色为渐变色,引用我们定义好的gradienappointmenttPink
粉色和gradientYellow
黄色。
渲染梯度开始角初始化失败是怎么解决度为startAngle
定义的开始角度,结束角度为360 * 0.3 + startAng运动会口号le
,从开始角度开始。
然后我们使用.fil
修饰符,将AngularGradient
角梯度赋予Rin梯度下降法原理gShap初始化磁盘e
内环。
非常不错!
动画效果
接下来,我们来实现下动画效果。
设置3个进度来展示进初始化游戏启动器失败度:0%、50%、100%
,当我们appearance的进度从0%
到50%
时,我们可以看到进度条内环从0~50度
的全过程。
我们先定义好初始的进度,替换我们固定的进度值:
@State var progress = 0.0
然后,我们完成下进度值的选择。
//进度调节
HStack {
Group {
Text("0%")
.font(.system(.headline, design: .rounded))
.onTapGesture {
self.progress = 0.0
}
Text("50%")
.font(.system(.headline, design: .rounded))
.onTapGesture {
self.progress = 0.5
}
Text("100%")
.font(.system(.headline, design: .rounded))
.onTapGesture {
self.progress = 1.0
}
}
.padding()
.background(Color(.systemGray6)).clipShape(RoundedRectangle(cornerRadius: 15.0, style: .continuous))
.padding()
}
.padding()
我们设置了3
个进度值调节,当我们点击0%
的进度值时,进度progress
赋值0
,appetite同理,我们完成了3
个进度值选择。
我们将整个进度调节和内外环视图使用VStack
垂直排布。
同时,我们在内外环的组合视图中增加Animation
动画。
.animation(Animation.easeInOut(duration: 1.0),value: progress)
嗯?好像出了点问题,我们梯度下降法发现内环的Animation
动画好像不起效果,进度加载仍旧很生硬
。
这是因为我们在实现RingShape
内环构建的过程中,它符合Shape
协议,而恰巧是Shape
协议它有一个默认
的动画,也就是没有数据的动画。
因此在ContentView
中,我们怎么加Animation
动画效果都没有作用。
要解决这个问题也很简单,我们只需要在构建Ring梯度公式Shape
内环时,赋予proapprovegress
新的值就可以了。
var animatableData: Double {
get { progress }
set { progress = newValue }
}
这样,我们就实现了内环进度的Animation
动画效果。
恭喜你,完成了本章的所有练apple习~
完整代码
import SwiftUI
struct ContentView: View {
var thickness: CGFloat = 30.0
var width: CGFloat = 250.0
var startAngle = -90.0
@State var progress = 0.0
var body: some View {
VStack {
ZStack {
// 外环
Circle()
.stroke(Color(.systemGray6), lineWidth: thickness)
// 内环
RingShape(progress: progress, thickness: thickness)
.fill(AngularGradient(gradient: Gradient(colors: [.gradientPink, .gradientYellow]), center: .center, startAngle: .degrees(startAngle), endAngle: .degrees(360*0.3 + startAngle)))
}
.frame(width: width, height: width, alignment: .center)
.animation(Animation.easeInOut(duration: 1.0),value: progress)
//进度调节
HStack {
Group {
Text("0%")
.font(.system(.headline, design: .rounded))
.onTapGesture {
self.progress = 0.0
}
Text("50%")
.font(.system(.headline, design: .rounded))
.onTapGesture {
self.progress = 0.5
}
Text("100%")
.font(.system(.headline, design: .rounded))
.onTapGesture {
self.progress = 1.0
}
}
.padding()
.background(Color(.systemGray6)).clipShape(RoundedRectangle(cornerRadius: 15.0, style: .continuous))
.padding()
}
.padding()
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
// 内环
struct RingShape: Shape {
var progress: Double = 0.0
var thickness: CGFloat = 30.0
var startAngle: Double = -90.0
var animatableData: Double {
get { progress }
set { progress = newValue }
}
func path(in rect: CGRect) -> Path {
var path = Path()
path.addArc(center: CGPoint(x: rect.width / 2.0, y: rect.height / 2.0), radius: min(rect.width, rect.height) / 2.0,startAngle: .degrees(startAngle),endAngle: .degrees(360 * progress + startAngle), clockwise: false)
return path.strokedPath(.init(lineWidth: thickness, lineCap: .round))
}
}
快来动手试试吧!
如果本专栏对你有帮助,不妨点赞、评论、关注~APP