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

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

我想在每次点击选项卡时运行一个函数。

在下面的代码中(通过使用

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)
        }
    }
}

我怎样才能同时获得这两件事:

  • 标签视图正常显示
  • 我的函数被调用
swift swiftui tabview swiftui-tabview swiftui-ontapgesture
4个回答
14
投票

从 iOS 14 开始,您可以使用

onChange
在状态变量更改时执行代码。您可以用以下手势替换点击手势:

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

如果您不想受限于 iOS 14,您可以在此处找到其他选项:状态更改时如何运行操作?


12
投票

除了一种情况外,上述答案都有效。如果您位于同一选项卡中,则不会调用 .onChange()。更好的方法是创建绑定扩展

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)
    }

2
投票

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

完整模块代码:

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 = $0

            // 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()
    }
}

0
投票

这真的很有帮助。

当用户登录时,会显示视频选项卡的家庭版本,但再次按视频时,用户必须转到该选项卡的视频版本。家庭版的侧边菜单中有导航。

你的代码可以缩短一点,但它的作用就像一个魅力,我感谢你。

struct MainView: View {
    @State private var selection = 0
    @Binding var shouldShowHome: Bool

    @State private var resetNavigationID = UUID()

    /** Navigate to tab view **/
    func navigate() -> some View {
        VStack {
            switch selection {
                case 0:
                    FolderVideoView(shouldShowHome: $shouldShowHome).id(self.resetNavigationID)
                case 1:
                    ChatView()
                case 2:
                    DocumentListView()
                case 3:
                    NewsView()
                case 4:
                    EventCalendarView()
                default:
                    EmptyView()
            }
        }
        .onAppear {
          print("Navigating to \(selection)")
        }
    }

    var body: some View {
        // Proxy binding to catch taps on selected tabs
        let selectable = Binding(
          get: { self.selection },
          set: { self.selection = $0

              // If recreating navigation ID, the tab can be relected, see tabContent()
              self.resetNavigationID = UUID()
          })

        TabView(selection: selectable)  {
            if GlobalData.sharedGlobal.role == "newuser" {
                navigate()
                    .tabItem {
                        Image("Chat")
                    }
                    .badge(chatViewModel.unreadMessagesCount)
                    .tag(1)
                navigate()
                    .tabItem {
                        Image("Video")
                    }
                    .tag(0)
            } else {
                navigate()
                    .tabItem {
                        Image("Video")
                    }
                    .tag(0)
                navigate()
                    .tabItem {
                        Image("Chat")
                    }
                    .badge(chatViewModel.unreadMessagesCount)
                    .tag(1)
            }
            navigate()
                .tabItem {
                    Image("Document")
                }
                .tag(2)
            navigate()
                .tabItem {
                    Image("News")
                }
                .tag(3)
            navigate()
                .tabItem {
                    Image("Event")
                }
                .tag(4)
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.