rpetruzz
rpetruzz

Reputation: 119

Can you use closure like a named block for ToolbarContent - ToolbarItem

I'm using ToolbarContent with ToolbarItem to have consistent toolbars across multiple views... The Toolbar looks like:

struct ToolBarCancelDeleteAdd: ToolbarContent {
    
    @Binding var deletePressed: Bool
    let cancel: () -> Void
    let deleting: () -> Void
    let adding: () -> Void
    
    var body: some ToolbarContent {
        
        ToolbarItemGroup(placement: .cancellationAction) {
            Button("Cancel", action: cancel )
        }
        
        ToolbarItemGroup(placement: .confirmationAction) {
            if deletePressed {
                Button(action: deleting, label:{Text("Done")})
            } else {
                Button(action: deleting, label:{Text("Delete")})
            }
            Button(action: adding, label: {Image(systemName: "plus")})
        }
    }

In my view I'm adding the toolbar and providing the closure information needed..

        .navigationBarBackButtonHidden(true)
        .navigationTitle("Budget Descriptions")
        .toolbar() { 
            ToolBarCancelDeleteAdd(deletePressed: $deletePressed){
                if self.editMode?.wrappedValue == .active {
                    self.editMode?.wrappedValue = .inactive
                    deletePressed.toggle()
                }
                self.presentationMode.wrappedValue.dismiss()
            }   deleting: {
                if self.editMode?.wrappedValue == .inactive {
                    self.editMode?.wrappedValue = .active
                    deletePressed.toggle()
                } else {
                    self.editMode?.wrappedValue = .inactive
                    deletePressed.toggle()
                }
            } adding: {
                if self.editMode?.wrappedValue == .active {
                    self.editMode?.wrappedValue = .inactive
                }
                self.isAdding.toggle()
            }
        } // END OF TOOLBAR

This all works very fine and I'm happy with it..... except I'm doing this code across multiple views and if I needed to tweak it I have to go to multiple views to tweak it. It would be great if I could name this closure content like I have done with some views using ViewBuilder. This way I could just name the closure content some way.. Like...

        .navigationBarBackButtonHidden(true)
        .navigationTitle("Budget Descriptions")
        .toolbar() { 
            ToolBarCancelDeleteAdd(deletePressed: $deletePressed){
                   CancelContent()
            }   deleting: {
                   DeleteContent()            
            } adding: {
                   AddingContent()
            }
        } // END OF TOOLBAR

Upvotes: 0

Views: 336

Answers (2)

rpetruzz
rpetruzz

Reputation: 119

With the suggestion of Nigel Gee above and the help of Javier of SwiftUI Lab I have my final solution to my goal of being able to inject the code into my .toolbar. It looks like:

class ToolbarViewModel: ObservableObject {
    
    func cancelContent(editMode: Binding<EditMode>?) {
    
        if editMode?.wrappedValue == .active {
            editMode?.wrappedValue = .inactive
        }
    }

    func deleteContent(deletePressed: inout Bool, editMode: Binding<EditMode>?) {
        
        if editMode?.wrappedValue == .inactive {
            editMode?.wrappedValue = .active
        } else {
            editMode?.wrappedValue = .inactive
        }
    }

    func addingContent(isAdding: inout Bool, editMode: Binding<EditMode>?) {

        if editMode?.wrappedValue == .active {
            editMode?.wrappedValue = .inactive
        }
        isAdding.toggle()
    }
}

In my View I declare:


    @Environment(\.editMode) var editMode: Binding<EditMode>?

    @StateObject var toolbarViewModel = ToolbarViewModel()

and the toolbar looks like:

.toolbar {
    ToolBarCancelDeleteAdd() {
        toolbarViewModel.cancelContent(editMode: editMode)
        presentationMode.wrappedValue.dismiss()
    } deleting: {
        toolbarViewModel.deleteContent(deletePressed: &deletePressed, editMode: editMode)
    } adding: {
        toolbarViewModel.addingContent(isAdding: &isAdding, editMode: editMode)
    }
} // END OF TOOLBAR


Upvotes: 1

Nigel Gee
Nigel Gee

Reputation: 123

Why not make a class that hold the methods

class ToolbarViewModel: ObservableObject {
    func cancelContent(deletePressed: inout Bool, editMode: inout EditMode) {
        if editMode == .active {
            editMode = .inactive
            deletePressed.toggle()
        }
    }

    func deleteContent(deletePressed: inout Bool, editMode: inout EditMode) {
        if editMode == .inactive {
            editMode = .active
            deletePressed.toggle()
        } else {
            editMode = .inactive
            deletePressed.toggle()
        }
    }

    func addingContent(isAdding: inout Bool, editMode: inout EditMode) {
        if editMode == .active {
            editMode = .inactive
        }
        isAdding.toggle()
    }
}

then you can use this at call

struct ContentView: View {
    @Environment(\.presentationMode) var presentationMode
    @StateObject var toolbarViewModel = ToolbarViewModel()
    @State private var deletePressed = false
    @State private var isAdding = false
    @State private var editMode: EditMode = .active

    var body: some View {
        NavigationView {
            VStack {
                Text("Hello, world!")
                    .padding()
            }
            .toolbar {
                ToolBarCancelDeleteAdd(deletePressed: $deletePressed) {
                    toolbarViewModel.deleteContent(deletePressed: &deletePressed, editMode: &editMode)
                    presentationMode.wrappedValue.dismiss()
                } deleting: {
                    toolbarViewModel.deleteContent(deletePressed: &deletePressed, editMode: &editMode)
                } adding: {
                    toolbarViewModel.addingContent(isAdding: &isAdding, editMode: &editMode)
                }
            }
        }
    }
}
enum EditMode {
    case active, inactive
}

Upvotes: 1

Related Questions