如何更改 Lazy Grid GridItem 的固定大小以响应动态文本大小更改? [英] How do I change the fixed size of a Lazy Grid GridItem in response to Dynamic Text size changes?

查看:34
本文介绍了如何更改 Lazy Grid GridItem 的固定大小以响应动态文本大小更改?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

下面的代码使用 LazyVGrid 来实现我的控件的布局,这样一切都像这样排列:

The code below is using a LazyVGrid to implement layout of my controls such that everything lines up like this:

特别是,滑块的末端全部对齐并且符号彼此居中对齐.我已经研究出如何为符号和数字读数调整 GridItems 的大小,以便它们是其内容的正确大小"——一些既不灵活也不自适应的 GridItems 将做的事情——同时考虑动态类型.

Particularly, the sliders' ends all align and the symbols are centre-aligned to each other. I have worked out how to size the GridItems for the symbol and the numeric readout such that they are the 'right size' for their contents — something neither flexible nor adaptive GridItems will do — while accounting for Dynamic Type.

在测试中,只要用户在应用程序处于活动状态后不更改其动态类型大小,这就会非常有效.如果这样做,类型(和符号)将按其应有的方式进行调整,但 GridItem 大小保持固定为其初始值.这会导致数字在小数点处换行.

In testing, this works really well so long as the user does not change their Dynamic Type size once the app is active. If that is done, the type (and symbols) will adapt as they should, but the GridItem size remains fixed at its initial value. This causes the numbers to wrap at the decimal point.

有没有办法让 GridItem 调整大小以响应动态类型的变化,或者有没有更好的方法来做这个布局?

Is there a way to have the GridItem resize in response to Dynamic Type changes, or is there a better way to do this layout?

import SwiftUI

struct ProtoGrid: View {
   let gridItems = [
      GridItem(.fixed(UIImage(systemName: "ruler", withConfiguration: UIImage.SymbolConfiguration(textStyle: .body, scale: .large))!.size.width)),
      GridItem(.flexible(minimum: 40, maximum: .infinity)),
      GridItem(.fixed(("00.00" as NSString).size(withAttributes: [NSAttributedString.Key.font: UIFont.preferredFont(forTextStyle: .body)]).width + 4), alignment: .trailing)
   ]
   @State var index1 = 5.0
   @State var index2 = 5.0
   @State var index3 = 5.0
   var body: some View {
      VStack {
         Rectangle()
            .fill(Color.red)
         LazyVGrid(columns: gridItems, spacing: 12) {
            Image(systemName: "person").imageScale(.large)
            Slider(value: $index1, in: 0...10)
            Text("\(String(format: "%.2f", index1))").font(Font.system(.body).monospacedDigit())
            Image(systemName: "megaphone").imageScale(.large)
            Slider(value: $index2, in: 0...10)
            Text("\(String(format: "%.2f", index2))").font(Font.system(.body).monospacedDigit())
            Image(systemName: "ruler").imageScale(.large)
            Slider(value: $index3, in: 0...10)
            Text("\(String(format: "%.2f", index3))").font(Font.system(.body).monospacedDigit())
         }
         .padding()
      }
   }
}

struct ProtoGrid_Previews: PreviewProvider {
    static var previews: some View {
        ProtoGrid()
         .previewDevice("iPhone 11 Pro")
    }
}
111

推荐答案

您在这里尝试使用网格的目的似乎很清楚,由于不同的图像大小而对齐滑块,但是网格配置是不变的,即您做到了一次.而且它实际上是硬编码的,因此不适合动态文本大小写.

It seems clear the purpose of your try to use grid here, to have aligned sliders due to different image sizes, but grid configuration is constant, ie you did it once. And it is pretty hardcoded, actually, so does not appropriate for dynamic text case.

我会提出替代方法 - 只需使用常规的 HStack,它可以动态地适应内容,以及一些自定义的内容动态对齐.

I would propose alternate approach - just use regular HStack, which fits content dynamically, and some custom dynamic alignment for content.

使用 Xcode 12/iOS 14 测试

Tested with Xcode 12 / iOS 14

struct ProtoGrid: View {

    @State var index1 = 5.0
    @State var index2 = 5.0
    @State var index3 = 5.0

    @State private var imageWidth = CGFloat.zero

    var body: some View {
        VStack {
            Rectangle()
                .fill(Color.red)
            HStack(spacing: 12) {
                Image(systemName: "person").imageScale(.large)
                    .alignedView(width: $imageWidth)
                Slider(value: $index1, in: 0...10)
                Text("\(String(format: "%.2f", index1))").font(Font.system(.body).monospacedDigit())
            }
            HStack(spacing: 12) {
                Image(systemName: "megaphone").imageScale(.large)
                    .alignedView(width: $imageWidth)
                Slider(value: $index2, in: 0...10)
                Text("\(String(format: "%.2f", index2))").font(Font.system(.body).monospacedDigit())
            }
             HStack(spacing: 12) {
                Image(systemName: "ruler").imageScale(.large)
                    .alignedView(width: $imageWidth)
                Slider(value: $index3, in: 0...10)
                Text("\(String(format: "%.2f", index3))").font(Font.system(.body).monospacedDigit())
            }
        }
        .padding()
    }
}

extension View {
    func alignedView(width: Binding<CGFloat>) -> some View {
        self.modifier(AlignedWidthView(width: width))
    }
}

// creates a view which uses max width of calculated intrinsic
// content or shared from external width, updating external
// bound variable if own width is bigger.
struct AlignedWidthView: ViewModifier {
    @Binding var width: CGFloat

    func body(content: Content) -> some View {
        content
            .background(GeometryReader {
                Color.clear
                    .preference(key: ViewWidthKey.self, value: $0.frame(in: .local).size.width)
            })
            .onPreferenceChange(ViewWidthKey.self) {
                if $0 > self.width {
                    self.width = $0
                }
            }
            .frame(width: width)
    }
}

struct ViewWidthKey: PreferenceKey {
    typealias Value = CGFloat
    static var defaultValue = CGFloat.zero
    static func reduce(value: inout Value, nextValue: () -> Value) {
        value += nextValue()
    }
}

这篇关于如何更改 Lazy Grid GridItem 的固定大小以响应动态文本大小更改?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