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)
}
}
}
我怎样才能得到这两个东西:
- 正常显示的tabview
- 正在调用我的函数
自 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)
}
我想在每次点击标签时 运行 一个函数。
在下面的代码中(通过使用 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)
}
}
}
我怎样才能得到这两个东西:
- 正常显示的tabview
- 正在调用我的函数
自 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)
}