怎样运用swiftUI制作进度条
这个标题名起完之后,我就觉得小了,格式小了!由于在本篇文章的内容里,不仅仅有一个绘图的功用,还会交叉一下比较实用的开发tips。
本文代码来自于我个人研发的满满财表,感兴趣的小伙伴能够去下载一个体验一下,针关于个人的财政管理东西。
终究作用
拆解问题
这是咱们惯用的解题策略,一旦遇到一个相对复杂的问题时,算法题也好,数学题也好,程序开发也好,首先要想到的一定是化繁为简。
假如一个复杂问题,不能被拆解成为若干个或者是单个重复的简略问题,那么,咱们的思路一定是错的。
上面这句话并非毫无根据的主观臆断,而是遵从于傅立叶改换的思想办法。
因而第一步,咱们先将终究作用的款式拆分一下,首先是环形进度条,与之前的风格相同,不废话直接上代码:
struct XXRingShape: Shape {
var progress: Double = 0.0
var size: CGSize
var animatableData: Double {
get { progress }
set { progress = newValue }
}
func path(in rect: CGRect) -> Path {
var path = Path()
let center = CGPoint(x: size.width/2, y: size.height/2)
let radius: CGFloat = (size.width - XXCircleProgressBar.spacing)/2
path.addArc(center: center,
radius: radius,
startAngle: .degrees(-90),
endAngle: .degrees(-90 + (360 * progress)),
clockwise: false)
return path
}
}
其间的Shape能够理解为是Core Animation
框架中的CAShapeLayer,假如不清楚CAShapeLayer是什么的,也没有问题,CAShapeLayer就像是一个画家,能够用来制作和烘托2D形状。它能够创立复杂的形状,如圆形、矩形和曲线,并供给了许多特点来控制形状的外观和行为。它还能够与动画一同运用,以创立流通的过渡作用,要结合path运用。
path的每个参数都非常简略,因而就不过多介绍了,这里需求留意的是animatableData这个特点。
Shape需求运用animatableData才干履行动画是由于Shape是根据途径制作的,而animatableData能够帮助咱们控制途径的改变,然后完成Shape的动画作用。
在往上一层,在运用XXRingShape的时分,需求清晰给它一个size,那么怎样给它一个清晰的尺寸呢?
运用GeometryReader
之前挖了一个坑,在说到List上拉下拉的时分,咱们提过,想要知道父视图的矩形框,swiftUI为咱们供给了GeometryReader
用来解决此类问题。
那么,应该怎样运用呢?上代码:
struct XXCircleProgressBar: View {
var progress: Double
var color: Color
@State var scaleValue: Double = 0
static let spacing: Double = 8
var lineWidth: Double
init(progress: Double, color: Color, lineWidth: Double) {
self.progress = progress
self.color = color
self.lineWidth = lineWidth
}
var body: some View {
GeometryReader { geometry in
ZStack() {
XXRingShape(progress: 1, size: geometry.size)
.stroke(style: StrokeStyle(lineWidth: lineWidth, lineCap: .round, lineJoin: .round))
.foregroundColor(color.opacity(0.2))
XXRingShape(progress: scaleValue, size: geometry.size)
.stroke(style: StrokeStyle(lineWidth: lineWidth, lineCap: .round, lineJoin: .round))
.foregroundColor(color)
.animation(.easeOut(duration: 1), value: scaleValue)
.onAppear {
scaleValue = progress
}
.onDisappear {
scaleValue = 0
}
.frame(width: geometry.size.width, height: geometry.size.width)
}
}
}
}
GeometryReader
就像是一个丈量东西,用来丈量你的swiftUI父视图的大小和方位,帮助你更好地理解和布局你的应用程序。其间的geometry能够理解为是你的父视图,经过对他的大小计算,然后确认包内视图的大小及方位。
一旦咱们需求准确布局,完成一些自定义的动画作用,上拉下拉等作用,就离不开运用GeometryReader
。
在第一篇 《swiftUI总结》 中咱们提到了特点修饰器– @propertyWrapper,也简略地讲述了conbime
是什么。而@State
便是swiftUI供给的,用来观察特点改变的特点修饰器。swiftUI中的@State
修饰符用于在视图中声明和管理状况。与其他修饰符不同,@State
是用于管理视图内部状况的修饰符。能够运用@State
来声明变量,并且能够在视图中运用它们。这些变量的值能够更改,并且当值更改时,视图将主动从头制作。
完好代码
没错,历来不拿你们当外人,直接把开头代码的作用代码甩出来:
import SwiftUI
extension Color {
init(hexString: String) {
let hex = hexString.trimmingCharacters(in: CharacterSet.alphanumerics.inverted)
var int = UInt64()
Scanner(string: hex).scanHexInt64(&int)
let r, g, b: UInt64
switch hex.count {
case 3: // RGB (12-bit)
(r, g, b) = ((int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17)
case 6: // RGB (24-bit)
(r, g, b) = (int >> 16, int >> 8 & 0xFF, int & 0xFF)
case 8: // ARGB (32-bit)
(r, g, b) = (int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF)
default:
(r, g, b) = (0, 0, 0)
}
self.init(red: Double(r) / 255, green: Double(g) / 255, blue: Double(b) / 255)
}
}
public struct XXChartTargetCircleModel {
var outProgress: Double = 0.3
var midProgress: Double = 0.2
var inProgress: Double = 0.5
var progress: Int = 81
let outColor: Color = Color(hexString: "#1F8A70")
let midColor: Color = Color(hexString: "#BFDB38")
let inColor: Color = Color(hexString: "#FC7300")
var target: String = ""
}
struct XXAimCircleView: View {
@Environment(.colorScheme) var colorScheme: ColorScheme
var title: String
var data: XXChartTargetCircleModel
public var formSize:CGSize
@State var progress: Int = 0
init(title: String, data: XXChartTargetCircleModel) {
self.title = title
self.data = data
self.formSize = CGSize(width: 180, height: 180)
}
var body: some View {
ZStack {
Rectangle()
.fill(Color(hexString: "#F5F5F5"))
.cornerRadius(20)
VStack() {
ZStack {
XXCircleProgressBar(progress: data.outProgress, color: data.outColor, lineWidth: 16)
.frame(width: self.formSize.width - 30,
height: self.formSize.width - 30)
XXCircleProgressBar(progress: data.midProgress, color: data.midColor, lineWidth: 12)
.frame(width: self.formSize.width - 66,
height: self.formSize.width - 66)
XXCircleProgressBar(progress: data.inProgress, color: data.inColor, lineWidth: 10)
.frame(width: self.formSize.width - 94,
height: self.formSize.width - 94)
HStack(alignment: .lastTextBaseline) {
Text("(progress)")
.foregroundColor(.black)
.font(.title3) + Text("%")
.foregroundColor(.black)
.font(.subheadline)
}
.onAppear {
Timer.scheduledTimer(withTimeInterval: 0.02, repeats: true) { timer in
if progress < Int(data.progress) {
progress += 1
} else {
timer.invalidate()
}
}
}
}
.padding([.top])
HStack {
Text("(Image(systemName: "target"))")
Spacer()
Text("(title)挑战")
.font(.bold(.body)())
.foregroundColor(.black)
}
.padding([.bottom, .leading, .trailing])
}
}.frame(minWidth:self.formSize.width,
maxWidth:self.formSize.width,
minHeight:self.formSize.height,
maxHeight:self.formSize.height)
}
}
struct XXCircleProgressBar: View {
var progress: Double
var color: Color
@State var scaleValue: Double = 0
static let spacing: Double = 8
var lineWidth: Double
init(progress: Double, color: Color, lineWidth: Double) {
self.progress = progress
self.color = color
self.lineWidth = lineWidth
}
var body: some View {
GeometryReader { geometry in
ZStack() {
XXRingShape(progress: 1, size: geometry.size)
.stroke(style: StrokeStyle(lineWidth: lineWidth, lineCap: .round, lineJoin: .round))
.foregroundColor(color.opacity(0.2))
XXRingShape(progress: scaleValue, size: geometry.size)
.stroke(style: StrokeStyle(lineWidth: lineWidth, lineCap: .round, lineJoin: .round))
.foregroundColor(color)
.animation(.easeOut(duration: 1), value: scaleValue)
.onAppear {
scaleValue = progress
}
.onDisappear {
scaleValue = 0
}
.frame(width: geometry.size.width, height: geometry.size.width)
}
}
}
}
struct XXRingShape: Shape {
var progress: Double = 0.0
var size: CGSize
var animatableData: Double {
get { progress }
set { progress = newValue }
}
func path(in rect: CGRect) -> Path {
var path = Path()
let center = CGPoint(x: size.width/2, y: size.height/2)
let radius: CGFloat = (size.width - XXCircleProgressBar.spacing)/2
path.addArc(center: center,
radius: radius,
startAngle: .degrees(-90),
endAngle: .degrees(-90 + (360 * progress)),
clockwise: false)
return path
}
}
struct XXAimCircleView_Previews: PreviewProvider {
static var previews: some View {
XXAimCircleView(title: "今天", data: XXChartTargetCircleModel())
}
}
结束语
近期chatGPT的爆火,让一个有趣的问题又从头进入人们的视界——AI是否能够代替人类作业?
这个问题看看chatGPT是怎样答复的。
很巧的是,在我问它这个问题之前,我能想到的答案与它的答复大致相同。没错,随着科技的开展,咱们的生产东西的确获得一日千里的前进,因而,一些东西威胁论会发生也是非常天然的或许,人类关于自己无法控制的力量总是充满敬畏。
但我相信,斧子不会自己杀人,能杀死人的只要会用斧子的人。
所以,与其担心会不会斧子砍死,不如先学会运用斧子。
最后,感谢一切为了科学前进,为了社会前进,为了人类前进的伟大科学家。Respect!!!
封面是运用Midjourney制作。再次感谢那些一切推动科技开展的人!