You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从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布尔变量实现触发:

  1. 修改MyObject:
class MyObject: ObservableObject {
    @Published var shouldTriggerAdd = false
    
    func onAdd() {
        shouldTriggerAdd = true
        // 重置状态,避免重复触发
        DispatchQueue.main.async {
            self.shouldTriggerAdd = false
        }
    }
}
  1. 在ContentView中监听状态变化:
var body: some View {
    Text("Your App Content")
        .onChange(of: myObject.shouldTriggerAdd) { newValue in
            if newValue {
                onAdd()
            }
        }
}

内容的提问来源于stack exchange,提问作者user46938

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 23:59:58