swiftUI时钟( Jmeter 样式)

h7appiyu  于 2023-01-16  发布在  Swift
关注(0)|答案(2)|浏览(189)

我想创建一个时钟样式与新的GaugeStyle自iOS 16。你可以看到我的代码和预览在这篇文章中。我想画的3,6,9,12线(粗体黑色)。彩色线显示实际小时(这部分工作良好)。

Gauge(value: Double(hour.formatted(.number)) ?? 0, in: 0...24) {
    Image(systemName: "gauge.medium")
       .font(.system(size: 50.0))
} currentValueLabel: {
    Text(Image(systemName: weatherModel.symbol ?? "xmark.icloud"))
}
.gaugeStyle(clockGaugeStyle())

与结构代码

struct clockGaugeStyle: GaugeStyle {
    private var purpleGradient = LinearGradient(gradient: Gradient(colors: [ Color(red: 207/255, green: 150/255, blue: 207/255), Color(red: 107/255, green: 116/255, blue: 179/255) ]), startPoint: .trailing, endPoint: .leading)
 
    func makeBody(configuration: Configuration) -> some View {
        ZStack {
            Circle()
                .trim(from: 0, to: 0.75 * configuration.value)
                .stroke(purpleGradient, lineWidth: 20)
                .rotationEffect(.degrees(135))
            Circle()
                .trim(from: 0, to: 0.75)
                .stroke(Color.black, style: StrokeStyle(lineWidth: 10, lineCap: .butt, lineJoin: .round, dash: [1, 96], dashPhase: 0.0))
                .rotationEffect(.degrees(360))
            VStack {
                configuration.currentValueLabel
                    .font(.system(size: 80, weight: .bold, design: .rounded))
                    .foregroundColor(.gray)
            } 
        }
        .frame(width: 300, height: 300)
    } 
}

4ioopgfo

4ioopgfo1#

我知道你想在 Jmeter 上画出小时标记和3、6、9、12小时的显著标记。
这就是我的解决方案。如果大小总是固定的话,你就不需要GeometryReader了。

struct clockGaugeStyle: GaugeStyle {
    private var purpleGradient = LinearGradient(gradient: Gradient(colors: [ Color(red: 207/255, green: 150/255, blue: 207/255), Color(red: 107/255, green: 116/255, blue: 179/255) ]), startPoint: .trailing, endPoint: .leading)
    
    func makeBody(configuration: Configuration) -> some View {
        GeometryReader { geo in
            ZStack {
                Circle()
                    .trim(from: 0, to: 0.75 * configuration.value)
                    .stroke(purpleGradient, lineWidth: 20)
                    .rotationEffect(.degrees(135))
                
                // hours
                ForEach(0..<12) { hour in
                    Color.black.opacity(0.5)
                        .frame(width: 20, height: 1)
                        .offset(x: geo.size.width / 2 )
                        .rotationEffect(.degrees(Double(hour * 30)))
                }
                
                // 3,6,9,12
                ForEach(0..<4) { hour in
                    Color.black
                        .frame(width: 20, height: 4)
                        .offset(x: geo.size.width / 2 )
                        .rotationEffect(.degrees(Double(hour * 3 * 30)))
                }

                configuration.currentValueLabel
                    .font(.system(size: 80, weight: .bold, design: .rounded))
                    .foregroundColor(.gray)
                
            }
        }
        .frame(width: 300, height: 300)
        
    }
}
wixjitnu

wixjitnu2#

我会使用Canvas来绘制量规。它的代码更多,但我发现它更容易理解。下面是我的代码:

即使您不使用Canvas,也应该考虑使用AngularGradient而不是LinearGradient

左边的图使用Angular 渐变。右边的图使用线性渐变。请注意,在左边,颜色从弧的起点到终点平滑地变化。在右边,请注意,弧左侧的大部分看起来都是粉红色,弧右侧的大部分看起来都是靛蓝色。线性渐变仅在大约10:30到1:30之间明显。
下面是我的代码(使用Angular 梯度):

struct ContentView: View {
    @State var hour = 12

    var body: some View {
        VStack {
            Spacer()

            Gauge(value: Double(hour), in: 0...24) {
                Image(systemName: "gauge.medium")
                    .font(.system(size: 50.0))
            } currentValueLabel: {
                Image(systemName: "xmark.icloud")
            }
            .gaugeStyle(.clock)
            .animation(.easeOut, value: hour)

            Slider(
                value: .init(
                    get: { Double(hour) },
                    set: { hour = Int($0.rounded()) }
                ),
                in: 0.0 ... 24.0
            )
        }
        .padding()
    }
}

extension GaugeStyle where Self == ClockGaugeStyle {
    static var clock: Self { .init() }
}

struct ClockGaugeStyle: GaugeStyle {
    func makeBody(configuration: Configuration) -> some View {
        ZStack {
            configuration.currentValueLabel
                .font(.system(
                    size: 80,
                    weight: .bold,
                    design: .rounded))
                .foregroundColor(.gray)

            GaugeGraphic(animatableData: configuration.value)
        }
        .frame(width: 300, height: 300)
    }

    private struct GaugeGraphic: View, Animatable {
        var animatableData: Double

        var gradient: Gradient {
            .init(stops: [
                .init(color: Color(#colorLiteral(red: 0.8117647059, green: 0.5882352941, blue: 0.8117647059, alpha: 1)), location: 0),
                .init(color: Color(#colorLiteral(red: 0.8117647059, green: 0.5882352941, blue: 0.8117647059, alpha: 1)), location: 0.125),
                .init(color: Color(#colorLiteral(red: 0.4196078431, green: 0.4549019608, blue: 0.7019607843, alpha: 1)), location: 0.875),
                .init(color: Color(#colorLiteral(red: 0.4196078431, green: 0.4549019608, blue: 0.7019607843, alpha: 1)), location: 1.0),
            ])
        }

        var body: some View {
            Canvas { gc, size in
                let center = CGPoint(x: 0.5 * size.width, y: 0.5 * size.height)
                gc.translateBy(x: center.x, y: center.y)

                let arcWidth = size.width * 0.08

                let arc = Path {
                    let baseDegrees = 135.0
                    $0.addArc(
                        center: .zero, radius: 0.5 * (size.width - arcWidth),
                        startAngle: .degrees(baseDegrees),
                        // max with 0.0001 so it draws a dot if animatableData == 0
                        endAngle: .degrees(baseDegrees + 270 * max(0.0001, animatableData)),
                        clockwise: false
                    )
                }

                let shading = GraphicsContext.Shading.conicGradient(
                    gradient, center: .zero,
                    angle: .degrees(90)
                )

                gc.stroke(
                    arc, with: shading,
                    style: .init(lineWidth: arcWidth, lineCap: .round)
                )

                for hour in 0 ..< 12 {
                    let tick = Path {
                        $0.move(to: .init(x: 0.5 * size.width - 7, y: 0))
                        $0.addLine(to: .init(x: 0.5 * size.width - arcWidth + 7, y: 0))
                    }.applying(.init(rotationAngle: Double(hour) * .pi / 6))

                    gc.stroke(
                        tick,
                        with: .color(.black),
                        style: .init(
                            lineWidth: hour.isMultiple(of: 3) ? 3 : 1,
                            lineCap: .round
                        )
                    )
                }
            }
        }
    }
}

线性梯度的唯一区别是如何初始化Shading

let shading = GraphicsContext.Shading.linearGradient(
                    gradient,
                    startPoint: .init(x: -0.5 * size.width, y: 0),
                    endPoint: .init(x: 0.5 * size.width, y: 0)
                )

相关问题