How would you share common button actions across views?

How would you share a common Button(action: { some action }) across Views?

I have:

VStack {
   ToolbarViewA(states)
   TableViewB(states)
   ...
}

View initializer states include $data & $selection, so each View has the 'same' data.

I would like a Button in a contextMenu on a Table in TableViewB to perform the identical click action as a Button in ToolbarViewA.

How would you approach this challenge?

I am interested in learning how to think SwiftUI so I would appreciate your thoughts! I really like v5, it is gelling with me. Thank you Apple!

Answered by Claude31 in 743288022

What I would do:

  • Define States class
class States: ObservableObject {
    @Published var data: =           // some type of data and initial value
    @Published var selection :  = "" // some type of data and initial value
}
  • define (even at main app level) a State object for states : States
  • declare it in the environment
@main
struct TheApp: App {
    @StateObject var states = States()  

    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(states)    // Essential, to pass value to environment
        }
    }
}
  • create a View holding the Button using this environment object
struct CommonButton: View {
    @EnvironmentObject var states: States

    var body: some View {
        Button(action: {
          // using states
        }) {
            Text("Button title")  // That could be specific to the Button: declare a var for Title that will be passed as parameter
           }
    }
}

Use CommonButton where needed

Accepted Answer

What I would do:

  • Define States class
class States: ObservableObject {
    @Published var data: =           // some type of data and initial value
    @Published var selection :  = "" // some type of data and initial value
}
  • define (even at main app level) a State object for states : States
  • declare it in the environment
@main
struct TheApp: App {
    @StateObject var states = States()  

    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(states)    // Essential, to pass value to environment
        }
    }
}
  • create a View holding the Button using this environment object
struct CommonButton: View {
    @EnvironmentObject var states: States

    var body: some View {
        Button(action: {
          // using states
        }) {
            Text("Button title")  // That could be specific to the Button: declare a var for Title that will be passed as parameter
           }
    }
}

Use CommonButton where needed

quick question if it's ok...I'm still learning, so any tips will help!

what might I need to look at, to make sure arrays "publish"?

I had originally started with a similar pattern as you @Claude31 ... but not as clean (probably where I went astray)

typealias Data = [Datum]

class StateFactory: ObservableObject {
    @Published var data:Data =  []
    @Published var selection:Set<Datum.id>  = []

    dataLoad(input:Data) { ..  }

}

views deep down for some reason didn't recognize that the @Published Data array had been updated, and so went on blissfuly unaware..

However...if I pulled each array up to the app level...then things worked...


 func dataLoad(store:inout Data, input: Data) { ..  }

struct MyApp: App {
  @State var data:Data = []

 var body: some Scene { 
      ContentView { 
          ...
      }
      .onAppear { dataLoad(store: &data, input: someFactory()  ) }
      ...
     

I wasn't sure where I went wrong, how how to get StateFactory.data to signal that yo, I have updated

I would prefer to control that signal as data may be appended hundreds/thousands of times. But, it could just as well have been some sort of other bug of my own doing.

thanks in advance to any who offer tips/tricks!

How would you share common button actions across views?
 
 
Q