I'm working on a macOS app that shows a list of items. A user can perform different actions on each item using either the toolbar or the main menu. Since they are are just different ways to perform the same actions, I want to re-use the code. What would be a good approach to accomplish it?
我正在开发一款MacOS应用程序,它可以显示一系列商品。用户可以使用工具栏或主菜单对每个项目执行不同的操作。因为它们只是执行相同操作的不同方式,所以我想重复使用代码。什么是实现这一目标的好方法?
Here's an example to illustrate the problem:
这里有一个例子来说明这个问题:
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ItemListView()
}
.commands {
ItemCommands()
}
}
}
struct ItemListView: View {
var body: some View {
List {
...
}
.toolbar {
ToolbarItemGroup {
Button("Button1", action: action1)
Button("Button2", action: action2)
Button("Button3", action: action3)
}
}
}
// Intent functions
func action1() { ... }
func action2() { ... }
func action3() { ... }
}
struct ItemCommands: Commands {
var body: some Commands {
CommandMenu("Item") {
// I want to re-use my intent functions here.
Button("Button1") { ... }
Button("Button2") { ... }
Button("Button3") { ... }
}
}
}
Up until this point I didn't need to have any view models in my project, but this seems like a good use case for it. I could move my intent functions to a view model and then pass it to MyItemListView
and MyItemCommands
.
在此之前,我的项目中不需要任何视图模型,但这似乎是一个很好的用例。我可以将意图函数移动到视图模型,然后将其传递给MyItemListView和MyItemCommands。
It's also possible to pass an intent function from a view to a command as a focused value:
还可以将Intent函数作为焦点值从视图传递给命令:
// View
someView.focusedValue(\.myAction1Func, myAction1Func)
// Commands
@FocusedValue(\.myAction1Func) private var myAction1Func
Button("Action1") { myAction1Func?() }
But this approach doesn't scale well when you need to pass many different values to implement a command.
但是,当您需要传递许多不同的值来实现命令时,这种方法不能很好地伸缩。
更多回答
Asking for a “… good approach to accomplish it” is a bit vague in my opinion and I also believe that finding the best solution very much depends on what data the functions depends upon which we don’t know.
索要“…”实现它的好方法“在我看来有点含糊,我还认为找到最佳解决方案在很大程度上取决于我们不知道的函数依赖于什么数据。
If actions are to be executed at app level, then define them in App and pass then to the 2 views as parameters. But you can also define a model with the actions in it.
如果要在应用程序级别执行操作,则在应用程序中定义它们,然后将其作为参数传递给2个视图。但您也可以定义一个包含其中的操作的模型。
@PtitXav Thanks! But some actions depend on scene-specific state, so I can't define them at the app level.
@PtitXav谢谢!但一些动作取决于场景特定的状态,所以我不能在应用程序级别定义它们。
So how do you except actio declared called in App to execute code depending on scène state ? You need to use a model owned by App then holds the Scene state and the execution of the actions.
那么,除了在App中调用声明的actio之外,如何根据scène状态执行代码呢?您需要使用App拥有的模型,然后保存场景状态和操作的执行。
Can you add code to your sample where you access this "scene state"? I would just create a MenuButtonsViewModel
that executed the logic (it might even return a some View
with all the buttons as a tupled view) and pass in all the scene/context specific data to either that function (so it's available in the scope) or pass the context individually within the enclosing view scope (i.e. in your example it's CommandMenu() { Button("1") { vm.action1(sceneSpecificState) }}
你能在你的样例中添加代码来访问这个“场景状态”吗?我只需要创建一个MenuButtonsViewModel来执行逻辑(它甚至可能返回一个包含所有按钮的某个视图作为元组视图),并将所有特定于场景/上下文的数据传递给该函数(因此它在作用域中可用),或者在封闭的视图范围内单独传递上下文(即在您的示例中为CommandMenu(){Button(“1”){vm.action1(SceneSpecificState)})
use ViewModel:
使用视图模型:
class ItemListViewModel: ObservableObject {
func action1() { ... }
func action2() { ... }
func action3() { ... }
}
In your main app structure and views, you'd do something like:
在您的主要应用程序结构和视图中,您将执行如下操作:
@main
struct MyApp: App {
@StateObject var viewModel = ItemListViewModel()
var body: some Scene {
WindowGroup {
ItemListView(viewModel: viewModel)
}
.commands {
ItemCommands(viewModel: viewModel)
}
}
}
struct ItemListView: View {
@ObservedObject var viewModel: ItemListViewModel
var body: some View {
Button("Button1", action: viewModel.action1)
}
}
struct ItemCommands: Commands {
var viewModel: ItemListViewModel
var body: some Commands {
CommandMenu("Item") {
Button("Button1", action: viewModel.action1)
Button("Button2", action: viewModel.action2)
Button("Button3", action: viewModel.action3)
}
}
}
更多回答
我是一名优秀的程序员,十分优秀!