swift Swifui:如何使用GeometryReader来缩放这个视图以适合任何大小的父母?

mepcadol  于 2023-03-17  发布在  Swift
关注(0)|答案(2)|浏览(101)

这个嵌套的环UI工作得很好,但是我如何编写它,使它可以缩放,无论它的父是非常小还是非常大?

import SwiftUI

struct CustomGaugeStyleView: View {
    
    @State private var innerRingFill = 6.5
   
      var body: some View {
          Gauge(value: innerRingFill, in: 0...10) {
              Image(systemName: "gauge.medium")
                  .font(.system(size: 50.0))
          } currentValueLabel: {
              Text("\(innerRingFill.formatted(.number))")
   
          }
          .gaugeStyle(twoRingGaugeStyle(outerRingMin: 5.5, outerRingMax: 7.5))
         
      }
}

struct CustomGaugeStyleView_Previews: PreviewProvider {
    static var previews: some View {
        CustomGaugeStyleView()
    }
}


struct twoRingGaugeStyle: GaugeStyle {
    
    var outerRingMin: Double
    var outerRingMax: Double
    
    func makeBody(configuration: Configuration) -> some View {
        
        GeometryReader { geometry in
            
            ZStack {
                Circle()
                    .stroke(Color(.lightGray).opacity(0.2), style: StrokeStyle(lineWidth: 20))
                    .frame(height: geometry.size.height * 0.70)
                Circle()
                    .trim(from: 0, to: 0.75 * configuration.value)
                    .stroke(Color.orange.gradient, style: StrokeStyle(lineWidth: 20, lineCap: .round, lineJoin: .round))
                    .rotationEffect(.degrees(270))
                    .frame(height: geometry.size.height * 0.70)
                Circle()
                    .trim(from: outerRingMin / 10, to: outerRingMax / 10)
                    .stroke(Color.green.gradient, style: StrokeStyle(lineWidth: 20, lineCap: .round, lineJoin: .round))
                    .rotationEffect(.degrees(270))
                   .frame(height: geometry.size.height * 0.82)
            }
            .padding()
        }
        .aspectRatio(contentMode: .fit)
    }
    
}

第一个图像是没有任何帧大小的视图,第二个视图是将.frame(height: 100)添加到Gauge。

bpzcxfmw

bpzcxfmw1#

正如@loremipsum在评论中提到的,如果你想让这个UI缩放到任何屏幕尺寸,那么你的lineWidth需要是一个百分比。

示例

struct CustomGuageStyleView: View {
    
    @State private var innerRingFill = 6.5
   
      var body: some View {
          Gauge(value: innerRingFill, in: 0...10) {
              Image(systemName: "gauge.medium")
                  .font(.system(size: 50.0))
          } currentValueLabel: {
              Text("\(innerRingFill.formatted(.number))")
   
          }
          .gaugeStyle(twoRingGaugeStyle(outerRingMin: 5.5, outerRingMax: 7.5))
         
      }
}


struct twoRingGaugeStyle: GaugeStyle {
    
    var outerRingMin: Double
    var outerRingMax: Double
    
    //This is not strictly necessary but it gives you an option
    var multiplierAmount: Double = 0.045
    
