user1591668
user1591668

Reputation: 2883

SwiftUI TabView how can I load more data on 2nd tab click

I am working with TabView and would like to add more data when a user clicks for the 2nd time on the same tab item . I have been looking at other examples such as this SwiftUI TabView On Click Action however I have not been able to get it to work . I have a simple example below (full code) . Basically if the tag is already on MainView and the user clicks it again I want to hit the print("Update get more data") code . I have used the OnReceive method however that does not work if clicked more than 1 time in the same tab . I have also tried to use onTapGesture outside of the TabView but do not seem to bind any suggestions would be great since I am learning SwiftUI and I am using version 3.0

  *TabView(selection: $selectedTab) {
      ... 
    }.onTapGesture {
        print(selectedTab) // This always give MainView 
    }*

I have also tried the above and it is always giving MainView

import SwiftUI
import Combine

struct TabViews: View {
    @State var selectedTab: Tab = .MainView
    @State private var firstPass = false
    
    var body: some View {
        
        TabView(selection: $selectedTab) {
            Text("Main View")
                .padding()
                .tabItem {
                    Image(systemName: "1.circle")
                    
                    Text("First")
                    
                }
                .tag(Tab.MainView)
            
            Text("Menu View")
            
                .padding()
                .tabItem {
                    Image(systemName: "2.circle")
                    
                    Text("Second")
                       
                }
                .tag(Tab.MenuView)
            
        }
        
        .onReceive(Just(selectedTab)) {
            print("Tapped!! \(selectedTab)")
            
 
            if $0 == .MainView  {
                
                if firstPass == true {
                    // update list
                    print("Update get more data")
                }
                firstPass = true
            }
            if $0 == .MenuView {
                print("2")
            }
        }
       
        
    }
}

extension TabViews {
    enum Tab: Hashable {
        case MainView
        case MenuView
        case RankingView
    }
}

struct TabView_Previews: PreviewProvider {
    static var previews: some View {
        TabViews()
    }
}

Upvotes: 1

Views: 451

Answers (2)

Jano
Jano

Reputation: 63667

Publish your tab variable

@Published var tabSelection: Tab = .mainView

Then catch the change in your TabView using Combine’s onReceive. This will trigger, even when re-selecting the tab.

import Combine
...

.onReceive(appModel.$tabSelection) { newSelection in
    // ... do anything
    // avoid infinite loop
    if tabSelected != newSelection {
        self.tabSelected = newSelection
    }
}

I placed onReceive on a NavigationStack of a TabView child view, in order to reset the navigation stack

Passing a function as a Binding also works, but semantically, you are not really Binding.

Upvotes: 0

Asperi
Asperi

Reputation: 257543

For your scenario the solution is to use proxy binding (to intercept every click) as was shown in second link from referenced question.

Tested with Xcode 13 / iOS 15

demo

struct TabViews: View {
    @State var selectedTab: Tab = .MainView
    @State private var firstPass = false

    private var selection: Binding<Tab> { Binding(  // << this !!
        get: { selectedTab },
        set: {
            selectedTab = $0

            print("Tapped!! \(selectedTab)")


            if $0 == .MainView  {

                if firstPass == true {
                    // update list
                    print("Update get more data")

                    firstPass = false  // << reset !!
                    return
                }
                firstPass = true
            }
            if $0 == .MenuView {
                print("2")
                firstPass = false
            }
        }
    )}

    var body: some View {
        TabView(selection: selection) {    // << here !!
            Text("Main View")
                .padding()
                .tabItem {
                    Image(systemName: "1.circle")
                    Text("First")
                }
                .tag(Tab.MainView)

            Text("Menu View")
                .padding()
                .tabItem {
                    Image(systemName: "2.circle")
                    Text("Second")
                }
                .tag(Tab.MenuView)
        }
    }
}

Upvotes: 2

Related Questions