SwiftUIで作るプログレス

#はじめに
この記事は[Diverse Advent Calendar 2020](https://qiita.com/advent-calendar/2020/diverse)の18日目の記事です。
業務でいくつかプログレスを作ったので紹介します。
同じようなデザインのプログレスを作る際はぜひご活用ください！

GitHubにこの記事のサンプルを上げてます。
↓実際に動きを見たい方はこちら
https://github.com/Masataka-n/SwiftUIProgress

#Ring


```swift
struct RingProgressView: View {

    var value: CGFloat
    var lineWidth: CGFloat = 6.0
    var outerRingColor: Color = Color.black.opacity(0.08)
    var innerRingColor: Color = Color.orange

    var body: some View {
        ZStack {
            Circle()
                .stroke(lineWidth: self.lineWidth)
                .foregroundColor(self.outerRingColor)
            Circle()
                .trim(from: 0.0, to: CGFloat(min(self.value, 1.0)))
                .stroke(
                    style: StrokeStyle(
                        lineWidth: self.lineWidth,
                        lineCap: .square, // プログレスの角を丸くしたい場合は.round
                        lineJoin: .round
                    )
                )
                .foregroundColor(self.innerRingColor)
                .rotationEffect(.degrees(-90.0))
        }
        .padding(.all, self.lineWidth / 2)
    }
}
```

```swift
@State var value: CGFloat = 0.0

 var body: some View {
    RingProgressView(value: value)
        .frame(width: 150, height: 150)
        .onAppear {
            withAnimation(.linear(duration: 5)) {
                self.value = 1.0
            }
        }        
}
```
![ezgif.com-gif-maker (2).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/106260/60238e2f-7959-e2a9-47ba-33a78e78fd9e.gif)


#Pie

Shapeを継承してPieShapeというものを作っています。
Shapeを自作した場合、アニメーションが適用されないので `animatableData` をoverrideする必要があります。


```swift

struct PieProgressView: View {

    var value: CGFloat

    var body: some View {
        ZStack {
            Circle()
                .fill(Color.black.opacity(0.08))
            PieShape(progress: value)
                .fill(Color.orange)
                .rotationEffect(.degrees(-90))
        }
    }
}

struct PieShape: Shape {

    var value: CGFloat

    var animatableData: CGFloat {
        get { value }
        set { value = newValue }
    }

    func path(in rect: CGRect) -> Path {
        Path { path in
            let center = CGPoint(x: rect.midX, y: rect.midY)
            path.move(to: center)
            path.addArc(
                center: center,
                radius: rect.width / 2,
                startAngle: .degrees(0),
                endAngle: .degrees(Double(360 * value)),
                clockwise: false
            )
            path.closeSubpath()
        }
    }
}

```

```swift

@State var value: CGFloat = 0.0

var body: some View {
    PieProgressView(value: value)
        .frame(width: 150, height: 150)
        .onAppear {
            withAnimation(.linear(duration: 5)) {
                self.value = 1.0
            }
        } 
}
```

![ezgif.com-gif-maker.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/106260/bc19bac2-d686-8058-36ae-71b00c173f13.gif)



#Square

Rectangleを2つ重ねて上のRectangleのwidthをvalueに応じて変えることで実現してます。
Squareと名付けてますがcornerRadiusを指定して角丸にすることもできます。

```swift
struct SquareProgressView: View {

    var value: CGFloat
    var baseColor: Color = Color.black.opacity(0.08)
    var progressColor: Color = Color.orange

    var body: some View {
        GeometryReader { geometry in
            VStack(alignment: .trailing) {
                ZStack(alignment: .leading) {
                    Rectangle()
                        .fill(self.baseColor)
                    Rectangle()
                        .fill(self.progressColor)
                        .frame(minWidth: 0, idealWidth:self.getProgressBarWidth(geometry: geometry),
                               maxWidth: self.getProgressBarWidth(geometry: geometry))
                }
            }
        }
    }

    func getProgressBarWidth(geometry:GeometryProxy) -> CGFloat {
        let frame = geometry.frame(in: .global)
        return frame.size.width * value
    }
}
```

```swift

@State var value: CGFloat = 0.0

var body: some View {

    SquareProgressView(value: value)
        .frame(height: 20)
        //.cornerRadius(10) 角丸も可
        .onAppear {
            withAnimation(.linear(duration: 5)) {
                self.value = 1.0
            }
        } 
}
```

![ezgif.com-gif-maker (3).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/106260/e955b6ce-b9db-6c07-c87c-1f832b9d4d04.gif)

#さいごに
今回は作成したプログレスを3つ紹介しました。
基本的にコピペで使えると思いますのでぜひご活用ください。