    func makeBody(configuration: Configuration) -> some View {
        
        GeometryReader { geometry in
            
            ZStack {
                Circle()
                    .stroke(Color(.lightGray).opacity(0.2), style: StrokeStyle(lineWidth: geometry.size.width * multiplierAmount)) ///<<<--- HERE!! We are now making this variable.
                    .frame(height: geometry.size.height * 0.70)
                Circle()
                    .trim(from: 0, to: 0.75 * configuration.value)
                    .stroke(Color.orange.gradient, style: StrokeStyle(lineWidth: geometry.size.width * multiplierAmount, lineCap: .round, lineJoin: .round)) ///<<<--- HERE too.
                    .rotationEffect(.degrees(270))
                    .frame(height: geometry.size.height * 0.70)
                Circle()
                    .trim(from: outerRingMin / 10, to: outerRingMax / 10)
                    .stroke(Color.green.gradient, style: StrokeStyle(lineWidth: geometry.size.width * multiplierAmount, lineCap: .round, lineJoin: .round)) ///<<<--- HERE!! We are now making this variable.
                    .rotationEffect(.degrees(270))
                    .frame(height: geometry.size.height * 0.78) // <<<--- Here, I changed the value to 0.75 - this will make the rings slightly closer together, which works better with the new scaling.
                //- NOTE: I might add a `minHeight` above, or the circle will end up eventually having not enough of a change between the inner value to appear separated.
            }
            .padding()
        }
        .aspectRatio(contentMode: .fit)
    }
    
}

解释

这里的代码只做了几处更改,所有更改都在twoRingGuageStyle中。

  • StrokeStyle中的每个lineWidth上,我将值从20更改为geometry.size.width * multiplierAmount,这使得线宽也随GeometryReader缩放。
  • 我在twoRingGuageStyle的顶部添加了一个可选变量multiplierAmount,它允许您选择性地配置Circle的宽度,默认值为0.045
  • 虽然我把frame留在height: geometry.size.height * 0.82处的外部Circle上,正如我在那一行的评论中提到的,

我可能会在上面添加一个minHeight,否则圆最终将没有足够的内部值之间的变化,以显示分离。
否则,在非常小的屏幕尺寸下,您的圆圈将显示为间隔不够。

屏幕截图

第一节第一节第一节第一节第一次

注解

此代码已在Xcode 14.2和macOS 13.1上进行了测试。可能需要对UI的值进行细微调整,以获得所需的确切外观。

mwkjh3gx

mwkjh3gx2#

我亲自运行了您的代码,并检查了绿色Circle()的原始值是否不正确。

GeometryReader { geometry in
    ZStack {
        Circle()
            .stroke(Color(.lightGray).opacity(0.2), style: StrokeStyle(lineWidth: 20))
            .frame(height: geometry.size.height * 0.70)
        Circle()
            .trim(from: 0, to: 0.75 * configuration.value)
            .stroke(Color.orange.gradient, style: StrokeStyle(lineWidth: 20, lineCap: .round, lineJoin: .round))
            .rotationEffect(.degrees(270))
            .frame(height: geometry.size.height * 0.70)
        Circle()
            .trim(from: outerRingMin / 10, to: outerRingMax / 10)
            .stroke(Color.green.gradient, style: StrokeStyle(lineWidth: 20, lineCap: .round, lineJoin: .round))
            .rotationEffect(.degrees(270))
            .frame(height: geometry.size.height * 0.70)  // 0.82 -> 0.70
    }
    .padding()
}
.aspectRatio(contentMode: .fit)

而且,正如@lorem ipsum所说,要解决重叠问题,需要修改代码,使lineWidth:的工作方式为percentage

GeometryReader { geometry in
    ZStack {
        Circle()
            .stroke(Color(.lightGray).opacity(0.2), style: StrokeStyle(lineWidth: geometry.size.height * 0.10))
            .frame(height: geometry.size.height * 0.70)
        Circle()
            .trim(from: 0, to: 0.75 * configuration.value)
            .stroke(Color.orange.gradient, style: StrokeStyle(lineWidth: geometry.size.height * 0.10, lineCap: .round, lineJoin: .round))
            .rotationEffect(.degrees(270))
            .frame(height: geometry.size.height * 0.70)
        Circle()
            .trim(from: outerRingMin / 10, to: outerRingMax / 10)
            .stroke(Color.green.gradient, style: StrokeStyle(lineWidth: geometry.size.height * 0.10, lineCap: .round, lineJoin: .round))
            .rotationEffect(.degrees(270))
            .frame(height: geometry.size.height * 0.70)
    }
    .padding()
}

相关问题