Godfather
Godfather

Reputation: 4340

SwiftUI Extract @ToolbarContent to its own var

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

Answers (4)

ingconti
ingconti

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

shufflingb
shufflingb

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

pawello2222
pawello2222

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

Raja Kishan
Raja Kishan

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

Related Questions