SwiftUI 的 TabView 内部如何识别每个 Item?

问题描述 投票:0回答:1

使用 TabView 时:

TabView {
    ReceivedView()
        .badge(2)
        .tabItem {
            Label("Received", systemImage: "tray.and.arrow.down.fill")
        }
    SentView()
        .tabItem {
            Label("Sent", systemImage: "tray.and.arrow.up.fill")
        }
    AccountView()
        .badge("!")
        .tabItem {
            Label("Account", systemImage: "person.crop.circle.fill")
        }
}

但是,我想知道TabView如何识别每个视图?我假设在内部,一些代码会像

ForEach(Content.views) { view in
    if view == selectedItem {
        view
    }
}

但我不知道如何获取 Content.views,有什么想法吗?

swiftui tabview
1个回答
0
投票

我猜测了一点,但我相信它通过 ViewTraitKey 和 VariadicViews 使用标签。

我将尝试复制下面的选择行为

首先我们将定义一些可以帮助我们解决问题的事情。

  1. 允许我们从 ViewBuilder 内容迭代子视图的视图
struct VariadicViewReader<Source: View, Content: View>: View {
    var source: Source
    var content: Content

    init(_ source: Source, @ViewBuilder content: @escaping (_VariadicView.Children) -> Content) {
        self.source = source
        self.content = content
    }

    var body: some View {
        _VariadicView.Tree(Root(content: content)) { source }
    }
}
  1. 用标识符标记我们的视图的机制
private struct CustomTagTraitKey: _ViewTraitKey {
    static var defaultValue: AnyHashable? = nil
}

extension _VariadicView_Children.Element {
    var customTag: AnyHashable? {
        self[CustomTagTraitKey.self]
    }

    func customTag<T: Hashable>(as: T.Type) -> T? {
        customTag as? T
    }
}

extension View {
    func customTag<V: Hashable>(_ tag: V) -> some View {
        _trait(CustomTagTraitKey.self, AnyHashable(tag))
    }
}

现在设置到位,我们可以创建一个 CustomTabView

struct CustomTabView<SelectionValue, Content>: View where SelectionValue: Hashable> {

    @Binding var selection: SelectionValue
    let content: Content

    init(_ selection: Binding<SelectionValue>, @ViewBuilder content: () -> Content) {
        self._selection = selection
        self.content = content()
    }

    var body: some View {
        VariadicViewReader(content) { children in
             ForEach(children) child in
                  if let tag = child.customTag(as: SelectionValue.self), tag == selection {
                      child
                  }
             }
        }
    }

}

然后使用它就像

CustomTabView($selectedTab) {
    View1().customTag(0)
    View2().customTag(1)
}

CustomTabView($selectedTab) {
    ForEach([0, 1], id: \.self) { num
        View1().customTag(num)
    }
}

这是苹果文档中的相关注释

ForEach 使用相应元素的 id 参数自动将默认标记应用于每个枚举视图。如果元素的 id 参数和选择器的选择输入具有完全相同的类型,则可以省略显式标记修饰符。要查看不需要显式标签的示例,请参阅选取器。

考虑到这一点

TabView {
    ForEach([0, 1], id: \.self) { num
        View1()
    }
}

相同
TabView {
    ForEach([0, 1], id: \.self) { num
        View1().tag(num)
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.