我有一个List,显示当月的天数。 List中的每一行都包含缩写的日期,DividerVStack中的日期。然后将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)
        }
    }

}


代码的结果如下:

swiftui - 在列表行中创建等宽的SwiftUI View-LMLPHP

这可能不是很明显,但是分隔符没有对齐,因为文本的宽度可能在行与行之间变化。我要实现的是使包含日期信息的视图具有相同的宽度,以便它们在视觉上对齐。

我尝试使用GeometryReaderframe()修饰符设置最小宽度,理想宽度和最大宽度,但是我需要确保使用动态类型设置可以使文本缩小和增长。我选择不使用父级百分比的宽度,因为我不确定如何确定本地化文本始终适合允许的宽度。

如何修改视图,以使行中的每个视图具有相同的宽度,而不考虑文本的宽度?

关于动态类型,我将创建一个更改该设置时要使用的不同布局。

最佳答案

我使用GeometryReaderPreferences使它工作。

首先,在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协议,允许您在视图之间传递首选项时将此结构用作键。

接下来,创建一个名为ViewDayListItemGeometry-这将用于确定VStackDayListItem的宽度:

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-LMLPHP

关于swiftui - 在列表行中创建等宽的SwiftUI View ,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/56997725/

10-12 19:11