WWDC20: 在 Swift Playgrounds 中构建 SwiftUI 视图

发布
更新
字数 300
阅读 2 分钟
阅读量 379

WWDC 20 介绍了在 Playgrounds 中使用 SwiftUI 可以轻松构建原型或者在 Xcode 中使用的组件 https://developer.apple.com/videos/play/wwdc2020/10643/,过程比较简单,这里只记录了 ProgressView 部分的代码,稍有修改。

import SwiftUI

extension GeometryProxy {
    var minSize: CGFloat {
        min(size.width, size.height)
    }
}

struct ProgressView: View {
    let gradientColors: [Color] = [.pink, .purple]
    let sliceSize = 0.35
    let progress: Double
    
    private let percentageFormatter: NumberFormatter = {
        let formatter = NumberFormatter()
        formatter.numberStyle = .percent
        return formatter
    }()
    
    private var strokeGradient: AngularGradient {
        return AngularGradient(gradient: Gradient(colors: self.gradientColors), center: .center, angle: .degrees(-10))
    }
    
    init(_ progress: Double) {
        self.progress = min(progress, 1)
    }
    
    var body: some View {
        GeometryReader { geometry in
            ZStack {
                Group {
                    Circle()
                        .trim(from: 0, to: 1 - CGFloat(self.sliceSize))
                        .stroke(self.strokeGradient, style: self.strokeStyle(with: geometry))
                        .opacity(0.5)
                    Circle()
                        .trim(from: 0, to: (1 - CGFloat(self.sliceSize)) * CGFloat(self.progress))
                        .stroke(self.strokeGradient, style: self.strokeStyle(with: geometry))
                }
                .rotationEffect(.degrees(90) + .degrees(360 * self.sliceSize / 2))
                
                if self.progress >= 0.995 {
                    Image(systemName: "star.fill")
                        .font(.system(size: 0.4 * geometry.minSize, weight: .bold, design: .rounded))
                        .foregroundColor(.yellow)
                        .offset(y: -0.05 * geometry.minSize)
                        .animation(nil, value: 0)
                } else {
                    Text(self.percentageFormatter.string(from: self.progress as NSNumber)!)
                        .font(.system(size: 0.3 * geometry.minSize, weight: .bold, design: .rounded))
                        .offset(y: -0.05 * geometry.minSize)
                        .animation(nil, value: 0)
                }
            }
            .offset(y: 0.1 * geometry.minSize)
        }
        .padding(40)
        .background(Color(uiColor: .systemBackground))
        .cornerRadius(20)
    }
                                
    private func strokeStyle(with geometry: GeometryProxy) -> StrokeStyle {
        return StrokeStyle(lineWidth: 0.12 * geometry.minSize, lineCap: .round)
    }
}

fileprivate struct Preview: View {
    @State var progress = 0.25
    
    var body: some View {
        VStack {
            ProgressView(progress)
        }
        .onTapGesture {
            increment()
        }
        .padding(50)
        .background(Color(uiColor: UIColor.secondarySystemBackground))
    }
    
    func increment() {
        withAnimation {
            progress += 0.1
        }
    }
}

struct Preview_Previews: PreviewProvider {
    static var previews: some View {
        Preview()
    }
}