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

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

How to draw a vertical linear Gauge in SwiftUI nicely?

问题

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

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

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

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

  1. import SwiftUI
  2. struct SingleValueVertical: View {
  3. @State var val: Float = 32.0
  4. @State var MinMax: (min: Float, max: Float) = (0, 50)
  5. var body: some View {
  6. ZStack {
  7. HStack (alignment: .top){
  8. VStack(alignment: .leading) {
  9. Image(systemName: "airplane.departure")
  10. .foregroundColor(.white)
  11. Text("Speed")
  12. .font(.title2)
  13. .foregroundColor(.white)
  14. .opacity(0.9)
  15. Text("\(Int(val))")
  16. .font(.largeTitle)
  17. .foregroundColor(.white)
  18. }
  19. RotatedGauge()
  20. }
  21. .padding([.bottom, .top], 30)
  22. .padding([.horizontal], 20)
  23. }
  24. .frame(width: 200, height: 200)
  25. .background(Color.black.opacity(0.8))
  26. .cornerRadius(30)
  27. }
  28. }
  29. struct SingleValueVertical_Previews: PreviewProvider {
  30. static var previews: some View {
  31. SingleValueVertical()
  32. }
  33. }

其中RotatedGauge类是:

  1. struct RotatedGauge: View {
  2. @State var val: Float = 32.0
  3. @State var MinMax: (min: Float, max: Float) = (0, 50)
  4. var body: some View {
  5. VStack {
  6. Gauge(value: val, in: MinMax.min...MinMax.max) {
  7. } currentValueLabel: {
  8. } minimumValueLabel: {
  9. Text("\(Int(MinMax.min))")
  10. .foregroundColor(Color.green)
  11. .rotationEffect(.degrees(90))
  12. } maximumValueLabel: {
  13. Text("\(Int(MinMax.max))")
  14. .foregroundColor(Color.red)
  15. .rotationEffect(.degrees(90))
  16. }
  17. .tint(Gradient(colors: [.green, .red]))
  18. .gaugeStyle(.accessoryLinear)
  19. .rotationEffect(.degrees(-90))
  20. }
  21. }
  22. }

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

英文:

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:

  1. import SwiftUI
  2. struct SingleValueVertical: View {
  3. @State var val: Float = 32.0
  4. @State var MinMax: (min: Float, max: Float) = (0, 50)
  5. var body: some View {
  6. ZStack {
  7. HStack (alignment: .top){
  8. VStack(alignment: .leading) {
  9. Image(systemName: "airplane.departure")
  10. .foregroundColor(.white)
  11. Text("Speed")
  12. .font(.title2)
  13. .foregroundColor(.white)
  14. .opacity(0.9)
  15. Text("\(Int(val))")
  16. .font(.largeTitle)
  17. .foregroundColor(.white)
  18. }
  19. RotatedGauge()
  20. }
  21. .padding([.bottom, .top], 30)
  22. .padding([.horizontal], 20)
  23. }
  24. .frame(width: 200, height: 200)
  25. .background(.black.opacity(0.8))
  26. .cornerRadius(30)
  27. // .shadow(radius: 5)
  28. }
  29. }
  30. struct SingleValueVertical_Previews: PreviewProvider {
  31. static var previews: some View {
  32. SingleValueVertical()
  33. }
  34. }

in which RotatedGauge class is

  1. struct RotatedGauge: View {
  2. @State var val: Float = 32.0
  3. @State var MinMax: (min: Float, max: Float) = (0, 50)
  4. var body: some View {
  5. VStack {
  6. Gauge(value: val, in: MinMax.min...MinMax.max) {
  7. // Image(systemName: "heart.fill")
  8. // .foregroundColor(.red)
  9. } currentValueLabel: {
  10. // Text("\(Int(val))")
  11. // .foregroundColor(Color.green)
  12. } minimumValueLabel: {
  13. Text("\(Int(MinMax.min))")
  14. .foregroundColor(Color.green)
  15. .rotationEffect(.degrees(90))
  16. } maximumValueLabel: {
  17. Text("\(Int(MinMax.max))")
  18. .foregroundColor(Color.red)
  19. .rotationEffect(.degrees(90))
  20. }
  21. .tint(Gradient(colors: [.green, .red]))
  22. .gaugeStyle(.accessoryLinear)
  23. .rotationEffect(.degrees(-90))
  24. }
  25. }
  26. }

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,而不是将现有的样式弯曲以满足你的需求。这是线性附件计量样式的简单垂直版本:

  1. struct VerticalAccessoryGaugeStyle: GaugeStyle {
  2. func makeBody(configuration: Configuration) -> some View {
  3. VStack(spacing: 0) {
  4. configuration.maximumValueLabel
  5. GeometryReader { proxy in
  6. Capsule()
  7. .fill(.tint)
  8. .rotationEffect(.degrees(180))
  9. Circle()
  10. .stroke(.background, style: StrokeStyle(lineWidth: 3))
  11. .position(x: 5, y: proxy.size.height * (1 - configuration.value))
  12. }
  13. .frame(width: 10)
  14. .clipped()
  15. configuration.minimumValueLabel
  16. }
  17. }
  18. }

