Search code examples
swiftuiswiftui-listswiftui-navigationview

SwiftUI List Separator within NavigationView Problem


I'm not sure if this is well-known-issue or not but it is very odd. The problem can be reproduced with Apple's example code navigationBarItems(leading:trailing:) As you can see, list separators have extra leading space that look like they are indented for some reason.

enter image description here

Here is actual code:

import Foundation
import SwiftUI
import UIKit
import PlaygroundSupport

struct ContentView: View {
    var body: some View {
        NavigationView {
            List {
                Text("Chocolate")
                Text("Vanilla")
                Text("Strawberry")
            }
            .navigationBarTitle(Text("Today‘s Flavors"))
            .navigationBarItems(leading:
                HStack {
                    Button("Hours") {
                        print("Hours tapped!")
                    }
                }, trailing:
                HStack {
                    Button("Favorites") {
                        print("Favorites tapped!")
                    }

                    Button("Specials") {
                        print("Specials tapped!")
                    }
                }
            )
        }
    }
}

PlaygroundPage.current.setLiveView(ContentView())

I test above code with Playground, iPhone 13/15.3.1 they are the same. I did messing around the code and found that applying .navigationBarTitle(), .navigationBarItems() to List causes the problem. They must apply to each List item. Very odd though. This means almost all List sample code that wrapping with NavigationView are WRONG. Here is a fix I found.

enter image description here

import Foundation
import SwiftUI
import UIKit
import PlaygroundSupport

struct ContentView: View {
    let contents = ["Chocolate", "Vanilla", "Strawberry"]

    var body: some View {
        NavigationView {
            List {
                ForEach (contents, id: \.self) { content in
                    Text(content)
                        .navigationBarTitle(Text("Today‘s Flavors"))
                        .navigationBarItems(leading:
                            HStack {
                                Button("Hours") {
                                    print("Hours tapped!")
                                }
                            }, trailing:
                            HStack {
                                Button("Favorites") {
                                    print("Favorites tapped!")
                                }

                                Button("Specials") {
                                    print("Specials tapped!")
                                }
                            }
                        )
                }
            }
        }
    }
}

PlaygroundPage.current.setLiveView(ContentView())

Although I'm not sure if I can call this is a bug but definitely either document or implementation is wrong. Could anyone explain this?


Solution

  • navigationBarItems will be deprecated in the future so you should use toolbar instead

    .toolbar {
        ToolbarItem(placement: .navigationBarLeading) {
            Button("Hours") { }
        }
    }
    .toolbar {
        ToolbarItem(placement: .navigationBarTrailing) {
            HStack {
                Button("Favorites") { }
                Button("Specials") { }
            }
        }
    }
    

    If you want to keep using navigationBarItems, there is a better work around by using other ListStyle

    struct ContentView: View {
        var body: some View {
            NavigationView {
                List {
                    Text("Chocolate")
                    Text("Vanilla")
                }
                .navigationBarTitle(Text("Today‘s Flavors"))
                .navigationBarItems(
                    leading: Button("Hours") { }
                )
                .listStyle(PlainListStyle())
            }
        }
    }
    

    do not apply navigationBarTitle on every row for a work around, it's bad