如何从SwiftUI App菜单.command调用外部View中的现有函数?
在macCatalyst SwiftUI应用中从App菜单Commands调用主View函数
问题背景
你目前在macCatalyst平台的SwiftUI应用中,通过commands为App菜单添加了“New”命令,但该命令当前只能调用自定义ObservableObject类的onAdd()方法。你希望直接调用主SwiftUI View(ContentView)中已有的onAdd()函数,但直接调用时出现作用域错误——这是因为App层级的commands无法直接访问View实例的方法。
解决方案
核心思路是通过ObservableObject作为事件传递的桥梁,将菜单命令的触发信号传递给主View,由主View执行自身的onAdd()函数。以下是具体实现步骤:
1. 修改ObservableObject,添加事件触发机制
使用Combine框架的PassthroughSubject来发送触发信号(也可以用@Published变量实现,这里推荐更适合事件场景的PassthroughSubject):
import Combine class MyObject: ObservableObject { // 创建一个用于传递"新建"事件的信号源 let addTrigger = PassthroughSubject<Void, Never>() func onAdd() { // 发送触发信号 addTrigger.send() } }
2. 在主ContentView中订阅触发信号
在ContentView中通过onReceive订阅MyObject的信号,收到信号后调用自身的onAdd()函数:
struct ContentView: View { @EnvironmentObject var myObject: MyObject // 你的现有属性:items、bulleton、fullText等 @State private var items: [Item] = [] @State private var bulleton: Bool = false private var fullText = "" private var fullText2 = "" private var fullText3 = "" static var count = 0 static var number = 0 public func onAdd() { // 你的现有实现逻辑 if bulleton == true { items.append(Item(title: fullText, subtitle: fullText2, isExpanded: false,isChecked: false)) saveNotes() } else { items.append(Item(title: fullText, subtitle: fullText3, isExpanded: false,isChecked: false)) Self.count = 1 Self.count += 1 Self.number += 1 saveNotes() } } private func saveNotes() { // 你的保存逻辑 } var body: some View { // 你的View内容 Text("Your App Content") .onReceive(myObject.addTrigger) { _ in // 收到菜单触发信号,执行自身的onAdd onAdd() } } } // 假设你的Item模型 struct Item: Identifiable { let id = UUID() var title: String var subtitle: String var isExpanded: Bool var isChecked: Bool }
3. 保持App结构体的Commands代码不变
原有的commands代码无需修改,依然调用myObject.onAdd()——现在这个方法会发送信号给ContentView,触发View自身的onAdd():
@main struct ApptestApp: App { let myObject = MyObject() var body: some Scene { WindowGroup { ContentView() .environmentObject(myObject) } .commands { CommandGroup(before: CommandGroupPlacement.newItem) { Button(action:{ myObject.onAdd() }, label: { Text("New") }) .keyboardShortcut("n") } } } }
替代方案:使用@Published变量
如果你不想引入Combine,也可以用@Published布尔变量实现触发:
- 修改
MyObject:
class MyObject: ObservableObject { @Published var shouldTriggerAdd = false func onAdd() { shouldTriggerAdd = true // 重置状态,避免重复触发 DispatchQueue.main.async { self.shouldTriggerAdd = false } } }
- 在
ContentView中监听状态变化:
var body: some View { Text("Your App Content") .onChange(of: myObject.shouldTriggerAdd) { newValue in if newValue { onAdd() } } }
内容的提问来源于stack exchange,提问作者user46938
相关产品推荐
相关产品推荐

