SwiftUI TabView:如何检测选项卡上的点击?

SwiftUI TabView: how to detect click on a tab?

我想在每次点击标签时 运行 一个函数。

在下面的代码中(通过使用 onTapGesture),当我点击一个新标签时,会调用 myFunction,但标签视图没有改变。

struct DetailView: View {
    var model: MyModel
    @State var selectedTab = 1
    
    var body: some View {
        TabView(selection: $selectedTab) {
            Text("Graphs").tabItem{Text("Graphs")}
               .tag(1)
            Text("Days").tabItem{Text("Days")}
               .tag(2)
            Text("Summary").tabItem{Text("Summary")}
               .tag(3)
        }
        .onTapGesture {
            model.myFunction(item: selectedTab)
        }
    }
}

我怎样才能得到这两个东西:

自 iOS 14 起,您可以使用 onChange 在状态变量更改时执行代码。您可以用这个替换您的点击手势:

.onChange(of: selectedTab) { newValue in
    model.myFunction(item: newValue)
}

如果您不想受限于 iOS14,您可以在此处找到其他选项:

这是可能的方法。对于 TabView,它提供与点击另一个选项卡并返回相同的行为,因此提供持久的外观和感觉。

gif image

完整模块代码:

import SwiftUI

struct TestPopToRootInTab: View {
@State private var selection = 0
@State private var resetNavigationID = UUID()

var body: some View {

    let selectable = Binding(        // << proxy binding to catch tab tap
        get: { self.selection },
        set: { self.selection = [=10=]

            // set new ID to recreate NavigationView, so put it
            // in root state, same as is on change tab and back
            self.resetNavigationID = UUID()
        })

    return TabView(selection: selectable) {
        self.tab1()
            .tabItem {
                Image(systemName: "1.circle")
            }.tag(0)
        self.tab2()
            .tabItem {
                Image(systemName: "2.circle")
            }.tag(1)
    }
}

private func tab1() -> some View {
    NavigationView {
        NavigationLink(destination: TabChildView()) {
            Text("Tab1 - Initial")
        }
    }.id(self.resetNavigationID) // << making id modifiable
}

private func tab2() -> some View {
    Text("Tab2")
}
}

struct TabChildView: View {
    var number = 1
    var body: some View {
        NavigationLink("Child \(number)",
                       destination: TabChildView(number: number + 1))
    }
}

struct TestPopToRootInTab_Previews: PreviewProvider {
    static var previews: some View {
        TestPopToRootInTab()
    }
}

除了在一种情况下,以上答案都适用。如果您出现在同一个选项卡中,则不会调用 .onChange()。更好的方法是创建 binding

的扩展
extension Binding {
 func onUpdate(_ closure: @escaping () -> Void) -> Binding<Value> {
    Binding(get: {
        wrappedValue
    }, set: { newValue in
        wrappedValue = newValue
        closure()
    })
}

用法是这样的

TabView(selection: $selectedTab.onUpdate{ model.myFunction(item: selectedTab) }) {
        Text("Graphs").tabItem{Text("Graphs")}
           .tag(1)
        Text("Days").tabItem{Text("Days")}
           .tag(2)
        Text("Summary").tabItem{Text("Summary")}
           .tag(3)
    }