skip to Main Content

I’m trying to add an accessory view embedded in a navigation bar below the title, which can be seen in the default iOS calendar app (the “s m t w t f s” row) or the GitHub mobile app:

Gh1Gh1

And I’d like it to work along with the large title style navigation bar like the GH mobile.

LazyVStack’s pinnedView with a section header almost work, but I can’t get the background color to make it seemless with the navigation bar, even with the ultraThinMaterial. It also leaves the divider line between the pinned view and the bar.

Is there a way to achieve this layout?
Solutions in SwiftUI, SwiftUI+Introspect, and UIKit are all welcome!

2

Answers


  1. Have you tried setting a .safeAreaInset view? This will have the stickiness you’re looking for, and items in the "main" part of the view will take its height into account when rendering, so won’t get obscured.

    Here’s a quick example I knocked up:

    struct ContentView: View {
        var body: some View {
          NavigationView {
            List {
              ForEach(0 ..< 30) { item in
                Text("Hello, world!")
              }
            }
            .navigationTitle("Accessory View")
            .safeAreaInset(edge: .top) {
              AccessoryView()
            }
          }
        }
    }
    
    struct AccessoryView: View {
      var body: some View {
        HStack {
          Button("Button") { }
          Button("Button") { }
          Button("Button") { }
          Spacer()
        }
        .padding()
        .background(Color(uiColor: .systemGroupedBackground))
        .buttonStyle(.bordered)
        .controlSize(.mini)
      }
    }
    

    You have to give the view a background otherwise it’ll be transparent – but that background will (as long as it’s a colour or a material) automatically extend into the navigation bar itself. Here’s a GIF of the above code in action, where I’ve set the background to match the grouped list’s background:

    enter image description here

    It’s not perfect, especially as it looks distinct from the nav bar on scroll, but it might be useable for you?

    Login or Signup to reply.
  2. Another idea is to replace the navigation bar with a custom one like this:

    {
        ...
    }
        .safeAreaInset(edge: .top) {
            VStack(alignment: .leading, spacing: 8) {
                HStack() {
                    Button {
                        presentationMode.wrappedValue.dismiss()
                    } label: {
                        Image(systemName: "chevron.backward")
                    }
    
                    Spacer()
    
                        Text(navigationTitle).font(.title2).bold()
                            .multilineTextAlignment(.center)
                            .foregroundColor(.accentColor)
                            .frame(maxWidth: .infinity)
                    Spacer()
                    
                }
               HStack {
                     Button("Button") { }
                     Button("Button") { }
                     Button("Button") { }
                     Spacer()
                   }
            }
            .padding()
            .background(
                .bar
            )
        }
    

    You will also have to set:

    .navigationBarBackButtonHidden(true)
    

    and do not set a navigation title:

    // .navigationTitle("....")
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search