I am displaying a list of names in a “list". When I encapculate this in a section it works. BUT when I add the header the Toolbar items get duplicated. If I REM it out it behaves as expected, e.g.:
/* header: {
Text("Names in Order")
.frame(width: 380, alignment: .leading)
} */
Examples of the screen shots and code follows:
import SwiftUI
import SwiftData
enum AppearanceStyle {
case dark
case light
case auto
}
struct EntrantsHomeView: View {
@Environment(\.modelContext) var context
@State private var isShowingItemSheet = false
@Query(sort: \EntrantsDatabase.number) var entrants: [EntrantsDatabase]
@State private var entrantToEdit: EntrantsDatabase?
@State private var appearance: AppearanceStyle = .auto
var body: some View {
Section {
List {
ForEach(entrants) { entrant in
EntrantCell(entrant: entrant)
.onTapGesture {
entrantToEdit = entrant
}
}
.onDelete { indexSet in
for index in indexSet {
context.delete(entrants[index])
try! context.save()
}
}
}
} /* header: {
Text("Names in Order")
.frame(width: 380, alignment: .leading)
} */
.overlay {
if entrants.isEmpty {
ContentUnavailableView(label: {
Label("No Names", systemImage: "list.bullet.rectangle.portrait")
}, description: {
Text("Start adding names to your list.")
}, actions: {
Button("Add Name") {isShowingItemSheet = true }
})
.offset(y: -60)
}
}
.toolbar(content: {
if !entrants.isEmpty {
ToolbarItem(placement: .topBarLeading) {
Button("Delete Name", systemImage: "minus") {
do {
try context.delete(model: EntrantsDatabase.self)
} catch {
print("Failed to clear the Names database")
}
}
}
ToolbarItem(placement: .topBarTrailing) {
Button("Add Name", systemImage: "plus") {
isShowingItemSheet = true
}
}
}
})
.navigationTitle("Names' Manager")
.navigationBarTitleDisplayMode(.large)
.sheet(isPresented: $isShowingItemSheet) {AddEntrantSheet() }
.sheet(item: $entrantToEdit) { entrant in
UpdateEntrantSheet(entrant: entrant)
}
}
}
struct EntrantCell: View {
let entrant: EntrantsDatabase
var body: some View {
HStack {
CircledText(text: String(entrant.number))
Spacer()
Text(entrant.name)
.frame(width: 160, alignment: .leading)
.bold()
Spacer()
}
}
}
A Section
with a header may look like one view, but it's actually two views. Compare:
var body: some View {
Group(subviews: twoViews) { subviews in
Text("\(subviews.count)") // 2
}
Group(subviews: section) { subviews in
Text("\(subviews.count)") // 2
}
// also see:
ZStack { section }
}
@ViewBuilder var section: some View {
Section {
Text("Foo")
} header: {
Text("Bar")
}
}
@ViewBuilder var twoViews: some View {
Text("Foo")
Text("Bar")
}
Similarly, a Section
with both a header and a footer is three views.
As a result, the toolbar
modifier modifies the two views at the same time, creating two copies of the same toolbar.
To fix this, you can move the toolbar
modifier to modify the List
instead.
The same applies to the overlay
and sheet
modifiers, so I suggest you move them to the List
as well.
Alternatively, use a VStack
instead of Section
. VStack
counts as one view no matter how many views it contain. Only certain views give Section
s special appearances (e.g. List
). Your Section
doesn't seem to be in such a view, so I'd suggest just using a VStack
instead.