【SwiftUI】swift自定义一个指针仪表盘,自定义Path绘制。

看看效果:

IMG_1242

最开始的时候使用Path绘制的弧形曲线填充渐变,后来圆角拐点的时候老是有问题,就换了一个实现方式,使用曲线绘制给曲线末端设置圆角就ok了。

1.思路:使用ZStack一层层叠加后,设置同心圆,添加一个指针图片与当前角度绑定一起,这个角度也影响弧形进度的角度。叠加两个进度,一个是默认的灰色,角度180,另一个是当前进度随之下面的slider的改变而改变,画进度条后,不用Fill,先绘制一条弧线,然后给线添加一个边(Stroke),这个边的大小就是进度的厚度,边的颜色就是进度条的颜色。

代码:有注释

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
//
// ContentView.swift
// swiftUiDemo 指针仪表盘
// http://blog.borebooks.top
// Created by IWH718 on 2020/10/20.
//

import SwiftUI

struct ContentView: View {

let leftG1 = 1...10

@State var index:CGFloat = 0

let colors = Gradient(colors: [Color.yellow,Color.red,Color.orange ])



var body: some View {

VStack{
Text("SwiftUI 🌰").font(.title).bold().padding()

//仪表盘容器
ZStack{
//同心圆
Group{
Circle().stroke(Color.gray).frame(width:220,height:220)
Circle().fill(Color.white).frame(width:180,height:180)
Circle().fill(Color.blue).frame(width:80,height:80)
Circle().fill(Color.white).frame(width:60,height: 60)
Circle().fill(Color.blue).frame(width:40,height: 40)
}
Group{
//左侧仪表点
ForEach(leftG1,id:\.self){i in
Text("·") .frame(width:10,height:10)
.offset( x: 0, y: -70) .rotationEffect(.init(degrees: Double(i * 9 ) * -1 ))
.foregroundColor( (Double(i * 9 ) * -1) == Double(index * 9) ? Color.blue :Color.gray )
}
}
Group{
//右侧仪表点
ForEach(leftG1,id:\.self){i in
Text("·").frame(width:10,height:10)
.offset( x: 0, y: -70) .rotationEffect(.init(degrees: Double(i * 9) ))
.foregroundColor( (Double(i * 9 ) ) == Double(index * 9) ? Color.blue :Color.gray )
}
}

//仪表数据
VStack{
Text("\(Int(index))")
Text("value")
}.offset(x: 0, y: 80).foregroundColor(Color.gray).font(.system(size: 13))

GeometryReader{
proxy in
//进度条背景
Path{ path in
path.move(to: CGPoint(x:proxy.size.width / 2 - 90 , y:proxy.size.height / 2))
//绘制上弧形
path.addArc(center: CGPoint(x:proxy.size.width / 2 ,y:proxy.size.height / 2 ), radius: CGFloat(90), startAngle: Angle.init(degrees: -180), endAngle: Angle.init(
degrees: Double(0) ), clockwise: false)

}
.stroke(Color.gray,style: StrokeStyle(lineWidth: 15, lineCap: .round))

//进度条
Path{ path in
path.move(to: CGPoint(x:proxy.size.width / 2 - 90 , y:proxy.size.height / 2))
//绘制上弧形
path.addArc(center: CGPoint(x:proxy.size.width / 2 ,y:proxy.size.height / 2 ), radius: CGFloat(90), startAngle: Angle.init(degrees: -180), endAngle: Angle.init(
degrees: Double((-1 * (180 - index * 9))) ), clockwise: false)

}
.stroke(LinearGradient(gradient: colors,startPoint: .topLeading,endPoint: .bottomTrailing),style: StrokeStyle(lineWidth: 15, lineCap: .round))

}.frame(width:220,height:220)
//指针
Image("arrow").resizable().frame(width:60,height:60).offset(x: 0, y: -16)
.rotationEffect(.init(degrees: Double(index * 9) - 90 )).animation(.linear)

}.padding()

Slider(value: $index, in: 0...19, step:1){_ in
withAnimation{
index += 1
}
}.padding()

}




}
}

struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}