Lorenzo Zorri
Lorenzo Zorri

Reputation: 147

How add a segmented picker under the title in a NavigationBar on SwiftUI?

I'm trying to add a segmented picker in the NavigationBar under the title with SwiftUI. I've tried with the toolbar, but it was show on the top and not under the title.

How can I achieve the result like the image below in SwiftUI?

Screenshot from the App Store leaderboard.

This is what I have tried but doesn't work.


struct ExampleView: View {

    @State private var selectedTab: String = "tab1"

    var body: some View {
        VStack {
            Picker("Mode", selection: $selectedTab) {
                Text("tab1").tag("tab1")
                Text("tab2").tag("tab2")
                Text("tab3").tag("tab3")
            }
            .pickerStyle(.segmented)
            .padding([.horizontal, .top])
        
            Form {
                switch selectedTab {
                    case "tab1":
                        TabOneView()
                    case "tab2":
                        TabTwoView()
                    case "tab3":
                        TabThreeView()
                    default:
                        TabOneView()
                }
            }
        }
        .navigationTitle("Title")
        .navigationBarTitleDisplayMode(.inline)
    }
}

Upvotes: 1

Views: 104

Answers (1)

Daniel Crompton
Daniel Crompton

Reputation: 516

I've just been watching a similar thing in one of Kavsoft's videos on YouTube on how to make a sticky header, it's a super interesting topic and there is no default way to do it.

There's 2 important points in the video:

  1. where he hides the default toolbar background (the thin material with divider);
  2. and where he adds the view to the safe area and brings back the toolbar background.

I will also list the stages here:

  1. Add the segmented picker inside the safe area top inset, which pins it to the top even when scrolling, with the .safeAreaInset(edge: .top) modifier.

  1. Give the segmented picker (safe area inset contents) a background that resembles the original toolbar background, for example:
.padding(.bottom)
.background {
    Rectangle()
        .fill(.ultraThinMaterial)
        .ignoresSafeArea()
}

Edit!!! Instead of trying to emulate the toolbar background, you can access it with .background(.bar). Thanks to @BenzyNeez for this suggestion.

  1. Delete the default toolbar background (with either .toolbarBackgroundVisibility(.hidden, for: .navigationBar) or .toolbarBackground(.hidden, for: .navigationBar).

Code implementation:

I implemented it in code for you:


import SwiftUI

enum Option {
    case a, b
    var text: String {
        switch self {
        case .a: "App gratuite"
        case .b: "App a pagamento"
        }
    }
}

struct SticktoHeader: View {
    
    @State private var tab = Option.a
    
    var body: some View {
        
        NavigationStack {
            
            // List stuff
            List(0..<50) { n in
                Text("Item \(n)")
            }
            .navigationTitle("Classifiche")
            .toolbar {
                // Add a default toolbar
                ToolbarItem(placement: .topBarTrailing) {
                    Button("Tutte le app") { }
                }
            }
            
            // NEW 1: Add safe area inset
            .safeAreaInset(edge: .top) {
                Picker("Pick tab", selection: $tab) {
                    Text(Option.a.text)
                        .tag(Option.a)
                    
                    Text(Option.b.text)
                        .tag(Option.b)
                }
                .padding(.horizontal)
                .padding(.bottom)
                .pickerStyle(.segmented)
                //.background {
                    //Rectangle()
                        //.fill(.ultraThinMaterial)
                        //.ignoresSafeArea()
                        //.overlay(alignment: .bottom) {
                            //Divider()
                        //}
                //}
                .background(.bar) // Thanks @BenzyKneez
            }
            .toolbarBackground(.hidden, for: .navigationBar)
            
        }
        
        
    }
}

#Preview {
    SticktoHeader()
}

Upvotes: 2

Related Questions