Due to a bug on macOS Sonoma I am looking for a workaround to trigger default action of NSSavePanel
in a sandboxed app.
The issue is that the NSSavePanel
's accessorryView
view doesn't react to enter key press. This works on lower macOS version.
The enter key press calls performKeyEquivalent:
in the NSResponder
chain.
Can I just call savePanel.performKeyEquivalent
in the sandboxed app and call it a day?
import Carbon.HIToolbox.Events
override func performKeyEquivalent(with event: NSEvent) -> Bool {
if let savePanel, event.type == .keyDown && (event.keyCode == UInt16(kVK_Return) || event.keyCode == UInt16(kVK_ANSI_KeypadEnter)) {
savePanel.performKeyEquivalent(with: event)
} else {
super.performKeyEquivalent(with: event)
}
}
PS: this workaround is not needed on Ventura and below
Full quick example:
import Carbon.HIToolbox.Events
class CustomView: NSView {
weak var savePanel: NSSavePanel?
override func performKeyEquivalent(with event: NSEvent) -> Bool {
if let savePanel, event.type == .keyDown && (event.keyCode == UInt16(kVK_Return) || event.keyCode == UInt16(kVK_ANSI_KeypadEnter)) {
savePanel.performKeyEquivalent(with: event)
} else {
super.performKeyEquivalent(with: event)
}
}
}
class ViewController: NSViewController {
let savePanel = NSSavePanel()
override func viewDidLoad() {
super.viewDidLoad()
let customView = CustomView()
customView.addSubview(NSTextField(string: "11111111"))
customView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
customView.widthAnchor.constraint(equalToConstant: 120),
customView.heightAnchor.constraint(equalToConstant: 20)
])
customView.savePanel = savePanel
savePanel.accessoryView = customView
}
override func viewWillAppear() {
savePanel.runModal()
}
}
This was a bug on macOS14. Fixed in macOS15. Provided code works in production app.
import Carbon.HIToolbox.Events
override func performKeyEquivalent(with event: NSEvent) -> Bool {
if let savePanel, event.type == .keyDown && (event.keyCode == UInt16(kVK_Return) || event.keyCode == UInt16(kVK_ANSI_KeypadEnter)) {
savePanel.performKeyEquivalent(with: event)
} else {
super.performKeyEquivalent(with: event)
}
}