Reputation: 4340
Is it possible to extract the toolbar content in a separate var like using @ViewBuilder
?
I would like to extract it and set .toolBar(content: myToolBarContent)
var body: some View {
NavigationView {
List {
}
.toolbar(content: {
ToolbarItem(placement: .principal) {
Text("Hi")
}
ToolbarItem(placement: .navigationBarTrailing) {
Text("Ho")
}
})
}
}
Upvotes: 30
Views: 7072
Reputation: 11666
Nice tip. I was here trying to remove a messed up toolbar with many items.
Here is my approach, using buttons and callbacks.
struct ContentView: View {
// straight from the xcode template, BUT add a NavigationStack!
var body: some View {
NavigationStack {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundStyle(.tint)
Text("Hello, world!")
}
// add here, NOT on the NavigationStack!
.toolbar{ CustomToolBarContent(t1: "Hoo", t2: "Hii", callback: doStuff ) }
}
}
private func doStuff(_ index: Int){
print(index)
}
}
Then, in the appropriate file…
typealias CallBack = ( (Int)->() )?
struct CustomToolBarContent: ToolbarContent {
internal init(t1: String, t2: String, callback: CallBack = nil) {
self.t1 = t1
self.t2 = t2
self.callback = callback
}
private let t1: String
private let t2: String
private let callback: CallBack
var body: some ToolbarContent {
ToolbarItem(placement: .principal) {
Text(t1)
}
ToolbarItem(placement: .navigationBarTrailing) {
Text(t2)
}
ToolbarItem(placement: .navigationBarTrailing) {
Button("Do 1") {
callback?(1)
}
}
ToolbarItem(placement: .navigationBarTrailing) {
Button("Do 2") {
callBack?(2)
}
}
}
}
Source: https://gist.github.com/ingconti/be5417e253b16d99bd09128c26dd94ba
(I made it decently separated; in my gist, it is all one piece, of course.)
Upvotes: 0
Reputation: 1947
Worth adding on to pawello2222 answer by mentioning that on macOS, it doesn't take much more to enable nifty user customisable toolbars, e.g.
@main
struct myHappyAppyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
.commands {
ToolbarCommands() //<- Turns on the toolbar customization
}
}
}
struct ContentView: View {
var body: some View {
NavigationView {
List {}
.toolbar(id: "hihobar", content: myToolBarContent)
}
}
@ToolbarContentBuilder
func myToolBarContent() -> some CustomizableToolbarContent {
ToolbarItem(id: "hitag") {
Text("Hi")
}
ToolbarItem(id: "hotag") {
Text("Ho")
}
ToolbarItem(id: "spacertag") {
Spacer()
}
}
}
Upvotes: 4
Reputation: 54611
You don't actually need to create another struct - instead you can use @ToolbarContentBuilder
. It's a @ViewBuilder
equivalent for ToolbarContent
:
struct ContentView: View {
var body: some View {
NavigationView {
List {}
.toolbar(content: myToolBarContent)
}
}
@ToolbarContentBuilder
func myToolBarContent() -> some ToolbarContent {
ToolbarItem(placement: .principal) {
Text("Hi")
}
ToolbarItem(placement: .navigationBarTrailing) {
Text("Ho")
}
}
}
Upvotes: 60
Reputation: 19014
If you want to separate the toolbar from the body, then you need to create a separate struct for Toolbar content. Here is a possible demo.
struct ContentView: View {
var body: some View {
NavigationView {
List {
}
.toolbar{MyToolBarContent()}
}
}
struct MyToolBarContent: ToolbarContent {
var body: some ToolbarContent {
ToolbarItem(placement: .principal) {
Text("Hi")
}
ToolbarItem(placement: .navigationBarTrailing) {
Text("Ho")
}
}
}
}
Upvotes: 22