我正在开发一个 NumberedList 组件,其工作原理类似于
我正在开发一个 NumberedList
组件,其功能类似于 <ol>
HTML 中的标签。我希望能够提供任意数量的元素,这些元素将自动编号和布局。但是布局方面有一个方面我不太清楚。
我已经创建了一个 OrderedList
视图(相当于 <ol>
HTML 标签),它使用了一个 _VariadicView.Tree
自定义 _VariadicView_UnaryViewRoot
布局。
public struct OrderedList<Content: View>: View {
let content: Content
@Environment(\.listLevel)
var level
public init(@ViewBuilder content: () -> Content) {
self.content = content()
}
public var body: some View {
let layout = OrderedListLayout(level: level)
_VariadicView.Tree(layout) {
content
}
}
}
struct OrderedListLayout: _VariadicView_UnaryViewRoot {
let level: ListLevel
@ViewBuilder
func body(children: _VariadicView.Children) -> some View {
let padding: Double = switch level {
case .first:
8
case .second, .third:
-6
}
VStack(alignment: .leading, spacing: 8) {
ForEach(Array(children.enumerated()), id: \.element.id) { index, child in
HStack(alignment: .centerOfFirstLine, spacing: 8) {
Text(verbatim: "\(index + 1).")
child
}
}
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.leading, padding)
}
}
视图(相当于 ListElement
一起使用 <li>
,它可以包含任意标签和可选子视图。
public struct ListElement<Label: View, Child: View>: View {
let label: Label
let child: Child?
@Environment(\.listLevel)
var level
public init(@ViewBuilder label: () -> Label, @ViewBuilder child: () -> Child) {
self.label = label()
self.child = child()
}
public var body: some View {
VStack(alignment: .leading, spacing: 8) {
label
if let child {
child
.environment(\.listLevel, level.next())
}
}
}
}
public extension ListElement where Child == EmptyView {
init(@ViewBuilder label: () -> Label) {
self.label = label()
self.child = nil
}
}
此屏幕截图 显示了当前结果。我为编号文本添加了绿色边框,为列表元素添加了蓝色边框,以更好地显示它们未对齐的情况。我希望能够布局元素,以便同一列表级别中所有元素的前导都对齐。本质上,我希望所有编号文本具有相同的宽度。
从技术上讲,我可以使用 来 Grid
帮助实现这种布局。但是,我认为这行不通,因为我希望能够在需要时嵌套多个列表。我认为解决方案可能是使用自定义 HorizontalAlignment
指南或自定义 Layout
,但我不太明白。