如何在SwiftUI中漂亮地绘制垂直线性仪表盘?

huangapple go评论121阅读模式
英文:

How to draw a vertical linear Gauge in SwiftUI nicely?

问题

我是新手学习SwiftUI,并最近开始着手我的第一个项目(关于可视化一些数据)。我喜欢SwiftUI中的Gauge结构看起来,所以我想基于它设计视图。

现在,我想展示一个垂直线性仪表,但我遇到了一些问题。以下是我的代码的结果:

如何在SwiftUI中漂亮地绘制垂直线性仪表盘?

垂直线性仪表的底部填充看起来非常奇怪。这是我的代码:

import SwiftUI

struct SingleValueVertical: View {
    @State var val: Float = 32.0
    @State var MinMax: (min: Float, max: Float) = (0, 50)
    
    var body: some View {
        ZStack {
            HStack (alignment: .top){
                VStack(alignment: .leading) {
                    Image(systemName: "airplane.departure")
                        .foregroundColor(.white)
                    Text("Speed")
                        .font(.title2)
                        .foregroundColor(.white)
                        .opacity(0.9)
                    Text("\(Int(val))")
                        .font(.largeTitle)
                        .foregroundColor(.white)
                }
                RotatedGauge()
            }
            .padding([.bottom, .top], 30)
            .padding([.horizontal], 20)
        }
        .frame(width: 200, height: 200)
        .background(Color.black.opacity(0.8))
        .cornerRadius(30)
    }
}

struct SingleValueVertical_Previews: PreviewProvider {
    static var previews: some View {
        SingleValueVertical()
    }
}

其中RotatedGauge类是:

struct RotatedGauge: View {
    @State var val: Float = 32.0
    @State var MinMax: (min: Float, max: Float) = (0, 50)
    
    var body: some View {
        VStack {
            Gauge(value: val, in: MinMax.min...MinMax.max) {
            } currentValueLabel: {
            } minimumValueLabel: {
                Text("\(Int(MinMax.min))")
                    .foregroundColor(Color.green)
                    .rotationEffect(.degrees(90))
            } maximumValueLabel: {
                Text("\(Int(MinMax.max))")
                    .foregroundColor(Color.red)
                    .rotationEffect(.degrees(90))
            }
            .tint(Gradient(colors: [.green, .red]))
            .gaugeStyle(.accessoryLinear)
            .rotationEffect(.degrees(-90))
        }
    }
}

请原谅我的糟糕编码,但我真的想知道我的代码有什么问题。是否有更好的方法来做这个?我已经尝试了很多搜索,但没有一个结果符合我的期望。

英文:

I am new to SwiftUI and just start to struggle with my first project recently (it is about visualizing some data). I love how the Gauge struct in SwiftUI looks, so I want to design views based on it.

Now, I want to show a vertical linear gauge, but I ran into some problems. Here is the result of my code:

如何在SwiftUI中漂亮地绘制垂直线性仪表盘?

It looks pretty strange that the vertical linear gauge's padding to the bottom is huge. Here is my code:

import SwiftUI

struct SingleValueVertical: View {
    @State var val: Float = 32.0
    @State var MinMax: (min: Float, max: Float) = (0, 50)
    
    var body: some View {
        ZStack {
            HStack (alignment: .top){
                VStack(alignment: .leading) {
                    Image(systemName: "airplane.departure")
                        .foregroundColor(.white)
                    Text("Speed")
                        .font(.title2)
                        .foregroundColor(.white)
                        .opacity(0.9)
                    Text("\(Int(val))")
                        .font(.largeTitle)
                        .foregroundColor(.white)
                }
                RotatedGauge()
            }
            .padding([.bottom, .top], 30)
            .padding([.horizontal], 20)
        }
        .frame(width: 200, height: 200)
        .background(.black.opacity(0.8))
        .cornerRadius(30)
//        .shadow(radius: 5)
    }
}

struct SingleValueVertical_Previews: PreviewProvider {
    static var previews: some View {
        SingleValueVertical()
    }
}

in which RotatedGauge class is

struct RotatedGauge: View {
    @State var val: Float = 32.0
    @State var MinMax: (min: Float, max: Float) = (0, 50)
    
    var body: some View {
        VStack {
            Gauge(value: val, in: MinMax.min...MinMax.max) {
//                Image(systemName: "heart.fill")
//                    .foregroundColor(.red)
            } currentValueLabel: {
//                Text("\(Int(val))")
//                    .foregroundColor(Color.green)
            } minimumValueLabel: {
                Text("\(Int(MinMax.min))")
                    .foregroundColor(Color.green)
                    .rotationEffect(.degrees(90))
            } maximumValueLabel: {
                Text("\(Int(MinMax.max))")
                    .foregroundColor(Color.red)
                    .rotationEffect(.degrees(90))
            }
            .tint(Gradient(colors: [.green, .red]))
            .gaugeStyle(.accessoryLinear)
            .rotationEffect(.degrees(-90))
        }
    }
}

Excuse my bad coding, but I really want to know that what is wrong with my code. Is there some methods to do it more nicely?

