我有一个包含两行的简单 ListView 。
每行包含两个 TextView 。查看一和查看二。
我想对齐每行中的最后一个标签(查看两个),以便名称标签前导对齐,并且无论字体大小如何都保持对齐。
第一个标签(查看一个)也需要前导对齐。
我尝试在第一个标签(View One)上设置最小框架宽度,但它不起作用。设置最小宽度以及使 TextView 在 View 一中前导对齐似乎也是不可能的。
有什么想法吗?这在 UIKit 中相当简单。
最佳答案
我找到了一种解决此问题的方法,该方法支持动态类型并且不麻烦。答案是使用PreferenceKeys和 GeometryReader !
此解决方案的本质是每个数字 Text
将具有根据其文本大小绘制的宽度。 GeometryReader
可以检测到这个宽度,然后我们可以使用 PreferenceKey
将其冒泡到列表本身,其中可以跟踪最大宽度,然后分配给每个数字Text
的框架宽度。
PreferenceKey
是您使用关联类型创建的类型(可以是符合 Equatable
的任何结构,这是您存储有关首选项的数据的位置),即附加到任何View
,当它附加时,它会在 View 树中向上冒泡,并且可以使用.onPreferenceChange(PreferenceKeyType.self)
在祖先 View 中监听。
首先,我们将创建 PreferenceKey 类型及其包含的数据:
struct WidthPreferenceKey: PreferenceKey {
typealias Value = [WidthPreference]
static var defaultValue: [WidthPreference] = []
static func reduce(value: inout [WidthPreference], nextValue: () -> [WidthPreference]) {
value.append(contentsOf: nextValue())
}
}
struct WidthPreference: Equatable {
let width: CGFloat
}
接下来,我们将创建一个名为 WidthPreferenceSettingView
的 View ,它将附加到我们想要调整大小的任何内容的背景(在本例中为数字标签)。这将负责设置首选项,该首选项将通过 PreferenceKeys 传递此数字标签的首选宽度。
struct WidthPreferenceSettingView: View {
var body: some View {
GeometryReader { geometry in
Rectangle()
.fill(Color.clear)
.preference(
key: WidthPreferenceKey.self,
value: [WidthPreference(width: geometry.frame(in: CoordinateSpace.global).width)]
)
}
}
}
最后是列表本身!我们有一个 @State 变量,它是数字“列”的宽度(实际上不是一列,因为数字不会直接影响代码中的其他数字)。通过 .onPreferenceChange(WidthPreference.self) ,我们监听我们创建的首选项的变化,并将最大宽度存储在我们的宽度状态中。绘制所有数字标签并由 GeometryReader 读取其宽度后,宽度会向上传播,最大宽度由 .frame(width: width)
struct ContentView: View {
@State private var width: CGFloat? = nil
var body: some View {
List {
HStack {
Text("1. ")
.frame(width: width, alignment: .leading)
.lineLimit(1)
.background(WidthPreferenceSettingView())
Text("John Smith")
}
HStack {
Text("20. ")
.frame(width: width, alignment: .leading)
.lineLimit(1)
.background(WidthPreferenceSettingView())
Text("Jane Done")
}
HStack {
Text("2000. ")
.frame(width: width, alignment: .leading)
.lineLimit(1)
.background(WidthPreferenceSettingView())
Text("Jax Dax")
}
}.onPreferenceChange(WidthPreferenceKey.self) { preferences in
for p in preferences {
let oldWidth = self.width ?? CGFloat.zero
if p.width > oldWidth {
self.width = p.width
}
}
}
}
}
如果您有多列数据,缩放的一种方法是对列进行枚举或对它们进行索引,并且宽度的 @State 将成为一个字典,其中每个键都是一列和 。 onPreferenceChange
与列的最大宽度的键值进行比较。
要显示结果,this就是打开较大文本后的样子,就像一个魅力:)。
这篇关于 PreferenceKey 和检查 View 树的文章有很大帮助:https://swiftui-lab.com/communicating-with-the-view-tree-part-1/
关于ios - 在 HStack 中以正确的方式对齐两个 SwiftUI TextView ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56588332/