Home > Back-end >  How to put tabs inside a scrollview in SwiftUI?
How to put tabs inside a scrollview in SwiftUI?

Time:09-19

So basically I am very new to SwiftUI(started a few days ago) and am trying to put tabs within a ScrollView. The end result I am trying to achieve is that of Instagram's profile view.

I'd imagine the view's implementation would be something like this:

    ScrollView {
        VStack {
            HStack {
                // Profile Pic
                // Stats
            }
            // Bio
            // Button(s)
            LazyVStack(pinnedViews: .sectionHeaders) {
                Section {
                    TabView {
                        // Tab 1
                        // Tab 2
                        // Tab 3
                    }
                } headers: {
                      // Tab icons
                }
            }
        }
    }

The problem is, the TabView never appears. Also I am unsure whether this is the best setup especially the LazyVStack as I am only using to for the fact that it pins the headers. As I said previously, I'm super new to SwiftUI so there are definetly some views that I have no idea exist some of which might be useful in what I am trying to achieve.

Nonetheless, how can I achieve the layout I am going for?

Thank you!

Side Question: With the way that I have the view setup, the scrollbar for the view is for the entire view however in apps like Instagram, the scrollbars are only within the tabs themselves. How could I also incorporate that aspect into the solution? Thanks again!

CodePudding user response:

You are on the right track. TabView when not used as the root loses the ability to resize it self properly.

So that's why we need to specfiy its minHeight. You can use GeometryReader for that.

Then just give TabView a selection in order for the Tab-Buttons to work and most likely you want to apply .tabViewStyle(.page(indexDisplayMode: .never)) in order to be able to swipe between them.

Working Demo:

GeometryReader { proxy in
    ScrollView {
        VStack {
            HStack {
                Image(systemName: "person.circle")
                Text("Some Text")
            }
        }
        
        LazyVStack(spacing: 0, pinnedViews: .sectionHeaders) {
            Section {
                TabView(selection: $tabIndex) {
                    VStack {
                        Spacer()
                        Text("1")
                        Spacer()
                    }
                    .frame(maxWidth: .infinity)
                    .background(.red.opacity(0.5))
                    .tag(0)
                    
                    VStack {
                        Spacer()
                        Text("2")
                        Spacer()
                    }
                    .frame(maxWidth: .infinity)
                    .background(.green.opacity(0.5))
                    .tag(1)
                    
                    VStack {
                        Spacer()
                        Text("3")
                        Spacer()
                    }
                    .frame(maxWidth: .infinity)
                    .background(.blue.opacity(0.5))
                    .tag(2)
                }
                .tabViewStyle(.page(indexDisplayMode: .never))
                .frame(maxWidth: .infinity, minHeight: proxy.size.height)
            } header: {
                HStack {
                    Button {
                        withAnimation {
                            tabIndex = 0
                        }
                    } label: {
                        Text("Tab 1")
                    }
                    .buttonStyle(.bordered)
                    
                    Button {
                        withAnimation {
                            tabIndex = 1
                        }
                    } label: {
                        Text("Tab 2")
                    }
                    .buttonStyle(.bordered)
                    
                    Button {
                        withAnimation {
                            tabIndex = 2
                        }
                    } label: {
                        Text("Tab 3")
                    }
                    .buttonStyle(.bordered)
                }
                .padding()
                .frame(maxWidth: .infinity)
                .background(.regularMaterial)
            }
        }
    }
}

As to your side question: They most likely still use a single scrollview but then move the inset of the scroll indicators. You cannot do that with SwiftUI only but you would need to introspect the underlying UIScrollView for that. SwiftUI-Introspect is good for that.

And then adjust the verticalScrollIndicatorInsets to your needs

  • Related