我有一个List
,显示当月的天数。 List
中的每一行都包含缩写的日期,Divider
和VStack
中的日期。然后将VStack
嵌入到HStack
中,以便在当天和数字的右边添加更多文本。
struct DayListItem : View {
// MARK: - Properties
let date: Date
private let weekdayFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "EEE"
return formatter
}()
private let dayNumberFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "d"
return formatter
}()
var body: some View {
HStack {
VStack(alignment: .center) {
Text(weekdayFormatter.string(from: date))
.font(.caption)
.foregroundColor(.secondary)
Text(dayNumberFormatter.string(from: date))
.font(.body)
.foregroundColor(.red)
}
Divider()
}
}
}
DayListItem
的实例在ContentView
中使用:struct ContentView : View {
// MARK: - Properties
private let dataProvider = CalendricalDataProvider()
private var navigationBarTitle: String {
let formatter = DateFormatter()
formatter.dateFormat = "MMMM YYY"
return formatter.string(from: Date())
}
private var currentMonth: Month {
dataProvider.currentMonth
}
private var months: [Month] {
return dataProvider.monthsInRelativeYear
}
var body: some View {
NavigationView {
List(currentMonth.days.identified(by: \.self)) { date in
DayListItem(date: date)
}
.navigationBarTitle(Text(navigationBarTitle))
.listStyle(.grouped)
}
}
}
代码的结果如下:
这可能不是很明显,但是分隔符没有对齐,因为文本的宽度可能在行与行之间变化。我要实现的是使包含日期信息的视图具有相同的宽度,以便它们在视觉上对齐。
我尝试使用
GeometryReader
和frame()
修饰符设置最小宽度,理想宽度和最大宽度,但是我需要确保使用动态类型设置可以使文本缩小和增长。我选择不使用父级百分比的宽度,因为我不确定如何确定本地化文本始终适合允许的宽度。如何修改视图,以使行中的每个视图具有相同的宽度,而不考虑文本的宽度?
关于动态类型,我将创建一个更改该设置时要使用的不同布局。
最佳答案
我使用GeometryReader
和Preferences
使它工作。
首先,在ContentView
中添加以下属性:
@State var maxLabelWidth: CGFloat = 0
然后,在
DayListItem
中添加以下属性:@Binding var maxLabelWidth: CGFloat
接下来,在
ContentView
中,将self.$maxLabelWidth
传递给DayListItem
的每个实例:List(currentMonth.days.identified(by: \.self)) { date in
DayListItem(date: date, maxLabelWidth: self.$maxLabelWidth)
}
现在,创建一个名为
WidthPreferenceKey
的结构:struct WidthPreferenceKey: PreferenceKey {
typealias Value = CGFloat
static var defaultValue: CGFloat = 0
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
value = nextValue()
}
}
这符合
PreferenceKey
协议,允许您在视图之间传递首选项时将此结构用作键。接下来,创建一个名为
View
的DayListItemGeometry
-这将用于确定VStack
中DayListItem
的宽度:struct DayListItemGeometry: View {
var body: some View {
GeometryReader { geometry in
Rectangle()
.fill(Color.clear)
.preference(key: WidthPreferenceKey.self, value: geometry.size.width)
}
.scaledToFill()
}
}
然后,在
DayListItem
中,将代码更改为此:HStack {
VStack(alignment: .center) {
Text(weekdayFormatter.string(from: date))
.font(.caption)
.foregroundColor(.secondary)
Text(dayNumberFormatter.string(from: date))
.font(.body)
.foregroundColor(.red)
}
.frame(width: self.maxLabelWidth)
.background(DayListItemGeometry())
.onPreferenceChange(WidthPreferenceKey.self) {
if $0 > self.maxLabelWidth {
self.maxLabelWidth = $0
}
}
Divider()
}
我要做的是创建了一个
GeometryReader
,它可以缩放以填充VStack
的背景,该背景本身的大小根据文本的大小而定。然后,我将GeometryReader
的宽度设置为首选项,然后阅读该首选项更改并在必要时更新maxLabelWidth
。然后,将VStack
的框架设置为.frame(width: self.maxLabelWidth)
,并且由于maxLabelWidth
是绑定的,因此在计算新的DayListItem
时将更新每个maxLabelWidth
。关于swiftui - 在列表行中创建等宽的SwiftUI View ,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/56997725/