我如何在我的自定义视图中读取子元素中使用的修饰符的值,我想达到像TabView一样的效果?

struct TabItem: View {
    var body: some View {
        TabView {
            View1()
                .tabItem {
                    Label("Menu", systemImage: "list.dash")
                }


            View2()
                .tabItem {
                    Label("Order", systemImage: "square.and.pencil")
                }
        }
    }
}

我希望父视图(即My CustomView)读取子元素是否具有自定义修饰符(例如.inCustomView),并从中读取内容并基于它创建按钮或其他内容,就像在TabView的情况下一样

推荐答案

您需要使用一些带下划线前缀(表示它们不稳定)的API来读取ViewBuilder中的视图.然后,您可以使用您自己的特征键,同样使用下划线前缀的API来读取视图特征.特征可以存储表示传递给您的自定义修改器的视图的AnyView.

要从ViewBuilder中提取视图,可以使用Swift Package View Extractor.你可以看看他们是如何做到这一点的-这并不是很多代码.

例如,让我们创建一个VStack,它允许您使用自定义修饰符vstackButtonLabel在其子视图的底部添加按钮.用户可以使用它来指定他们想要的按钮标签的视图.

struct VStackWithButtons<Content: View>: View {
    @ViewBuilder let content: () -> Content
    
    var body: some View {
        VStack {
            content()
            HStack {
                ExtractMulti(content) { views in
                    // "views" is a RandomAccessCollection
                    ForEach(views) { view in
                        // You can also access "view.id" here if needed 
                        if let buttonLabel = view[VStackButtonLabelTrait.self] {
                            Button {
                                print("Do something")
                            } label: {
                                buttonLabel
                            }
                        }
                    }
                }
            }
        }
    }
}

VStackButtonLabelTrait是一个_ViewTraitKey:

struct VStackButtonLabelTrait: _ViewTraitKey {
    static let defaultValue: AnyView? = nil
}

extension View {
    func vstackButtonLabel<Content: View>(@ViewBuilder content: () -> Content) -> some View {
        _trait(VStackButtonLabelTrait.self, AnyView(content()))
    }
}

用法示例:

VStackWithButtons {
    Text("This has no buttons")
    
    Text("This has a button")
        .vstackButtonLabel {
            Label("Foo", systemImage: "globe")
        }
    
    Text("This also has a button")
        .vstackButtonLabel {
            Label("Bar", systemImage: "rectangle")
        }
}

结果:

enter image description here

作为另一个例子,这里有一个非常简单的"选项卡视图"(如果你可以这样称呼它的话),它允许你使用 Select 器来 Select 选项卡.

struct ContentView: View {
    @State var selectedTab = 0
    var body: some View {
        CustomTabView(selectedTab: $selectedTab) {
            Text("Tab 1")
                .id(0)
            
            Text("Tab 2")
                .id(1)
                .customTabItem {
                    Label("Foo", systemImage: "globe")
                }
            
            Text("Tab 3")
                .id(2)
                .customTabItem {
                    Label("Bar", systemImage: "rectangle")
                }
        }
    }
}

struct CustomTabView<Content: View, Selection: Hashable>: View {
    
    @Binding var selectedTab: Selection
    
    @ViewBuilder let content: () -> Content
    
    var body: some View {
        VStack {
            ExtractMulti(content) { views in
                ForEach(views) { view in
                    if view.id(as: Selection.self) == selectedTab {
                        view
                    }
                }
            }
            Picker("Pick", selection: $selectedTab) {
                ExtractMulti(content) { views in
                    ForEach(views) { view in
                        if let id = view.id(as: Selection.self) {
                            if let label = view[CustomTabItemTrait.self] {
                                label.tag(id)
                            } else {
                                Text("Unnamed").tag(id)
                            }
                        }
                    }
                }
            }
        }
    }
}

extension View {
    func customTabItem<Content: View>(@ViewBuilder content: () -> Content) -> some View {
        _trait(CustomTabItemTrait.self, AnyView(content()))
    }
}

struct CustomTabItemTrait: _ViewTraitKey {
    static let defaultValue: AnyView? = nil
}

Swift相关问答推荐

如何将EnvironmentObject从UIKit视图控制器传递到SwiftUI视图

如何在SWIFT中解码Instagram Backup中的字符串?

在visionOS 1.0中已弃用';base Color';:请改用`Color`属性

了解SWIFT中的任务行为

像WebSocket一样接收实时更新,但通过异步/等待或组合

了解SWIFT中的命名VS位置函数调用

ClosedRange.Swift 中 Int 的索引?

有没有办法让文本字段以不同的 colored颜色 显示而不影响半透明背景?

可以使 Swift init 仅可用于 Objective C 吗?

类型擦除`AsyncMapSequence, Element>`

我如何遵守与演员的协议?

并发执行代码中捕获的 var 的变异

如何有条件地依赖桌面与 iOS 的系统库?

更改该函数的参数之一时,如何将这些 SwiftUI 异步任务合并到一个任务中以从函数中获得正确的结果

为什么 `map(foo)` 和 `map{ foo($0) }` 返回不同的结果?

AVPlayer 在 iOS 15.4 中寻求 completionHandler 返回 false

是 swift 中另一个日期的同一周、月、年的日期

如何使用 swift 将 Images.xcassets 中的图像加载到 UIImage 中

如何从 UITableViewCell 类中引用 UITableViewController?

判断用户是否登录到 iCloud?Swift /iOS