swiftui - 如何更改 Lazy Grid GridItem 的固定大小以响应动态文本大小更改?
问题描述
下面的代码使用 aLazyVGrid
来实现我的控件的布局,以便所有内容都像这样排列:
特别地,滑块的末端全部对齐,并且符号彼此中心对齐。我已经研究了如何调整符号和数字读数的 GridItems 的大小,以使它们成为其内容的“正确大小”——既不灵活也不自适应 GridItems 将做的事情——同时考虑到 Dynamic Type。
在测试中,只要用户在应用程序处于活动状态后不更改其动态类型大小,这就会非常有效。如果这样做了,类型(和符号)将按照它们应有的方式进行调整,但 GridItem 大小保持固定在其初始值。这会导致数字在小数点处换行。
有没有办法让 GridItem 调整大小以响应动态类型的变化,或者有没有更好的方法来做这个布局?
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
解决方案
您尝试在这里使用网格的目的似乎很清楚,由于图像大小不同而对齐滑块,但网格配置是恒定的,即您只做过一次。实际上,它是相当硬编码的,因此不适合动态文本大小写。
我会提出替代方法 - 只需使用常规的 HStack,它会动态地适应内容,以及一些自定义的内容动态对齐方式。
使用 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()
}
}
推荐阅读
- php - 如何根据逐步 div 设置 div 的高度
- amazon-web-services - 有没有办法为 AWS 放大项目生成配置文件 (yaml) 或某种方式?
- vue.js - 在没有点击事件的情况下使用 $emit
- c++ - 将输入读取到 C++ 中的数组时出错
- python - 遍历行以检查数组的值是否存在于两列之间
- amazon-mws - 有没有办法使用亚马逊 MWS API 取消移除订单?
- mapbox-android - 更新 GeoJSON 源会使图层不可见,直到放大/缩小地图
- restsharp - 使用带有承载令牌的restsharp POST方法时出现未经授权的错误,但在邮递员工具中有效
- android - Android Studio 使用按钮切换布局
- vue.js - 如何在 nuxt js 中使用 .less 文件