Home > database >  SwiftUI TabView - run code in subview after sequential taps
SwiftUI TabView - run code in subview after sequential taps

Time:01-11

I am trying to implement the behavior in a TabView when the user taps the same tab multiple times, such as in the iOS AppStore app. First tap: switch to that view, second tap: pop to root, third tap: scroll to the top if needed.

The code below works fine for switching and didTap() is called for every tap.

import SwiftUI

enum Tab: String {
    case one
    case two
}

struct AppView: View {
    @State private var activeTab = Tab.one

    var body: some View {
        TabView(selection: $activeTab.onChange(didTap)) {
            One()
                .tabItem {
                    Label("one", systemImage: "1.lane")
                }
                .tag(Tab.one)

            Two()
                .tabItem {
                    Label("two", systemImage: "2.lane")
                }
                .tag(Tab.two)
        }
    }
    
    func didTap(to value: Tab) {
        print(value) // this captures every tap
    }
}

extension Binding {
    func onChange(_ handler: @escaping (Value) -> Void) -> Binding<Value> {
        Binding(
            get: { self.wrappedValue },
            set: { newValue in
                self.wrappedValue = newValue
                handler(newValue)
            }
        )
    }
}

What I am struggling with, is how to tell either One or Two that it was tapped for a second or third time? (How to pop and scroll is not the issue).

I have seen this: TabView, tabItem: running code on selection or adding an onTapGesture but it doesn't explain how to run code in one of the views.

Any suggestions?

CodePudding user response:

You can record additional taps (of same value) in an array. The array count gives you the number of taps on the same Tab.

EDIT: now with explicit subview struct.

struct ContentView: View {
    
    @State private var activeTab = Tab.one

    @State private var tapState: [Tab] = [Tab.one] // because .one is default
    
    var body: some View {
        TabView(selection: $activeTab.onChange(didTap)) {
            
            SubView(title: "One", tapCount: tapState.count)
                .tabItem {
                    Label("one", systemImage: "1.lane")
                }
                .tag(Tab.one)

            SubView(title: "Two", tapCount: tapState.count)
                .tabItem {
                    Label("two", systemImage: "2.lane")
                }
                .tag(Tab.two)
        }
    }
    
    func didTap(to value: Tab) {
        print(value) // this captures every tap
        if tapState.last == value {
            tapState.append(value) // apped next tap if same value
            print("tapped \(tapState.count) times")
        } else {
            tapState = [value] // reset tap state to new tab selection
        }
    }
}


struct SubView: View {
    
    let title: String
    let tapCount: Int
    
    var body: some View {
        VStack {
            Text("Subview \(title)").font(.title)
            Text("tapped \(tapCount) times")
        }
    }
}
  • Related