Search code examples
swiftswiftuitabviewtabitem

TabView, tabItem: running code on selection or adding an onTapGesture


I would like to run some code when one of my tabs in tabview is selected.

Hypothetical: I want to create an app with the intentions to: A) Use a tabview and B) seriously confuse the user. To achieve this, I will create an app with tabs "one", "two", and "three", and views "one", "two", and "three". Then, when the second tab is selected, I will change view "two" to say "one" instead. Diabolical.

A common sense solution to this goes something like this:

import SwiftUI

struct ContentView: View {
    @State private var two : String = "Two"
    var body: some View {
        TabView() {
          Text("One")
            .tabItem {
              Text("One")
            }
          Text("Two")
            .tabItem {
              Text(two)
            }
              .onTapGesture {
                print("Tapped!!")
                self.two = "One"
              }
          Text("Three")
            .tabItem {
              Text("Three")
            }
        }
    }
}

Unfortunately, this works exactly like a normal app and fails to confuse the user because two is not updated (and there is no "Tapped!" in the console).

How can I run code when a tabItem is selected or tapped? This could be updating variables, running an animation, or anything else.


Solution

  • Instead of using onTapGesture on tabView we can write an extension to Binding and it will detect the new tab selection value even if we tap the tab bar within the same tab it will detect the changes. Here I am provided the binding extension.

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

    I used this in my tabView. I attached my code below.

    TabView(selection: $tabSelection.onUpdate {
            setNewValue(value: tabSelection)
        }) {
          ContentView()
            .tabItem {
                    Label {
                        Text("Home")
                    } icon: {
                        Image("HomePage_icon")
                            .renderingMode(.template)
                    }
                }
                .tag(TabViews.homepage)
    }
    

    SetNewValue function, this function acts like onTapGesture

    func setNewValue(value: TabViews){
         self.tabSelection = value
         /* inside this function we can write the code, we like to write it in onTapGesture */
     }