I have tried so many googling, but none of the results gave me what I expected.

答案1

得分: 1

你可以很容易地实现自己的 GaugeStyle,而不是将现有的样式弯曲以满足你的需求。这是线性附件计量样式的简单垂直版本:

struct VerticalAccessoryGaugeStyle: GaugeStyle {
    func makeBody(configuration: Configuration) -> some View {
        VStack(spacing: 0) {
            configuration.maximumValueLabel
            GeometryReader { proxy in
                Capsule()
                    .fill(.tint)
                    .rotationEffect(.degrees(180))
                Circle()
                    .stroke(.background, style: StrokeStyle(lineWidth: 3))
                    .position(x: 5, y: proxy.size.height * (1 - configuration.value))
            }
            .frame(width: 10)
            .clipped()
            configuration.minimumValueLabel
        }
    }
}

当像这样使用时:

Gauge(value: value, in: 0...100) {
} currentValueLabel: {
} minimumValueLabel: {
    Text("0")
} maximumValueLabel: {
    Text("100")
}
.gaugeStyle(VerticalAccessoryGaugeStyle())
.frame(height: 100)
.tint(Gradient(colors: [.green, .red]))

看起来像这样:

如何在SwiftUI中漂亮地绘制垂直线性仪表盘?

英文:

You can implement your own GaugeStyle quite easily, instead of bending the existing ones to your needs. Here is a simple vertical version of the linear accessory gauge style:

struct VerticalAccessoryGaugeStyle: GaugeStyle {
    func makeBody(configuration: Configuration) -> some View {
        VStack(spacing: 0) {
            configuration.maximumValueLabel
            GeometryReader { proxy in
                Capsule()
                    .fill(.tint)
                    .rotationEffect(.degrees(180))
                Circle()
                    .stroke(.background, style: StrokeStyle(lineWidth: 3))
                    .position(x: 5, y: proxy.size.height * (1 - configuration.value))
            }
            .frame(width: 10)
            .clipped()
            configuration.minimumValueLabel
        }
    }
}

Which, when used like this:

Gauge(value: value, in: 0...100) {
} currentValueLabel: {
} minimumValueLabel: {
    Text("0")
} maximumValueLabel: {
    Text("100")
}
.gaugeStyle(VerticalAccessoryGaugeStyle())
.frame(height: 100)
.tint(Gradient(colors: [.green, .red]))

Looks like this:

如何在SwiftUI中漂亮地绘制垂直线性仪表盘?

答案2

得分: 0

当您旋转仪表时,您需要在旋转之前水平调整其大小,然后在旋转后垂直调整大小:

struct RotatedGauge: View {
    @State var val: Float = 32.0
    @State var MinMax: (min: Float, max: Float) = (0, 50)
    
    var body: some View {
        VStack {
            Gauge(value: val, in: MinMax.min...MinMax.max) {
                //                Image(systemName: "heart.fill")
                //                    .foregroundColor(.red)
            } currentValueLabel: {
                //                Text("\(Int(val))")
                //                    .foregroundColor(Color.green)
            } minimumValueLabel: {
                Text("\(Int(MinMax.min))")
                    .foregroundColor(Color.green)
                    .rotationEffect(.degrees(90))
            } maximumValueLabel: {
                Text("\(Int(MinMax.max))")
                    .foregroundColor(Color.red)
                    .rotationEffect(.degrees(90))
            }
            .frame(width: 160, height: 80) // 水平大小
            .rotationEffect(.degrees(-90))
            .frame(width: 80, height: 160) // 旋转后调整大小
            .tint(Gradient(colors: [.green, .red]))
            .gaugeStyle(.accessoryLinear)
//            .border(Color.white)
        }
    }
}

也许还有其他方法,但这可以解决问题。

英文:

As you rotate the gauge you need to resize it horizontally before rotation, then vertically after rotation :

struct RotatedGauge: View {
    @State var val: Float = 32.0
    @State var MinMax: (min: Float, max: Float) = (0, 50)
    
    var body: some View {
        VStack {
            Gauge(value: val, in: MinMax.min...MinMax.max) {
                //                Image(systemName: "heart.fill")
                //                    .foregroundColor(.red)
            } currentValueLabel: {
                //                Text("\(Int(val))")
                //                    .foregroundColor(Color.green)
            } minimumValueLabel: {
                Text("\(Int(MinMax.min))")
                    .foregroundColor(Color.green)
                    .rotationEffect(.degrees(90))
            } maximumValueLabel: {
                Text("\(Int(MinMax.max))")
                    .foregroundColor(Color.red)
                    .rotationEffect(.degrees(90))
            }
            .frame(width: 160, height: 80) // horizontal size
            .rotationEffect(.degrees(-90))
            .frame(width: 80, height: 160) // resize after rotation
            .tint(Gradient(colors: [.green, .red]))
            .gaugeStyle(.accessoryLinear)
//            .border(Color.white)
        }
    }
}

There may be another way, but this can do the trick.

huangapple
  • 本文由 发表于 2023年8月10日 17:42:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/76874520.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定