SwiftUI开发Mac状态栏应用:如何调用字体选择器并获取结果?
在SwiftUI Mac状态栏应用中触发字体选择器并捕获结果(Xcode 11.7)
当然可以实现!在Xcode 11.7的SwiftUI Mac状态栏应用里,由于SwiftUI本身没有原生的字体选择器组件,我们可以借助AppKit的NSFontPanel来完成这个需求,下面是具体的实现步骤和代码示例:
1. 创建字体状态管理器
首先我们需要一个ObservableObject来管理选中的字体状态,同时封装触发字体选择器和处理结果的逻辑:
import SwiftUI import AppKit class FontManager: ObservableObject { // 用@Published标记,让SwiftUI视图能监听字体变化 @Published var selectedFont: NSFont = .systemFont(ofSize: 14) func showFontPicker() { let fontPanel = NSFontPanel.shared // 设置当前选中的字体作为面板初始值 fontPanel.setPanelFont(selectedFont, isMultiple: false) // 弹出字体选择器 fontPanel.makeKeyAndOrderFront(nil) // 设置代理来捕获用户选择的结果 fontPanel.delegate = self } } // 实现NSFontPanelDelegate协议,处理字体选择回调 extension FontManager: NSFontPanelDelegate { func fontPanel(_ sender: NSFontPanel, didChangeFont font: NSFont?) { guard let newFont = font else { return } // 更新选中的字体状态,SwiftUI视图会自动刷新 selectedFont = newFont // 这里可以添加自定义的业务逻辑,比如保存字体设置 print("已选中字体:\(newFont.displayName ?? "未知字体"),大小:\(newFont.pointSize)") } }
2. 构建弹出窗口的SwiftUI视图
接下来创建弹出窗口的内容视图,添加触发按钮并展示选中的字体信息:
struct PopupView: View { // 注入FontManager实例,监听字体状态变化 @ObservedObject var fontManager = FontManager() var body: some View { VStack(spacing: 20) { Text("当前选中字体") .font(.headline) Text("\(fontManager.selectedFont.displayName ?? "未选择字体")") Text("字体大小:\(fontManager.selectedFont.pointSize, specifier: "%.1f")") Button("打开字体选择器") { fontManager.showFontPicker() } .padding(.horizontal, 16) .padding(.vertical, 8) .background(Color.accentColor) .foregroundColor(.white) .cornerRadius(6) } .padding(24) .frame(width: 300) } }
3. 配置状态栏应用的核心逻辑
最后我们需要设置状态栏项目和弹出窗口的显示逻辑,这部分需要借助AppKit的NSStatusItem和NSWindow:
import Cocoa import SwiftUI @NSApplicationMain class AppDelegate: NSObject, NSApplicationDelegate { var statusItem: NSStatusItem! var popupWindow: NSWindow! func applicationDidFinishLaunching(_ aNotification: Notification) { // 创建状态栏项目 statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength) // 设置状态栏按钮的图标和点击事件 if let button = statusItem.button { button.image = NSImage(systemSymbolName: "textformat", accessibilityDescription: "字体选择工具") button.action = #selector(togglePopup(_:)) } // 创建无标题栏的弹出窗口,包裹SwiftUI视图 let popupView = PopupView() popupWindow = NSWindow( contentRect: NSRect(x: 0, y: 0, width: 300, height: 200), styleMask: [.borderless], backing: .buffered, defer: false ) popupWindow.contentView = NSHostingView(rootView: popupView) // 关闭窗口时不释放,避免重复创建崩溃 popupWindow.isReleasedWhenClosed = false // 设置窗口层级为状态栏级别 popupWindow.level = .statusBar } // 切换弹出窗口的显示/隐藏状态 @objc func togglePopup(_ sender: Any?) { if popupWindow.isVisible { popupWindow.orderOut(nil) } else { // 将窗口定位到状态栏按钮的正下方 if let button = statusItem.button { let buttonFrame = button.window!.frame popupWindow.setFrameOrigin(NSPoint( x: buttonFrame.origin.x + buttonFrame.width/2 - popupWindow.frame.width/2, y: buttonFrame.origin.y - popupWindow.frame.height )) } popupWindow.orderFront(nil) } } }
关键注意事项
- Xcode 11.7中,SwiftUI视图需要通过
NSHostingView才能嵌入到AppKit的NSWindow中。 - 确保
popupWindow的isReleasedWhenClosed设为false,否则关闭窗口后再次打开会导致崩溃。 NSFontPanel是系统全局共享的面板,所以不需要手动销毁,用户关闭面板后会自动隐藏。
内容的提问来源于stack exchange,提问作者Manngo
相关产品推荐
相关产品推荐

