SwiftUI Shape 形狀與 Path 繪圖教學
SwiftUI 內建了許多基本形狀,如 Rectangle, Circle, Capsule 等。如果你需要更複雜的圖形或圖表,可以使用 Path 來自行繪製,甚至加上漸層與動畫。
1. 內建形狀 (Built-in Shapes)
形狀本身就是 View,可以套用 fill (填充) 或 stroke (邊框) 修飾符。
VStack {
// 1. 圓形
Circle()
.fill(.blue)
.frame(width: 50, height: 50)
// 2. 膠囊形 (常用於按鈕)
Capsule()
.fill(.green)
.frame(width: 100, height: 40)
// 3. 圓角矩形 (帶有漸層)
RoundedRectangle(cornerRadius: 15)
.fill(
LinearGradient(
colors: [.yellow, .orange],
startPoint: .topLeading,
endPoint: .bottomTrailing
)
)
.frame(width: 100, height: 100)
}
2. 邊框樣式 (StrokeStyle)
你可以透過 StrokeStyle 設定虛線、端點樣式等。
Circle()
.stroke(
.red,
style: StrokeStyle(
lineWidth: 5,
lineCap: .round, // 線條端點圓滑
dash: [10, 5] // 虛線:畫 10 點,空 5 點
)
)
.frame(width: 100)
3. Path (自由繪圖)
Path 類似於 Core Graphics 的操作,你可以移動畫筆、畫線、畫曲線。
Path { path in
// 1. 移動起點
path.move(to: CGPoint(x: 200, y: 100))
// 2. 畫直線
path.addLine(to: CGPoint(x: 100, y: 300))
path.addLine(to: CGPoint(x: 300, y: 300))
// 3. 閉合路徑 (自動連回起點)
path.closeSubpath()
}
.fill(.purple.opacity(0.5))
貝茲曲線與圓弧 (Curve & Arc)
Path { path in
path.move(to: CGPoint(x: 50, y: 100))
// 畫一條二次貝茲曲線 (Quad Curve)
path.addQuadCurve(
to: CGPoint(x: 250, y: 100),
control: CGPoint(x: 150, y: 0) // 控制點在上方,形成拱門
)
}
.stroke(.black, lineWidth: 3)
4. 自定義形狀 (Shape Protocol)
如果你想要製作一個可響應尺寸 (Responsive) 的形狀,應該遵循 Shape 協議。這樣你的圖形就能根據 rect 的大小自動縮放。
範例:波浪形狀 (Wave)
struct Wave: Shape {
func path(in rect: CGRect) -> Path {
var path = Path()
// 起點:左上角
path.move(to: CGPoint(x: 0, y: 0))
// 畫波浪到右側
path.addQuadCurve(
to: CGPoint(x: rect.maxX, y: 0),
control: CGPoint(x: rect.midX, y: rect.height) // 控制點在下方
)
// 連接底部,形成封閉區塊以便填充
path.addLine(to: CGPoint(x: rect.maxX, y: rect.height))
path.addLine(to: CGPoint(x: 0, y: rect.height))
path.closeSubpath()
return path
}
}
// 使用
Wave()
.fill(.cyan)
.frame(height: 100) // 高度 100,寬度自動填滿
.ignoresSafeArea() // 常用於頂部背景
5. 圖形動畫 (Trim)
trim 修飾符可以只畫出路徑的一部份,配合動畫可以製作出「畫圓」或「進度條」的效果。
struct ProgressCircle: View {
@State private var progress: CGFloat = 0.0
var body: some View {
ZStack {
// 背景圓環
Circle()
.stroke(.gray.opacity(0.2), lineWidth: 10)
// 進度圓環
Circle()
.trim(from: 0, to: progress) // 只畫出 0 到 progress 的部分
.stroke(
.blue,
style: StrokeStyle(lineWidth: 10, lineCap: .round)
)
.rotationEffect(.degrees(-90)) // 從 12 點鐘方向開始
.animation(.easeOut(duration: 1.0), value: progress)
}
.frame(width: 100, height: 100)
.onAppear {
progress = 0.8 // 動畫演示
}
}
}