当像这样使用时:

  1. Gauge(value: value, in: 0...100) {
  2. } currentValueLabel: {
  3. } minimumValueLabel: {
  4. Text("0")
  5. } maximumValueLabel: {
  6. Text("100")
  7. }
  8. .gaugeStyle(VerticalAccessoryGaugeStyle())
  9. .frame(height: 100)
  10. .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:

  1. struct VerticalAccessoryGaugeStyle: GaugeStyle {
  2. func makeBody(configuration: Configuration) -> some View {
  3. VStack(spacing: 0) {
  4. configuration.maximumValueLabel
  5. GeometryReader { proxy in
  6. Capsule()
  7. .fill(.tint)
  8. .rotationEffect(.degrees(180))
  9. Circle()
  10. .stroke(.background, style: StrokeStyle(lineWidth: 3))
  11. .position(x: 5, y: proxy.size.height * (1 - configuration.value))
  12. }
  13. .frame(width: 10)
  14. .clipped()
  15. configuration.minimumValueLabel
  16. }
  17. }
  18. }

Which, when used like this:

  1. Gauge(value: value, in: 0...100) {
  2. } currentValueLabel: {
  3. } minimumValueLabel: {
  4. Text("0")
  5. } maximumValueLabel: {
  6. Text("100")
  7. }
  8. .gaugeStyle(VerticalAccessoryGaugeStyle())
  9. .frame(height: 100)
  10. .tint(Gradient(colors: [.green, .red]))

Looks like this:

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

答案2

得分: 0

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

  1. struct RotatedGauge: View {
  2. @State var val: Float = 32.0
  3. @State var MinMax: (min: Float, max: Float) = (0, 50)
  4. var body: some View {
  5. VStack {
  6. Gauge(value: val, in: MinMax.min...MinMax.max) {
  7. // Image(systemName: "heart.fill")
  8. // .foregroundColor(.red)
  9. } currentValueLabel: {
  10. // Text("\(Int(val))")
  11. // .foregroundColor(Color.green)
  12. } minimumValueLabel: {
  13. Text("\(Int(MinMax.min))")
  14. .foregroundColor(Color.green)
  15. .rotationEffect(.degrees(90))
  16. } maximumValueLabel: {
  17. Text("\(Int(MinMax.max))")
  18. .foregroundColor(Color.red)
  19. .rotationEffect(.degrees(90))
  20. }
  21. .frame(width: 160, height: 80) // 水平大小
  22. .rotationEffect(.degrees(-90))
  23. .frame(width: 80, height: 160) // 旋转后调整大小
  24. .tint(Gradient(colors: [.green, .red]))
  25. .gaugeStyle(.accessoryLinear)
  26. // .border(Color.white)
  27. }
  28. }
  29. }

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

英文:

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

  1. struct RotatedGauge: View {
  2. @State var val: Float = 32.0
  3. @State var MinMax: (min: Float, max: Float) = (0, 50)
  4. var body: some View {
  5. VStack {
  6. Gauge(value: val, in: MinMax.min...MinMax.max) {
  7. // Image(systemName: "heart.fill")
  8. // .foregroundColor(.red)
  9. } currentValueLabel: {
  10. // Text("\(Int(val))")
  11. // .foregroundColor(Color.green)
  12. } minimumValueLabel: {
  13. Text("\(Int(MinMax.min))")
  14. .foregroundColor(Color.green)
  15. .rotationEffect(.degrees(90))
  16. } maximumValueLabel: {
  17. Text("\(Int(MinMax.max))")
  18. .foregroundColor(Color.red)
  19. .rotationEffect(.degrees(90))
  20. }
  21. .frame(width: 160, height: 80) // horizontal size
  22. .rotationEffect(.degrees(-90))
  23. .frame(width: 80, height: 160) // resize after rotation
  24. .tint(Gradient(colors: [.green, .red]))
  25. .gaugeStyle(.accessoryLinear)
  26. // .border(Color.white)
  27. }
  28. }
  29. }

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:

确定