Display desired tab from parent view of the TabView

49 Views Asked by At

I have a scrollable tabview that displays on the click of either button in the home view (ContentView). I want the 1st tab to show when "View 1" is clicked, and the 2nd tab to show when "View 2" is clicked. Once the tabview is opened, it should still function properly, meaning the tab buttons and scrolling still works correctly. Now that I think about it, this is similar to clicking on the "Followers" or "Following" button on Instagram. If anyone has any idea on how to solve this, please let me know. Thanks!

ContentView.swift:

import SwiftUI

struct ContentView: View {
    var body: some View {
        NavigationStack {
            HStack {
                NavigationLink {
                    ScrollTab(currentTab: 0)
                } label: {
                    Text("View 1")
                        .font(.subheadline)
                        .fontWeight(.semibold)
                }
                
                NavigationLink {
                    ScrollTab(currentTab: 1)
                } label: {
                    VStack {
                        Text("View 2")
                            .font(.subheadline)
                            .fontWeight(.semibold)
                    }
                    .frame(width: 76)
                }
            }
        }
    }
}

ScrollTab.swift:

struct ScrollTab: View {
    @State var currentTab: Int = 0
    
    init(currentTab: Int) {
        self.currentTab = currentTab
    }
    
    var body: some View {
        Spacer()
        VStack(alignment: .center, spacing: 0) {
            TabBarView(currentTab: self.$currentTab)
            
            TabView(selection: self.$currentTab) {
                Text("This is view 1").tag(0)
                    .onAppear() {
                        self.currentTab = 0
                    }
                Text("This is view 2").tag(1)
                    .onAppear() {
                        self.currentTab = 1
                    }
            }
            .tabViewStyle(.page(indexDisplayMode: .never))
            .background(Color.secondary)
        }
        .edgesIgnoringSafeArea(.all)
    }
}

struct TabBarView: View {
    @Binding var currentTab: Int
    @Namespace var namespace
    
    var tabBarOptions: [String] = ["View 1", "View 2"]
    var body: some View {
        HStack(spacing: 20) {
            ForEach(Array(zip(self.tabBarOptions.indices,
                              self.tabBarOptions)),
                    id: \.0,
                    content: {
                index, name in
                TabBarItem(currentTab: self.$currentTab,
                           namespace: namespace.self,
                           tabBarItemName: name,
                           tab: index)
                
            })
        }
        .padding(.horizontal)
        .background(Color.orange) // <<<< Remove
        .frame(height: 80)
    }
}

struct TabBarItem: View {
    @Binding var currentTab: Int
    let namespace: Namespace.ID
    
    var tabBarItemName: String
    var tab: Int
    
    var body: some View {
        Button {
            self.currentTab = tab
        } label: {
            VStack {
                Spacer()
                Text(tabBarItemName)
                if currentTab == tab {
                    Color.black
                        .frame(height: 2)
                        .matchedGeometryEffect(id: "underline",
                                               in: namespace,
                                               properties: .frame)
                } else {
                    Color.clear.frame(height: 2)
                }
            }
            .animation(.spring(), value: self.currentTab)
        }
        .buttonStyle(.plain)
    }
}

Edit: Thanks to vadian's reply, I was able to get it fixed. However, I am running into an issue where the tabview is not working correctly when I click the tab buttons (link below). Any suggestions here?

Tabview

1

There are 1 best solutions below

1
vadian On BEST ANSWER

This is a common mistake. You cannot initialize @State property wrappers just by assigning the value.

Replace the first lines of ScrollTab with

struct ScrollTab: View {
    @State private var currentTab: Int
    
    init(currentTab: Int) {
       _currentTab = State(initialValue: currentTab)
    }

And – although not related to the issue – it's highly recommended to declare @State property wrappers always as private.