From 6200bdf1541d9512ca45aaf6f3cc154d631b6402 Mon Sep 17 00:00:00 2001 From: Kyle Date: Sun, 1 Nov 2026 01:24:55 +0800 Subject: [PATCH 1/7] feat: add subview APIs --- .../OpenSwiftUI/View/Subview/Subview.swift | 16 +++ .../View/Subview/Subview.swift | 101 ++++++++++++++++++ 2 files changed, 117 insertions(+) create mode 100644 Sources/OpenSwiftUI/View/Subview/Subview.swift create mode 100644 Sources/OpenSwiftUICore/View/Subview/Subview.swift diff --git a/Sources/OpenSwiftUI/View/Subview/Subview.swift b/Sources/OpenSwiftUI/View/Subview/Subview.swift new file mode 100644 index 000000000..01801368b --- /dev/null +++ b/Sources/OpenSwiftUI/View/Subview/Subview.swift @@ -0,0 +1,16 @@ +// +// Subview.swift +// OpenSwiftUI +// +// Audited for 6.5.4 +// Status: Complete + +@_spi(ForOpenSwiftUIOnly) public import OpenSwiftUICore + +@_spi(DoNotImportVariadicView) +extension Subview { + @available(*, deprecated, renamed: "containerValues") + public var traits: TraitValues { + TraitValues(base: base.traits) + } +} diff --git a/Sources/OpenSwiftUICore/View/Subview/Subview.swift b/Sources/OpenSwiftUICore/View/Subview/Subview.swift new file mode 100644 index 000000000..1b5b4e1a4 --- /dev/null +++ b/Sources/OpenSwiftUICore/View/Subview/Subview.swift @@ -0,0 +1,101 @@ +// +// Subview.swift +// OpenSwiftUICore +// +// Audited for 6.5.4 +// Status: Complete +// ID: AC3F3B54DEA93347066C3491F9AA3DD4 (SwiftUICore) + +import OpenAttributeGraphShims + +// MARK: - Subview + +@available(OpenSwiftUI_v6_0, *) +public struct Subview: View, UnaryView, PrimitiveView, Identifiable { + public struct ID: Hashable { + let base: ViewList.ID + } + + package var base: _VariadicView.Children.Element + + package init(_ base: _VariadicView.Children.Element) { + self.base = base + } + + public var id: ID { + ID(base: base.view.subviewID) + } + + public var containerValues: ContainerValues { + ContainerValues(base: base.traits) + } + + @_spi(DoNotImportVariadicView) + @available(*, deprecated, message: "Use tag APIs on ContainerValues instead.") + public func isSelected(selection: V) -> Bool where V: Hashable { + guard let tag = base.traits.tag(for: V.self) else { + return false + } + return tag == selection + } + + @_spi(DoNotImportVariadicView) + @available(*, deprecated, message: "Use tag APIs on ContainerValues instead.") + public func tagValue(for type: V.Type) -> V? where V: Hashable { + base.traits.tagValue(for: type) + } + + package subscript(key: Trait.Type) -> Trait.Value where Trait: _ViewTraitKey { + base[key] + } + + nonisolated public static func _makeView( + view: _GraphValue, + inputs: _ViewInputs + ) -> _ViewOutputs { + let base = view[offset: { .of(&$0.base) }] + return ViewList.View._makeView( + view: base[offset: { .of(&$0.view) }], + inputs: inputs + ) + } + + nonisolated public static func _makeViewList( + view: _GraphValue, + inputs: _ViewListInputs + ) -> _ViewListOutputs { + var inputs = inputs + inputs.traits = Attribute(MergeTraits( + overrideTraits: view[offset: { .of(&$0.base.traits) }].value, + baseTraits: OptionalAttribute(inputs.traits) + )) + return .unaryViewList(view: view, inputs: inputs) + } +} + +@available(*, unavailable) +extension Subview: Sendable {} + +@available(*, unavailable) +extension Subview.ID: Sendable {} + +// MARK: - Subview.ID + HasCustomIDRepresentation + +extension Subview.ID: HasCustomIDRepresentation { + func containsID(_ id: ID) -> Bool where ID: Hashable { + base.containsID(id) + } +} + +// MARK: - MergeTraits + +private struct MergeTraits: Rule, AsyncAttribute { + @Attribute var overrideTraits: ViewTraitCollection + @OptionalAttribute var baseTraits: ViewTraitCollection? + + var value: ViewTraitCollection { + var traits = baseTraits ?? ViewTraitCollection() + traits.mergeValues(overrideTraits) + return traits + } +} From a4846186a5d7538a29f1cfb292a049599cb2d4f1 Mon Sep 17 00:00:00 2001 From: Kyle Date: Sun, 1 Nov 2026 01:31:37 +0800 Subject: [PATCH 2/7] feat: add ForEach subview APIs --- .../View/Subview/ForEach+Subviews.swift | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 Sources/OpenSwiftUICore/View/Subview/ForEach+Subviews.swift diff --git a/Sources/OpenSwiftUICore/View/Subview/ForEach+Subviews.swift b/Sources/OpenSwiftUICore/View/Subview/ForEach+Subviews.swift new file mode 100644 index 000000000..cd113259b --- /dev/null +++ b/Sources/OpenSwiftUICore/View/Subview/ForEach+Subviews.swift @@ -0,0 +1,87 @@ +// +// ForEach+Subviews.swift +// OpenSwiftUICore +// +// Audited for 6.5.4 +// Status: Complete + +// MARK: - ForEach + Subviews + +@available(OpenSwiftUI_v6_0, *) +extension ForEach { + @_spi(_) + @available(*, deprecated, renamed: "init(subviews:content:)") + public init( + elementOf view: V, + @ViewBuilder content: @escaping (Subview) -> Content + ) where Data == ForEachSubviewCollection, ID == Subview.ID, Content: View, V: View { + self.init(subviews: view, content: content) + } + + @_spi(_) + @available(*, deprecated, renamed: "init(subviews:content:)") + public init( + subviewOf view: V, + @ViewBuilder content: @escaping (Subview) -> Content + ) where Data == ForEachSubviewCollection, ID == Subview.ID, Content: View, V: View { + self.init(subviews: view, content: content) + } + + /// Creates an instance that uniquely identifies and creates views across + /// updates based on the subviews of a given view. + /// + /// Subviews are proxies to the resolved view they represent, meaning + /// that modifiers applied to the original view will be applied before + /// modifiers applied to the subview, and the view is resolved + /// using the environment of its container, *not* the environment of + /// its subview proxy. Additionally, because subviews must represent a + /// single leaf view, or container, a subview may represent a view after the + /// application of styles. As such, attempting to apply a style to it may + /// have no effect. + /// + /// - Parameters: + /// - view: The view to extract the subviews of. + /// - content: The view builder that creates views from subviews. + public init( + subviews view: V, + @ViewBuilder content: @escaping (Subview) -> Content + ) where Data == ForEachSubviewCollection, ID == Subview.ID, Content: View, V: View { + self.init( + ForEachSubviewCollection(elementOf: view, content: content), + id: \.id, + content: content + ) + } +} + +// MARK: - ForEachSubviewCollection + +/// A collection which allows a view to be treated as a collection of its +/// subviews in a for each loop. +@available(OpenSwiftUI_v6_0, *) +public struct ForEachSubviewCollection: RandomAccessCollection where Content: View { + var substituteView: AnyView + + init(elementOf view: V, content: @escaping (Subview) -> Content) where V: View { + substituteView = AnyView(GroupElementsOfContent(subviews: view) { subviews in + ForEach(subviews) { subview in + content(subview) + } + }) + } + + public var startIndex: Int { + _openSwiftUIUnreachableCode() + } + + public var endIndex: Int { + _openSwiftUIUnreachableCode() + } + + public subscript(index: Int) -> Subview { + _openSwiftUIUnreachableCode() + } +} + +@available(*, unavailable) +extension ForEachSubviewCollection: Sendable {} From 260ec198e057eea524f5a6d8ba8f51bdbc9e0784 Mon Sep 17 00:00:00 2001 From: Kyle Date: Sun, 1 Nov 2026 02:01:03 +0800 Subject: [PATCH 3/7] feat: add Group subview APIs --- .../View/Subview/Group+Subviews.swift | 268 ++++++++++++++++++ 1 file changed, 268 insertions(+) create mode 100644 Sources/OpenSwiftUICore/View/Subview/Group+Subviews.swift diff --git a/Sources/OpenSwiftUICore/View/Subview/Group+Subviews.swift b/Sources/OpenSwiftUICore/View/Subview/Group+Subviews.swift new file mode 100644 index 000000000..44308560a --- /dev/null +++ b/Sources/OpenSwiftUICore/View/Subview/Group+Subviews.swift @@ -0,0 +1,268 @@ +// +// Group+Subviews.swift +// OpenSwiftUICore +// +// Audited for 6.5.4 +// Status: Complete +// ID: B0B39CD9AD4D5C78B1166BFF4E214610 (SwiftUICore) + +package import OpenAttributeGraphShims + +// MARK: - Group + Subviews + +@available(OpenSwiftUI_v6_0, *) +extension Group { + @_spi(_) + @available(*, deprecated, renamed: "init(subviews:transform:)") + public init( + elementsOf view: Base, + @ViewBuilder transform: @escaping (SubviewsCollection) -> Result + ) where Content == GroupElementsOfContent, Base: View, Result: View { + self.init(subviews: view, transform: transform) + } + + @_spi(_) + @available(*, deprecated, renamed: "init(subviews:transform:)") + public init( + subviewsOf view: Base, + @ViewBuilder transform: @escaping (SubviewsCollection) -> Result + ) where Content == GroupElementsOfContent, Base: View, Result: View { + self.init(subviews: view, transform: transform) + } + + /// Constructs a group from the subviews of the given view. + /// + /// Use this initializer to create a group that gives you programmatic + /// access to the group's subviews. The following `CardsView` defines the + /// group's structure based on the set of views that you provide to it: + /// + /// struct CardsView: View { + /// var content: Content + /// + /// init(@ViewBuilder content: () -> Content) { + /// self.content = content() + /// } + /// + /// var body: some View { + /// VStack { + /// Group(subviews: content) { subviews in + /// HStack { + /// if subviews.count >= 2 { + /// SecondaryCard { subviews[1] } + /// } + /// if let first = subviews.first { + /// FeatureCard { first } + /// } + /// if subviews.count >= 3 { + /// SecondaryCard { subviews[2] } + /// } + /// } + /// if subviews.count > 3 { + /// subviews[3...] + /// } + /// } + /// } + /// } + /// } + /// + /// You can use `CardsView` with its view builder-based initializer to + /// arrange a collection of subviews: + /// + /// CardsView { + /// NavigationLink("What's New!") { WhatsNewView() } + /// NavigationLink("Latest Hits") { LatestHitsView() } + /// NavigationLink("Favorites") { FavoritesView() } + /// NavigationLink("Playlists") { MyPlaylists() } + /// } + /// + /// Subviews collection constructs subviews on demand, so only access the + /// part of the collection you need to create the resulting content. + /// + /// Subviews are proxies to the view they represent, which means + /// that modifiers that you apply to the original view take effect before + /// modifiers that you apply to the subview. OpenSwiftUI resolves the view + /// using the environment of its container rather than the environment of + /// its subview proxy. Additionally, because subviews represent a + /// single view or container, a subview might represent a view after the + /// application of styles. As a result, applying a style to a subview might + /// have no effect. + /// + /// - Parameters: + /// - view: The view to get the subviews of. + /// - transform: A closure that constructs a view from the collection of + /// subviews. + public init( + subviews view: Base, + @ViewBuilder transform: @escaping (SubviewsCollection) -> Result + ) where Content == GroupElementsOfContent, Base: View, Result: View { + self.init { + GroupElementsOfContent(subviews: view, content: transform) + } + } +} + +// MARK: - GroupElementsOfContent + +/// Transforms the subviews of a given view into a resulting content view. +/// +/// You don't use this type directly. Instead OpenSwiftUI creates this type on +/// your behalf. +@available(OpenSwiftUI_v6_0, *) +public struct GroupElementsOfContent: View where Subviews: View, Content: View { + enum Storage { + case subviewsCollection(SubviewsCollection) + case view(Subviews) + } + + let storage: Storage + let content: (SubviewsCollection) -> Content + + @usableFromInline + package init(subviews: Subviews, content: @escaping (SubviewsCollection) -> Content) { + if let subviews = subviews as? SubviewsCollection { + storage = .subviewsCollection(subviews) + } else { + storage = .view(subviews) + } + self.content = content + } + + public var body: some View { + switch storage { + case let .subviewsCollection(subviews): + content(subviews) + case let .view(view): + _VariadicView.Tree(SubviewsRoot(content: content)) { + view + } + } + } +} + +@available(*, unavailable) +extension GroupElementsOfContent: Sendable {} + +// MARK: - SubviewsCollection + +/// An opaque collection representing the subviews of a view. +/// +/// Subviews collection constructs subviews on demand, so only access the part +/// of the collection you need to create the resulting content. +/// +/// You can get access to a view's subview collection by using the +/// ``Group/init(subviews:transform:)`` initializer. +/// +/// The collection's elements are the pieces that make up the given view, and +/// the collection as a whole acts as a proxy for the original view. +@available(OpenSwiftUI_v6_0, *) +public struct SubviewsCollection: RandomAccessCollection { + package var base: _VariadicView.Children + + package init(_ base: _VariadicView.Children) { + self.base = base + } + + package init( + list: any ViewList, + contentSubgraph: Subgraph, + transform: ViewList.SublistTransform + ) { + base = _VariadicView.Children(list, contentSubgraph: contentSubgraph, transform: transform) + } + + public func index(before i: Int) -> Int { + base.index(before: i) + } + + public func index(after i: Int) -> Int { + base.index(after: i) + } + + public subscript(index: Int) -> Subview { + Subview(base[index]) + } + + public subscript(bounds: Range) -> SubviewsCollectionSlice { + SubviewsCollectionSlice(base: Slice(base: self, bounds: bounds)) + } + + public var startIndex: Int { base.startIndex } + + public var endIndex: Int { base.endIndex } +} + +@available(*, unavailable) +extension SubviewsCollection: Sendable {} + +@available(OpenSwiftUI_v6_0, *) +extension SubviewsCollection: View, MultiView, PrimitiveView { + nonisolated public static func _makeViewList( + view: _GraphValue, + inputs: _ViewListInputs + ) -> _ViewListOutputs { + _VariadicView.Children._makeViewList( + view: view[offset: { .of(&$0.base) }], + inputs: inputs + ) + } + + nonisolated public static func _viewListCount(inputs: _ViewListCountInputs) -> Int? { + nil + } +} + +// MARK: - SubviewsCollectionSlice + +/// A slice of a SubviewsCollection. +@available(OpenSwiftUI_v6_0, *) +public struct SubviewsCollectionSlice: RandomAccessCollection { + package var base: Slice + + public subscript(index: Int) -> Subview { + base[index] + } + + public subscript(bounds: Range) -> SubviewsCollectionSlice { + SubviewsCollectionSlice(base: Slice(base: base.base, bounds: bounds)) + } + + public var startIndex: Int { base.startIndex } + + public var endIndex: Int { base.endIndex } +} + +@available(*, unavailable) +extension SubviewsCollectionSlice: Sendable {} + +@available(OpenSwiftUI_v6_0, *) +extension SubviewsCollectionSlice: View, MultiView, PrimitiveView { + nonisolated public static func _makeViewList( + view: _GraphValue, + inputs: _ViewListInputs + ) -> _ViewListOutputs { + let child = _GraphValue(Child(slice: view[offset: { .of(&$0.base) }].value)) + return ForEach._makeViewList(view: child, inputs: inputs) + } + + nonisolated public static func _viewListCount(inputs: _ViewListCountInputs) -> Int? { + nil + } + + private struct Child: Rule, AsyncAttribute { + @Attribute var slice: Slice + + var value: ForEach, Subview.ID, Subview> { + ForEach(slice) { $0 } + } + } +} + +// MARK: - SubviewsRoot + +package struct SubviewsRoot: _VariadicView.MultiViewRoot where Content: View { + package let content: (SubviewsCollection) -> Content + + package func body(children: _VariadicView.Children) -> some View { + content(SubviewsCollection(children)) + } +} From db89e9b9d442e88936d31527ba9447f191b3d504 Mon Sep 17 00:00:00 2001 From: Kyle Date: Sun, 1 Nov 2026 02:04:46 +0800 Subject: [PATCH 4/7] feat: add legacy SubviewCollection compatibility --- .../View/Subview/SubviewCollection.swift | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 Sources/OpenSwiftUI/View/Subview/SubviewCollection.swift diff --git a/Sources/OpenSwiftUI/View/Subview/SubviewCollection.swift b/Sources/OpenSwiftUI/View/Subview/SubviewCollection.swift new file mode 100644 index 000000000..4d1b9476e --- /dev/null +++ b/Sources/OpenSwiftUI/View/Subview/SubviewCollection.swift @@ -0,0 +1,90 @@ +// +// SubviewCollection.swift +// OpenSwiftUI +// +// Audited for 6.5.4 +// Status: Complete + +@_spi(ForOpenSwiftUIOnly) public import OpenSwiftUICore + +// MARK: - Group + SubviewCollection + +@_spi(DoNotImportVariadicView) +extension Group { + @available(*, deprecated, message: "Use the public API instead.") + @_disfavoredOverload + public init( + elementsOf view: Base, + @ViewBuilder transform: @escaping (SubviewCollection) -> Result + ) where Content == GroupElementsOfContent, Base: View, Result: View { + self.init { + GroupElementsOfContent(subviews: view) { subviews in + transform(SubviewCollection(subviews)) + } + } + } +} + +// MARK: - SubviewCollection + +@_spi(DoNotImportVariadicView) +@available(*, deprecated, renamed: "SubviewsCollection") +public struct SubviewCollection: RandomAccessCollection { + package var base: _VariadicView.Children + + @usableFromInline + init(_ subviews: SubviewsCollection) { + base = subviews.base + } + + public subscript(index: Int) -> Subview { + Subview(base[index]) + } + + public subscript(bounds: Range) -> Slice { + Slice(base: self, bounds: bounds) + } + + public var startIndex: Int { base.startIndex } + + public var endIndex: Int { base.endIndex } + + public func selectedSubview(selection: V) -> Subview? where V: Hashable { + var start = 0 + var index = 0 + var selectedIndex: Int? + base.list.applySublists(from: &start, list: nil) { sublist in + if sublist.traits.tag(for: V.self) == selection, + !sublist.traits.isAuxiliaryContent { + selectedIndex = index + return false + } + index &+= sublist.count + return true + } + return selectedIndex.map { Subview(base[$0]) } + } +} + +@_spi(DoNotImportVariadicView) +@available(*, unavailable) +extension SubviewCollection: Sendable {} + +@_spi(DoNotImportVariadicView) +@available(OpenSwiftUI_v6_0, *) +extension SubviewCollection: View, MultiView, PrimitiveView { + public var body: some View { + base + } + + nonisolated public static func _makeViewList( + view: _GraphValue, + inputs: _ViewListInputs + ) -> _ViewListOutputs { + _VariadicView.Children._makeViewList(view: view[\.base], inputs: inputs) + } + + nonisolated public static func _viewListCount(inputs: _ViewListCountInputs) -> Int? { + _VariadicView.Children._viewListCount(inputs: inputs) + } +} From 70c31f236a4ca0b01e7d6179fee9f94fb61e85b9 Mon Sep 17 00:00:00 2001 From: Kyle Date: Sun, 1 Nov 2026 13:28:30 +0800 Subject: [PATCH 5/7] feat: implement view list ID traversal --- .../OpenSwiftUICore/View/Input/ViewList.swift | 60 +++++++++++++++---- 1 file changed, 48 insertions(+), 12 deletions(-) diff --git a/Sources/OpenSwiftUICore/View/Input/ViewList.swift b/Sources/OpenSwiftUICore/View/Input/ViewList.swift index 0e0346349..14ecc9ddb 100644 --- a/Sources/OpenSwiftUICore/View/Input/ViewList.swift +++ b/Sources/OpenSwiftUICore/View/Input/ViewList.swift @@ -647,28 +647,65 @@ extension ViewList { transform: inout ViewList.SublistTransform, to body: (ViewList.ID) -> Bool ) -> Bool { - guard style.granularity == 1, let viewIDs else { + guard style == .init(), let viewIDs else { return applyNodes( from: &start, style: style, list: listAttribute, transform: &transform ) { start, style, node, transform in - _openSwiftUIUnimplementedWarning() - return false + switch node { + case let .list(list, attribute): + return list.applyIDs(from: &start, style: style, listAttribute: attribute, transform: &transform, to: body) + case var .sublist(sublist): + let count = style.applyGranularity ? style.granularity * sublist.count : sublist.count + guard start < count else { + start -= count + return true + } + defer { start = 0 } + transform.apply(sublist: &sublist) + for id in sublist.id.elementIDs(count: sublist.count) { + guard body(id) else { return false } + } + return true + case let .group(group): + for (list, attribute) in group.lists { + guard list.applyIDs(from: &start, style: style, listAttribute: attribute, transform: &transform, to: body) else { + return false + } + } + return true + case let .section(section): + for (list, attribute) in section.base.lists.prefix(section.isHierarchical ? 1 : .max) { + guard list.applyIDs(from: &start, style: style, listAttribute: attribute, transform: &transform, to: body) else { + return false + } + } + return true + } } } - _openSwiftUIUnimplementedWarning() - return false + guard start < viewIDs.count else { + start -= viewIDs.count + return true + } + defer { start = 0 } + for var id in viewIDs.dropFirst(start) { + transform.bindID(&id) + guard body(id) else { return false } + } + return true } package func applyIDs( from start: inout Int, listAttribute: Attribute?, - transform t: inout ViewList.SublistTransform, + transform: ViewList.SublistTransform, to body: (ViewList.ID) -> Bool ) -> Bool { - applyIDs(from: &start, style: .init(), listAttribute: listAttribute, transform: &t, to: body) + var transform = transform + return applyIDs(from: &start, style: .init(), listAttribute: listAttribute, transform: &transform, to: body) } package func applyIDs( @@ -676,16 +713,15 @@ extension ViewList { listAttribute: Attribute?, to body: (ViewList.ID) -> Bool ) -> Bool { - var t = ViewList.SublistTransform() - return applyIDs(from: &start, listAttribute: listAttribute, transform: &t, to: body) + applyIDs(from: &start, listAttribute: listAttribute, transform: .init(), to: body) } package func applyIDs( from start: inout Int, - transform t: inout ViewList.SublistTransform, + transform: ViewList.SublistTransform, to body: (ViewList.ID) -> Bool ) -> Bool { - applyIDs(from: &start, listAttribute: nil, transform: &t, to: body) + applyIDs(from: &start, listAttribute: nil, transform: transform, to: body) } package func firstOffset( @@ -1860,7 +1896,7 @@ package struct EmptyViewList: ViewList { package var traitKeys: ViewTraitKeys? { .init() } - package var viewIDs: ID.Views? { .init(isDataDependent: false) } + package var viewIDs: ID.Views? { ID._Views(EmptyCollection(), isDataDependent: false) } package var traits: Traits { .init() } From ac9b67499656fe88d854c03dcfbeb26d9a38365e Mon Sep 17 00:00:00 2001 From: Kyle Date: Sun, 1 Nov 2026 13:45:35 +0800 Subject: [PATCH 6/7] feat: add section extraction APIs --- .../View/Input/SectionAccumulator.swift | 325 +++++++++++++++ .../View/Subview/ForEach+Sections.swift | 70 ++++ .../View/Subview/Group+Sections.swift | 390 ++++++++++++++++++ 3 files changed, 785 insertions(+) create mode 100644 Sources/OpenSwiftUICore/View/Input/SectionAccumulator.swift create mode 100644 Sources/OpenSwiftUICore/View/Subview/ForEach+Sections.swift create mode 100644 Sources/OpenSwiftUICore/View/Subview/Group+Sections.swift diff --git a/Sources/OpenSwiftUICore/View/Input/SectionAccumulator.swift b/Sources/OpenSwiftUICore/View/Input/SectionAccumulator.swift new file mode 100644 index 000000000..5fc3a169a --- /dev/null +++ b/Sources/OpenSwiftUICore/View/Input/SectionAccumulator.swift @@ -0,0 +1,325 @@ +// +// SectionAccumulator.swift +// OpenSwiftUICore +// +// Audited for 6.5.4 +// Status: Complete +// ID: D22569EAA67164B36532D028EDA82857 (SwiftUICore) + +package import OpenAttributeGraphShims + +// MARK: - SectionAccumulator + +package struct SectionAccumulator { + private var pendingChunks: [RowIDs.Chunk] = [] + private var pendingChunkIDCount: Int = 0 + private var lastExplicitSectionEnd: Int = 0 + private var list: (any ViewList)? + private var contentSubgraph: Subgraph? + package private(set) var items: [Item] = [] + package private(set) var viewCount: Int = 0 + private var pendingEmptySectionTraits: [ViewTraitCollection] = [] + + package init(contentSubgraph: Subgraph?) { + self.contentSubgraph = contentSubgraph + } + + package static func processUnsectionedContent( + list: any ViewList, + contentSubgraph: Subgraph? + ) -> [Item]? { + guard let keys = list.traitKeys, !keys.contains(IsSectionedTraitKey.self) else { + return nil + } + if let ids = list.viewIDs, ids.isEmpty { + return [] + } + return [.implicitSentinel(list, contentSubgraph: contentSubgraph)] + } + + package mutating func formResult(from list: any ViewList, listAttribute: Attribute?) { + self.list = list + Update.begin() + var start = 0 + var transform = ViewList.SublistTransform() + list.applyNodes(from: &start, style: .init(), list: listAttribute, transform: &transform) { + apply(start: &$0, style: $1, node: $2, transform: &$3) + } + if lastExplicitSectionEnd < viewCount { + appendImplicitSection() + } + if items.isEmpty { + items = viewCount > 0 ? [.implicitSentinel(list, contentSubgraph: nil)] : [] + } + Update.end() + self.list = nil + } + + private mutating func apply( + start: inout Int, + style: ViewList.IteratorStyle, + node: ViewList.Node, + transform: inout ViewList.SublistTransform + ) -> Bool { + switch node { + case let .list(list, attribute): + guard let keys = list.traitKeys, !keys.contains(IsSectionedTraitKey.self) else { + return list.applyNodes(from: &start, style: style, list: attribute, transform: &transform) { + apply(start: &$0, style: $1, node: $2, transform: &$3) + } + } + let count = list.count + pendingChunks.append(RowIDs.Chunk( + list: list, + listAttribute: attribute, + transform: transform, + start: start, + count: count, + lowerBound: pendingChunkIDCount + )) + pendingChunkIDCount += count + viewCount += count + case var .sublist(sublist): + transform.apply(sublist: &sublist) + pendingChunks.append(RowIDs.Chunk( + ids: .sublist(sublist.id.elementIDs(count: sublist.count)), + count: sublist.count, + lowerBound: pendingChunkIDCount + )) + pendingChunkIDCount += sublist.count + viewCount += sublist.count + case let .group(group): + for (list, attribute) in group.lists { + guard apply(start: &start, style: style, node: .list(list, attribute), transform: &transform) else { + return false + } + } + case let .section(section): + if lastExplicitSectionEnd < viewCount { + appendImplicitSection() + } + let count = section.count + if count > 0 { + items.append(Item( + features: [], + list: section, + contentSubgraph: contentSubgraph, + sectionList: section, + transform: transform, + ids: RowIDs( + list: section.content.list, + listAttribute: section.content.attribute, + start: 0, + count: section.content.list.count + ), + headerCount: section.header.list.count, + footerCount: section.footer.list.count, + id: section.id, + start: 0, + traits: pendingEmptySectionTraits + [section.traits] + )) + pendingEmptySectionTraits.removeAll(keepingCapacity: true) + viewCount += count + lastExplicitSectionEnd = viewCount + } else { + lastExplicitSectionEnd = viewCount + if items.isEmpty { + pendingEmptySectionTraits.append(section.traits) + } else { + items[items.count - 1].traits.append(section.traits) + } + } + } + return true + } + + private mutating func appendImplicitSection() { + let list = list! + items.append(Item( + features: .implicit, + list: list, + contentSubgraph: contentSubgraph, + sectionList: nil, + transform: .init(), + ids: RowIDs(chunks: pendingChunks), + headerCount: 0, + footerCount: 0, + id: UInt32(items.count), + start: lastExplicitSectionEnd, + traits: [list.traits] + )) + pendingChunks.removeAll(keepingCapacity: true) + pendingChunkIDCount = 0 + } +} + +// MARK: - SectionAccumulator.Item + +extension SectionAccumulator { + package struct Item { + package var features: Features + package var list: any ViewList + package var contentSubgraph: Subgraph? + package var sectionList: ViewList.Section? + package var transform: ViewList.SublistTransform + package var ids: RowIDs + package var headerCount: Int + package var footerCount: Int + package var id: UInt32 + package var start: Int + package var traits: [ViewTraitCollection] + + package var count: Int { ids.count } + + package var hasRows: Bool { count > 0 } + + package struct Features: OptionSet { + package let rawValue: UInt8 + + package init(rawValue: UInt8) { + self.rawValue = rawValue + } + + package static let implicit = Features(rawValue: 1 << 0) + } + + package static func implicitSentinel(_ list: any ViewList, contentSubgraph: Subgraph?) -> Item { + let count = Update.ensure { list.count } + return Item( + features: .implicit, + list: list, + contentSubgraph: contentSubgraph, + sectionList: nil, + transform: .init(), + ids: RowIDs(list: list, listAttribute: nil, start: 0, count: count), + headerCount: 0, + footerCount: 0, + id: 0, + start: 0, + traits: [list.traits] + ) + } + } +} + +// MARK: - SectionAccumulator.RowIDs + +extension SectionAccumulator { + package struct RowIDs: RandomAccessCollection { + let chunks: [Chunk] + + package init(ids: [ViewList.ID]) { + chunks = [Chunk(ids: .idArray(ids), count: ids.count, lowerBound: 0)] + } + + package init(list: any ViewList, listAttribute: Attribute?, start: Int, count: Int) { + chunks = [Chunk( + list: list, + listAttribute: listAttribute, + transform: .init(), + start: start, + count: count, + lowerBound: 0 + )] + } + + init(chunks: [Chunk]) { + self.chunks = chunks + } + + package var startIndex: Int { chunks.first?.lowerBound ?? 0 } + + package var endIndex: Int { + guard let last = chunks.last else { return 0 } + return last.lowerBound + last.count + } + + package subscript(index: Int) -> ViewList.ID.Canonical { + for chunk in chunks where index < chunk.lowerBound + chunk.count { + let index = index - chunk.lowerBound + return switch chunk.ids { + case let .viewListIDs(ids): ids[index].canonicalID + case let .idArray(ids): ids[index].canonicalID + case let .sublist(ids): ids[index].canonicalID + } + } + _openSwiftUIUnreachableCode() + } + + enum IDs { + case viewListIDs(ViewList.ID.Views) + case idArray([ViewList.ID]) + case sublist(ViewList.ID.ElementCollection) + } + + struct Chunk { + var ids: IDs + var count: Int + var lowerBound: Int + + init(ids: IDs, count: Int, lowerBound: Int) { + self.ids = ids + self.count = count + self.lowerBound = lowerBound + } + + init( + list: any ViewList, + listAttribute: Attribute?, + transform: ViewList.SublistTransform, + start: Int, + count: Int, + lowerBound: Int + ) { + if let viewIDs = list.viewIDs { + ids = .viewListIDs(transform.isEmpty ? viewIDs : ViewList.ID._Views( + TransformedIDs(base: viewIDs, transform: transform), + isDataDependent: true + )) + } else { + var array: [ViewList.ID] = [] + array.reserveCapacity(count) + var remaining = count + var start = start + _ = list.applyIDs(from: &start, listAttribute: listAttribute, transform: transform) { id in + array.append(id) + remaining -= 1 + return remaining != 0 + } + ids = .idArray(array) + } + self.count = count + self.lowerBound = lowerBound + } + } + } +} + +// MARK: - SectionAccumulator.TransformedIDs + +extension SectionAccumulator { + struct TransformedIDs: RandomAccessCollection, Equatable { + var base: ViewList.ID.Views + var transform: ViewList.SublistTransform + + var startIndex: Int { base.startIndex } + var endIndex: Int { base.endIndex } + + subscript(index: Int) -> ViewList.ID { + var sublist = ViewList.Sublist( + start: 0, + count: 1, + id: base[index], + elements: EmptyViewListElements(), + traits: .init(), + list: nil + ) + transform.apply(sublist: &sublist) + return sublist.id + } + + static func == (lhs: Self, rhs: Self) -> Bool { + lhs.base === rhs.base + } + } +} diff --git a/Sources/OpenSwiftUICore/View/Subview/ForEach+Sections.swift b/Sources/OpenSwiftUICore/View/Subview/ForEach+Sections.swift new file mode 100644 index 000000000..3bc4c3fbf --- /dev/null +++ b/Sources/OpenSwiftUICore/View/Subview/ForEach+Sections.swift @@ -0,0 +1,70 @@ +// +// ForEach+Sections.swift +// OpenSwiftUICore +// +// Audited for 6.5.4 +// Status: Complete + +// MARK: - ForEach + Sections + +@available(OpenSwiftUI_v6_0, *) +extension ForEach { + @_spi(_) + @available(*, deprecated, renamed: "init(sections:content:)") + public init( + sectionOf view: V, + @ViewBuilder content: @escaping (SectionConfiguration) -> Content + ) where Data == ForEachSectionCollection, ID == SectionConfiguration.ID, Content: View, V: View { + self.init(sections: view, content: content) + } + + /// Creates an instance that uniquely identifies and creates views across + /// updates based on the sections of a given view. + /// + /// - Parameters: + /// - view: The view to extract the sections of. + /// - content: The view builder that creates views from sections + public init( + sections view: V, + @ViewBuilder content: @escaping (SectionConfiguration) -> Content + ) where Data == ForEachSectionCollection, ID == SectionConfiguration.ID, Content: View, V: View { + self.init( + ForEachSectionCollection(subviewOf: view, content: content), + id: \.id, + content: content + ) + } +} + +// MARK: - ForEachSectionCollection + +/// A collection which allows a view to be treated as a collection of its +/// sections in a for each loop. +/// +/// You don't use this type directly. Instead OpenSwiftUI creates this type on +/// your behalf. +@available(OpenSwiftUI_v6_0, *) +public struct ForEachSectionCollection: RandomAccessCollection where Content: View { + var substituteView: AnyView + + init(subviewOf view: V, content: @escaping (SectionConfiguration) -> Content) where V: View { + substituteView = AnyView(Group(sections: view) { sections in + ForEach(sections, content: content) + }) + } + + public var startIndex: Int { + _openSwiftUIUnreachableCode() + } + + public var endIndex: Int { + _openSwiftUIUnreachableCode() + } + + public subscript(index: Int) -> SectionConfiguration { + _openSwiftUIUnreachableCode() + } +} + +@available(*, unavailable) +extension ForEachSectionCollection: Sendable {} diff --git a/Sources/OpenSwiftUICore/View/Subview/Group+Sections.swift b/Sources/OpenSwiftUICore/View/Subview/Group+Sections.swift new file mode 100644 index 000000000..207252b37 --- /dev/null +++ b/Sources/OpenSwiftUICore/View/Subview/Group+Sections.swift @@ -0,0 +1,390 @@ +// +// Group+Sections.swift +// OpenSwiftUICore +// +// Audited for 6.5.4 +// Status: Complete + +import OpenAttributeGraphShims + +// MARK: - Group + Sections + +@available(OpenSwiftUI_v6_0, *) +extension Group { + @_spi(_) + @available(*, deprecated, renamed: "init(sections:transform:)") + public init( + sectionsOf view: Base, + @ViewBuilder transform: @escaping (SectionCollection) -> Result + ) where Content == GroupSectionsOfContent, Base: View, Result: View { + self.init(sections: view, transform: transform) + } + + /// Constructs a group from the sections of the given view. + /// + /// Sections are constructed lazily, on demand, so access only as much + /// of this collection as is necessary to create the resulting content. + /// + /// struct SectionedStack: View { + /// var content: Content + /// + /// init(@ViewBuilder content: () -> Content) { + /// self.content = content() + /// } + /// + /// var body: some View { + /// VStack { + /// Group(sections: content) { sections in + /// ForEach(sections) { section in + /// SectionChrome { + /// section.content + /// } header: { + /// section.header + /// } footer: { + /// section.footer + /// } + /// } + /// } + /// } + /// } + /// } + /// + /// This can then be used by creating a `SectionedStack` with its + /// view builder-based initializer. + /// + /// SectionedStack { + /// Section("Header A") { + /// Text("Hello") + /// Text("World") + /// } footer: { + /// Text("Footer A") + /// } + /// Section("Header B") { + /// Text("Foo") + /// Text("Bar") + /// } footer: { + /// Text("Footer B") + /// } + /// } + /// + /// Any content of the given view which is not explicitly specified as a + /// section is grouped with its sibling content to form implicit sections. + /// For example in the following `SectionedStack`, there is one explicit + /// section, and two implicit sections containing the content before, + /// and after the explicit section: + /// + /// SectionedStack { + /// Text("First implicit section") + /// Section("Explicit section") { + /// Text("Content") + /// } + /// Text("Second implicit section") + /// } + /// + /// - Parameters: + /// - view: The view to extract the sections of. + /// - transform: A closure that constructs a view from the collection of + /// sections. + public init( + sections view: Base, + @ViewBuilder transform: @escaping (SectionCollection) -> Result + ) where Content == GroupSectionsOfContent, Base: View, Result: View { + self.init { + GroupSectionsOfContent(sections: view, content: transform) + } + } +} + +// MARK: - GroupSectionsOfContent + +/// Transforms the sections of a given view into a resulting content view. +/// +/// You don't use this type directly. Instead OpenSwiftUI creates this type on +/// your behalf. +@available(OpenSwiftUI_v6_0, *) +public struct GroupSectionsOfContent: View where Sections: View, Content: View { + let sections: Sections + let content: (SectionCollection) -> Content + + init(sections: Sections, content: @escaping (SectionCollection) -> Content) { + if sections is SubviewsCollection { + Log.externalWarning("Group(sections:) or ForEach(sections:) was passed a SubviewsCollection. These APIs do not support nested sectioning. Attempting to use them this way will result in losing sectioning information.") + } + self.sections = sections + self.content = content + } + + public var body: some View { + _VariadicView.Tree(SectionsRoot(content: content)) { + sections + } + } +} + +@available(*, unavailable) +extension GroupSectionsOfContent: Sendable {} + +// MARK: - SectionCollection + +/// An opaque collection representing the sections of view. +/// +/// Sections are constructed lazily, on demand, so access only as much +/// of this collection as is necessary to create the resulting content. +/// +/// You can get access to a view's ``SectionCollection`` by using the +/// ``Group/init(sections:transform:)`` initializer. +/// +/// Any content of the given view which is not explicitly specified as a section +/// is grouped with its sibling content to form implicit sections. +@available(OpenSwiftUI_v6_0, *) +public struct SectionCollection: RandomAccessCollection { + let base: [SectionConfiguration] + + public subscript(index: Int) -> SectionConfiguration { base[index] } + + public var startIndex: Int { base.startIndex } + + public var endIndex: Int { base.endIndex } +} + +@available(*, unavailable) +extension SectionCollection: Sendable {} + +// MARK: - SectionConfiguration + +/// Specifies the contents of a section. +/// +/// A `SectionConfiguration` includes the content of the section, as well as +/// its header and footer. +/// +/// A `SectionConfiguration` can represent either an explicit section, +/// or groups of sibling views that are not explicitly wrapped in a section. +/// +/// Notably, the `header`, `footer` and `content` properties of a +/// `SectionConfiguration` are all `SubviewsCollection`s as they can be made up +/// of multiple subviews. That means in most cases, the subviews collection +/// should be treated as a collection (either indexed into, or used with a +/// `ForEach`), or the subviews collection should be wrapped in a container +/// view, like a layout, or other custom container: +/// +/// PinboardSectionsLayout { +/// ForEach(sections: content) { section in +/// VStack { +/// HStack { section.header } +/// section.content +/// HStack { section.footer } +/// } +/// } +/// } +/// +/// Here, we want to create one view for `PinboardSectionsLayout` to place per +/// section in content. To do that, we surround the `ForEach` body in another +/// container, a `VStack` layout, ensuring the different subviews of +/// section.content are treated as a single view by the surrounding layout. +/// Additionally, surrounding the header and footer in an `HStack` layout +/// avoids vertically stacking subviews of the header and footer +/// which we want visually grouped together. +@available(OpenSwiftUI_v6_0, *) +public struct SectionConfiguration: Identifiable { + let item: SectionAccumulator.Item + + /// A unique identifier for a section. + public struct ID: Hashable { + let base: AnyHashable + } + + /// A unique identifier representing the section. + public var id: ID { ID(base: AnyHashable(item.id)) } + + /// The container values associated with the given section. + /// + /// Only explicitly created sections are able to have container values, + /// meaning this container values will be empty if the section is implicit. + public var containerValues: ContainerValues { + ContainerValues(base: item.sectionList?.traits ?? ViewTraitCollection()) + } + + var hasSubsections: Bool { + !item.features.contains(.implicit) && item.hasRows + } + + /// The contents of the section header. + /// + /// Notably, the section's header is a `SubviewsCollection`, not a + /// `Subview`, as it can be made up of multiple subviews. That means in most + /// cases, the subviews collection should be treated as a collection (either + /// indexed into, or used with a `ForEach`), or the subviews collection + /// should be wrapped in a container view, like a layout, or other custom + /// container: + /// + /// ForEach(sections: content) { section in + /// VStack { + /// HStack { section.header } + /// HStack { section.footer } + /// } + /// } + /// + /// Here, we surround the header and footer in an `HStack` layout to avoid + /// vertically stacking the subviews of the header and footer which we want + /// visually grouped together. Additionally, we surround the `ForEach` body + /// in a VStack, so it is treated as a single view by containers it gets + /// passed to. + public var header: SubviewsCollection { + if item.headerCount > 0, let section = item.sectionList { + SubviewsCollection( + list: section.header.list, + contentSubgraph: item.contentSubgraph!, + transform: item.transform + ) + } else { + SubviewsCollection(list: EmptyViewList(), contentSubgraph: item.contentSubgraph!, transform: .init()) + } + } + + /// The contents of the section footer. + /// + /// Notably, the section's footer is a `SubviewsCollection`, not a + /// `Subview`, as it can be made up of multiple subviews. That means in most + /// cases, the subviews collection should be treated as a collection (either + /// indexed into, or used with a `ForEach`), or the subviews collection + /// should be wrapped in a container view, like a layout, or other custom + /// container: + /// + /// ForEach(sections: content) { section in + /// VStack { + /// HStack { section.header } + /// HStack { section.footer } + /// } + /// } + /// + /// Here, we surround the header and footer in an `HStack` layout to avoid + /// vertically stacking the subviews of the header and footer which we want + /// visually grouped together. Additionally, we surround the `ForEach` body + /// in a VStack, so it is treated as a single view by containers it gets + /// passed to. + public var footer: SubviewsCollection { + if item.footerCount > 0, let section = item.sectionList { + SubviewsCollection( + list: section.footer.list, + contentSubgraph: item.contentSubgraph!, + transform: item.transform + ) + } else { + SubviewsCollection(list: EmptyViewList(), contentSubgraph: item.contentSubgraph!, transform: .init()) + } + } + + /// The contents of the section body. + /// + /// Notably, the section's content is a `SubviewsCollection`, not a + /// `Subview`, as it can be made up of multiple subviews. That means in most + /// cases, the subviews collection should be treated as a collection (either + /// indexed into, or used with a `ForEach`), or the subviews collection + /// should be wrapped in a container view, like a layout, or other custom + /// container: + /// + /// PinboardSectionsLayout { + /// ForEach(sections: content) { section in + /// VStack { + /// section.content + /// } + /// } + /// } + /// + /// Here, we want to create one view for `PinboardSectionsLayout` to place + /// per section in content. To do that, we surround the `ForEach` body in + /// another container, a `VStack` layout, ensuring the different subviews of + /// section.content are treated as a single view by the surrounding layout. + public var content: SubviewsCollection { + if let section = item.sectionList { + SubviewsCollection( + list: section.content.list, + contentSubgraph: item.contentSubgraph!, + transform: item.transform + ) + } else { + SubviewsCollection( + list: ViewListSlice(base: item.list, bounds: item.start ..< item.start + item.count), + contentSubgraph: item.contentSubgraph!, + transform: item.transform + ) + } + } +} + +@available(*, unavailable) +extension SectionConfiguration: Sendable {} + +@available(*, unavailable) +extension SectionConfiguration.ID: Sendable {} + +// MARK: - SectionsRoot + +struct SectionsRoot: _VariadicView.MultiViewRoot where Content: View { + var content: (SectionCollection) -> Content + + static var _viewListOptions: Int { + Int(_ViewListInputs.Options([ + .requiresDepthAndSections, + .requiresSections, + .allowsNestedSections, + ]).rawValue) + } + + nonisolated static func _makeView( + root: _GraphValue, + inputs: _ViewInputs, + body: (_Graph, _ViewInputs) -> _ViewListOutputs + ) -> _ViewOutputs { + withoutActuallyEscaping(body) { body in + .multiView(inputs: inputs) { graph, inputs in + let outputs = body(graph, inputs) + let list = outputs.makeAttribute(inputs: _ViewListInputs(inputs.base)) + let child = _GraphValue(Child( + view: root.value, + viewList: list, + contentSubgraph: Subgraph.current! + )) + return Content.makeDebuggableViewList(view: child, inputs: inputs.implicitRootBodyInputs) + } + } + } + + nonisolated static func _makeViewList( + root: _GraphValue, + inputs: _ViewListInputs, + body: (_Graph, _ViewListInputs) -> _ViewListOutputs + ) -> _ViewListOutputs { + let outputs = body(.init(), inputs) + let list = outputs.makeAttribute(inputs: inputs) + let child = _GraphValue(Child( + view: root.value, + viewList: list, + contentSubgraph: Subgraph.current! + )) + return Content.makeDebuggableViewList(view: child, inputs: inputs) + } + + typealias Body = Never + + struct Child: Rule { + @Attribute var view: SectionsRoot + @Attribute var viewList: any ViewList + var contentSubgraph: Subgraph + + var value: Content { + let items: [SectionAccumulator.Item] + if let unsectioned = SectionAccumulator.processUnsectionedContent( + list: viewList, + contentSubgraph: contentSubgraph + ) { + items = unsectioned + } else { + var accumulator = SectionAccumulator(contentSubgraph: contentSubgraph) + accumulator.formResult(from: viewList, listAttribute: $viewList) + items = accumulator.items + } + return view.content(SectionCollection(base: items.map { SectionConfiguration(item: $0) })) + } + } +} From d58b98f3c75a91514c67d776e9ab0a0a0c78cca2 Mon Sep 17 00:00:00 2001 From: Kyle Date: Sun, 1 Nov 2026 19:00:50 +0800 Subject: [PATCH 7/7] feat: render ForEach subview and section collections --- .../View/DynamicViewContent/ForEach.swift | 34 +++++++++++++++---- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/Sources/OpenSwiftUICore/View/DynamicViewContent/ForEach.swift b/Sources/OpenSwiftUICore/View/DynamicViewContent/ForEach.swift index c3f9d062e..5c5ab4706 100644 --- a/Sources/OpenSwiftUICore/View/DynamicViewContent/ForEach.swift +++ b/Sources/OpenSwiftUICore/View/DynamicViewContent/ForEach.swift @@ -3,7 +3,7 @@ // OpenSwiftUICore // // Audited for 6.5.4 -// Status: Blocked by Subview +// Status: Complete // ID: 1A3DD35AB7F6976908CD7AF959F34D1F (SwiftUICore) import Foundation @@ -148,7 +148,7 @@ public struct ForEach where Data: RandomAccessCollection, ID: @available(*, unavailable) extension ForEach: Sendable {} -// MARK: - ForEach + View [Blocked by Subview] +// MARK: - ForEach + View @available(OpenSwiftUI_v1_0, *) extension ForEach: View, PrimitiveView where Content: View { @@ -169,8 +169,19 @@ extension ForEach: View, PrimitiveView where Content: View { view: _GraphValue, inputs: _ViewInputs ) -> _ViewOutputs? { - // TODO: Subview stuff - return nil + if let view = view as? _GraphValue, Subview.ID, Content>> { + return AnyView._makeView( + view: view[offset: { .of(&$0.data.substituteView) }], + inputs: inputs + ) + } else if let view = view as? _GraphValue, SectionConfiguration.ID, Content>> { + return AnyView._makeView( + view: view[offset: { .of(&$0.data.substituteView) }], + inputs: inputs + ) + } else { + return nil + } } nonisolated public static func _makeViewList( @@ -217,8 +228,19 @@ extension ForEach: View, PrimitiveView where Content: View { view: _GraphValue, inputs: _ViewListInputs ) -> _ViewListOutputs? { - // TODO: Subview stuff - return nil + if let view = view as? _GraphValue, Subview.ID, Content>> { + return AnyView._makeViewList( + view: view[offset: { .of(&$0.data.substituteView) }], + inputs: inputs + ) + } else if let view = view as? _GraphValue, SectionConfiguration.ID, Content>> { + return AnyView._makeViewList( + view: view[offset: { .of(&$0.data.substituteView) }], + inputs: inputs + ) + } else { + return nil + } } }