Search code examples
swiftxcodeswiftuinavigationviewtabview

NavigationViews in TabView displayed incorrectly


I'm using SwiftUI and want to build a paged TabView with two NavigationView pages I can switch between horizontally. The pictures below show how it's supposed to work:

enter image description here

Here is my code for above example:

struct ContentView: View {
var body: some View {
    
    TabView {
        Page1()
        
        Page2()
        
    }
    .tabViewStyle(.page)
    
}}

struct Page1: View {
var body: some View {
    NavigationView {
        ScrollView {
            Rectangle()
                .fill(.red)
                .frame(width: 100, height: 100)
        }
        .navigationTitle("Page 1")
    }
    .navigationViewStyle(StackNavigationViewStyle()) 
}}

struct Page2: View {
var body: some View {
    NavigationView {
        ScrollView {
            Rectangle()
                .fill(.blue)
                .frame(width: 100, height: 100)
        }
        .navigationTitle("Page 2")
        
    }
    .navigationViewStyle(StackNavigationViewStyle())
}}

Unless there's another way to do it is important that the NavigationViews are inside the TabView so that if I'm scrolling up the navigation bar title switches to .inline like you can see below:

enter image description here

Also, I use the StackNavigationViewStyle() because without it the pages aren't shown when I first open the app before I switch back and forth between them. StackNavigationViewStyle() solves this but still the problem is that when I open the app for the first time the rectangles are being placed incorrectly right at the top of the screen. I then have to switch to the second page and back to get them positioned correctly:

enter image description here

Does anyone have an idea?


Solution

  • One solution is to use the Tab selection parameter. It is better not to use the StackNavigationViewStyle() with these embedded NavigationViews. What you may use is a selecter, which keeps track of the page you are on and a State variable storing the current page. This way, the NavigationView is in one place and different titels are given to each TabView item.

    struct ContentView: View {
        
        @State private var selectedTab = "1"
        
        var body: some View {
            NavigationView {
                TabView(selection: $selectedTab) {
                    Page1()
                        .tag("1")
                        .navigationBarTitle("Page 1")
    
                    Page2()
                        .tag("2")
                        .navigationBarTitle("Page 2")
                }
                .tabViewStyle(.page)
            }
        }
    }
    
    struct Page1: View {
    var body: some View {
        ScrollView {
            Rectangle()
                .fill(.red)
                .frame(width: 100, height: 100)
        }
    }}
    
    struct Page2: View {
    var body: some View {
        ScrollView {
            Rectangle()
                .fill(.blue)
                .frame(width: 100, height: 100)
        }
    }}
    

    Updated When you want the NavigationTitle to be .inline when scrolled, use the following code.

    struct ContentView: View {
        
        @State private var selectedTab = "1"
        
        var body: some View {
            NavigationView {
                GeometryReader { proxy in
                    ScrollView(showsIndicators: false) {
                        TabView(selection: $selectedTab) {
                            Page1()
                                .tag("1")
                                .navigationBarTitle("Page 1")
    
                            Page2()
                                .tag("2")
                                .navigationBarTitle("Page 2")
                        }
                        .tabViewStyle(.page)
                        .tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
                        .frame(height: proxy.size.height)
                        .ignoresSafeArea()
                        
                    }
                }
            }
        }
    }
    
    struct Page1: View {
        var body: some View {
            VStack {
                Rectangle()
                    .fill(.red)
                    .frame(width: 200, height: 200)
            }
        }
    }
    
    struct Page2: View {
        var body: some View {
            VStack {
                Rectangle()
                    .fill(.blue)
                    .frame(width: 200, height: 200)
            }
        }
    }