10
6

More than 1 year has passed since last update.

SwiftUIでの半円形プログレスリング

Last updated at Posted at 2023-05-25

demo-auto_AdobeExpress.gif

新しい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()
    }
}
10
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
6