william-weng/wwcircularrangepickerui
✨ [功能特色](https://peterpanswift.github.io/iphone-bezels/)
- 支援雙把手的圓形區間選擇。
- 使用純 SwiftUI 實作。
- 支援自訂起點與終點把手視圖。
- 提供預設把手圖示,方便快速整合。
- 透過 closure 回傳選取的 tick index。
- 支援負角度、360 度循環與正規化顯示。
- 選取弧線支援
Shape+AnimatablePair動畫。
📦 安裝方式
Swift Package Manager
在 Xcode 中加入套件:
- 打開你的專案。
- 選擇 File > Add Package Dependencies...。
- 貼上 repository URL。
- 將
WWCircularRangePickerUI加入你的 target。
🚀 基本用法
import SwiftUI
import WWCircularRangePickerUI
struct ContentView: View {
@State private var startAngle: Angle = .degrees(0)
@State private var endAngle: Angle = .degrees(90)
@State private var timeRangeText: String = ""
var body: some View {
ZStack {
Text(timeRangeText)
.font(.title)
WWCircularRangePickerUI(startAngle: $startAngle, endAngle: $endAngle) {
updateText(from: $0, to: $1)
}
.padding()
}
.padding()
}
}
private extension ContentView {
func calculateTime(at index: Int) -> String {
let totalMinutes = 10 * index
let hour = totalMinutes / 60
let minute = totalMinutes % 60
return String(format: "%02d:%02d", hour, minute)
}
func updateText(from startIndex: Int, to endIndex: Int) {
let startTime = calculateTime(at: startIndex)
let endTime = calculateTime(at: endIndex)
timeRangeText = "\(startTime) ~ \(endTime)"
}
}🔁 回傳值
選擇器會以 tuple 的方式回傳選取範圍:
public typealias SelectIndex = (start: Int, end: Int)使用方式如下:
WWCircularRangePickerUI(startAngle: $startAngle, endAngle: $endAngle) { startIndex, endIndex in
print(startIndex, endIndex)
}🪄 預設初始化
如果你想快速開始,可以直接使用內建的把手視圖:
WWCircularRangePickerUI(
startAngle: $startAngle,
endAngle: $endAngle
) { startIndex, endIndex in
print(startIndex, endIndex)
}這個初始化器使用的預設配置大致如下:
Configure(
lineWidth: 44,
innerColor: .gray.opacity(0.2),
outerColor: .yellow.opacity(0.5),
tickCount: 72,
tickStride: 6
)🎛️ 客製化把手
你可以自訂起點與終點把手視圖:
WWCircularRangePickerUI(
configure: configure,
startAngle: $startAngle,
endAngle: $endAngle,
startView: Image(systemName: "moon.fill"),
endView: Image(systemName: "alarm.fill")
) { startIndex, endIndex in
print(startIndex, endIndex)
}⚙️ 配置說明
選擇器透過 Configure 進行設定。
let configure = WWCircularRangePickerUI<EmptyView, EmptyView>.Configure(
lineWidth: 44,
innerColor: .gray.opacity(0.2),
outerColor: .yellow.opacity(0.5),
tickCount: 72,
tickStride: 6
)常用參數
lineWidth:圓弧線寬度。innerColor:底層圓環顏色。outerColor:選取區間顏色。tickCount:圓盤總刻度數。tickStride:主要刻度間距。stepAngle:每個刻度對應的角度,通常由tickCount推導。
🧭 角度行為
這個元件是以圓形互動為核心,所以角度處理很重要。
- 角度會被正規化到
0..<360。 - 支援負角度。
- 超過
360的值會自動循環回圓盤內。 - 選取 index 可以透過四捨五入對應到最近的刻度。
- 初始 callback 建議在
.onAppear中觸發,而不是在init中執行。
⚠️ 注意事項
onAppear比init更適合處理第一次 callback,因為 SwiftUI 的 View initializer 應盡量避免 side effect。onChange可以用來監聽startAngle和endAngle的後續變動。- 如果你要把每個 tick 對應成時間,建議把轉換邏輯放在元件外部,讓 picker 保持可重用性。
⏱️ 時間範例
如果每個 tick 代表 10 分鐘,你可以把 index 轉成可讀時間字串:
func calculateTime(at index: Int) -> String {
let totalMinutes = 10 * index
let hour = totalMinutes / 60
let minute = totalMinutes % 60
return String(format: "%02d:%02d", hour, minute)
}再搭配 callback 組合成區間文字:
func updateText(from startIndex: Int, to endIndex: Int) {
let startTime = calculateTime(at: startIndex)
let endTime = calculateTime(at: endIndex)
timeRangeText = "\(startTime) ~ \(endTime)"
}👀 預覽
#Preview {
ContentView()
}Package Metadata
Repository: william-weng/wwcircularrangepickerui
Default branch: main
README: README.md