SwiftUI TabView: как определить щелчок по вкладке?

Я хотел бы запускать функцию каждый раз при нажатии на вкладку.

В приведенном ниже коде (используя 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
  • моя функция вызывается

3 ответа

Решение

Начиная с iOS 14 вы можете использовать onChangeдля выполнения кода при изменении переменной состояния. Вы можете заменить жест касания следующим образом:

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

Если вы не хотите ограничиваться iOS 14, вы можете найти дополнительные параметры здесь: Как я могу запустить действие при изменении состояния?

Приведенные выше ответы работают хорошо, за исключением одного условия. Если вы находитесь на той же вкладке, .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)
    }

Вот возможный подход. Для TabView это дает то же поведение, что и нажатие на другую вкладку и обратно, поэтому обеспечивает постоянный внешний вид.

GIF-изображение

Полный код модуля:

      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()
    }
}
Другие вопросы по тегам