新しいAIアプリを開発しています。そして、進行状況(またはスコア)を表示するためにSwiftUIで半円のプログレスリングを作成しました。
これは円形の要素を使用しますが、円の上半分のみが表示されるようにクロップします。
SwiftUIで.trimビューモディファイアを使用して、円の上半分だけを表示しました。しかし、円のフレームは変わらず、そのため円の下に白いスペースが存在するかもしれません。
これは、円をより小さいコンテナ上にオーバーレイすることで解決できますが、この記事ではそのままのコードを共有します。
import Foundation
import SwiftUI
public struct HalfCircularProgressRing: View {
private var progressPercentageToShow: Double
@State private var currentProgressValue: Float = 0
private let timer = Timer.publish(every: 0.01, on: .main, in: .common).autoconnect()
static let circleTrimStartPosition: CGFloat = 0.3
static let circleTrimEndPositon: CGFloat = 0.9
static let colorfulGradientPattern: AngularGradient = .init(gradient: Gradient(stops: [
.init(color: .red, location: 0.4),
.init(color: .orange, location: 0.5),
.init(color: .yellow, location: 0.6),
.init(color: .green, location: 0.7),
.init(color: .blue, location: 0.8)
]), center: .center)
public init(progressPercentageToShow: Double) {
self.progressPercentageToShow = progressPercentageToShow
}
public var body: some View {
ZStack {
// background circle
Circle()
.trim(from: Self.circleTrimStartPosition, to: Self.circleTrimEndPositon)
.stroke(style: .init(lineWidth: 12, lineCap: .round, lineJoin: .round))
.opacity(0.3)
.foregroundColor(Color.gray)
.rotationEffect(.degrees(55))
// progress circle
Circle()
.trim(from: Self.circleTrimStartPosition, to: Self.circleTrimStartPosition + CGFloat(currentProgressValue) * (Self.circleTrimEndPositon - Self.circleTrimStartPosition))
.stroke(style: .init(lineWidth: 12, lineCap: .round, lineJoin: .round))
.fill(Self.colorfulGradientPattern)
.rotationEffect(.degrees(55))
// progress text
Text("\(Int(progressPercentageToShow * 100))%")
.font(.system(size: 60))
.bold()
}
.frame(width: 250, height: 250)
.onReceive(timer) { _ in
withAnimation {
let convertedProgressPercentage = Float(progressPercentageToShow)
if currentProgressValue < convertedProgressPercentage {
currentProgressValue = min(currentProgressValue + 0.006, convertedProgressPercentage)
} else if currentProgressValue > convertedProgressPercentage {
currentProgressValue = max(currentProgressValue - 0.006, convertedProgressPercentage)
}
}
}
}
}
struct HalfCircularProgressRing_Previews: PreviewProvider {
struct PreviewContainer: View {
@State private var currentProgress: Double = 0.8
var body: some View {
VStack {
HalfCircularProgressRing(progressPercentageToShow: currentProgress)
Stepper("Value is \(Int(currentProgress * 100))%", value: $currentProgress, in: 0...1, step: 0.01)
Spacer()
}
.padding()
}
}
static var previews: some View {
PreviewContainer()
}
}