The Selector keyword has been deprecated in future versions of Swift How can I create Keyboard Shortcuts in Dialog without an Edit Menu
Attempt #2 doesn't work because NSUndoManager
has a method with the selector undo
and not undo:
.
I suggest you create a protocol to represent "an entity that responds to the undo action" and use that protocol to hold your selector:
@objc protocol UndoActionRespondable {
func undo(sender: AnyObject)
}
let undoSelector = #selector(UndoActionRespondable.undo(_:))
Cocoa Keyboard Shortcuts in Dialog without an Edit Menu
What worked for me was using The View Solution presented in Copy and Paste Keyboard Shortcuts at CocoaRocket.
Basically, this means subclassing NSTextField and overriding performKeyEquivalent:
.
Update: The CocoaRocket site is apparently gone. Here's the Internet Archive link: http://web.archive.org/web/20100126000339/http://www.cocoarocket.com/articles/copypaste.html
Edit: The Swift code looks like this
class Editing: NSTextField {
private let commandKey = NSEventModifierFlags.CommandKeyMask.rawValue
private let commandShiftKey = NSEventModifierFlags.CommandKeyMask.rawValue | NSEventModifierFlags.ShiftKeyMask.rawValue
override func performKeyEquivalent(event: NSEvent) -> Bool {
if event.type == NSEventType.KeyDown {
if (event.modifierFlags.rawValue & NSEventModifierFlags.DeviceIndependentModifierFlagsMask.rawValue) == commandKey {
switch event.charactersIgnoringModifiers! {
case "x":
if NSApp.sendAction(Selector("cut:"), to:nil, from:self) { return true }
case "c":
if NSApp.sendAction(Selector("copy:"), to:nil, from:self) { return true }
case "v":
if NSApp.sendAction(Selector("paste:"), to:nil, from:self) { return true }
case "z":
if NSApp.sendAction(Selector("undo:"), to:nil, from:self) { return true }
case "a":
if NSApp.sendAction(Selector("selectAll:"), to:nil, from:self) { return true }
default:
break
}
}
else if (event.modifierFlags.rawValue & NSEventModifierFlags.DeviceIndependentModifierFlagsMask.rawValue) == commandShiftKey {
if event.charactersIgnoringModifiers == "Z" {
if NSApp.sendAction(Selector("redo:"), to:nil, from:self) { return true }
}
}
}
return super.performKeyEquivalent(event)
}
}
Edit: The Swift 3 code looks like this
class Editing: NSTextView {
private let commandKey = NSEventModifierFlags.command.rawValue
private let commandShiftKey = NSEventModifierFlags.command.rawValue | NSEventModifierFlags.shift.rawValue
override func performKeyEquivalent(with event: NSEvent) -> Bool {
if event.type == NSEventType.keyDown {
if (event.modifierFlags.rawValue & NSEventModifierFlags.deviceIndependentFlagsMask.rawValue) == commandKey {
switch event.charactersIgnoringModifiers! {
case "x":
if NSApp.sendAction(#selector(NSText.cut(_:)), to:nil, from:self) { return true }
case "c":
if NSApp.sendAction(#selector(NSText.copy(_:)), to:nil, from:self) { return true }
case "v":
if NSApp.sendAction(#selector(NSText.paste(_:)), to:nil, from:self) { return true }
case "z":
if NSApp.sendAction(Selector(("undo:")), to:nil, from:self) { return true }
case "a":
if NSApp.sendAction(#selector(NSResponder.selectAll(_:)), to:nil, from:self) { return true }
default:
break
}
}
else if (event.modifierFlags.rawValue & NSEventModifierFlags.deviceIndependentFlagsMask.rawValue) == commandShiftKey {
if event.charactersIgnoringModifiers == "Z" {
if NSApp.sendAction(Selector(("redo:")), to:nil, from:self) { return true }
}
}
}
return super.performKeyEquivalent(with: event)
}
}
How to add undo/redo support for NSTextView and NSTextField when building a macOS application programmatically?
This works on my system:
// **** Edit Menu **** //
let editMenu = NSMenuItem()
editMenu.submenu = NSMenu(title: "Edit")
editMenu.submenu?.addItem(withTitle: "Undo", action: Selector(("undo:")), keyEquivalent: "z")
editMenu.submenu?.addItem(withTitle: "Redo", action: Selector(("redo:")), keyEquivalent: "Z")
editMenu.submenu?.addItem(.separator())
editMenu.submenu?.addItem(withTitle: "Cut", action: #selector(NSText.cut(_:)), keyEquivalent: "x")
editMenu.submenu?.addItem(withTitle: "Copy", action: #selector(NSText.copy(_:)), keyEquivalent: "c")
editMenu.submenu?.addItem(withTitle: "Paste", action: #selector(NSText.paste(_:)), keyEquivalent: "v")
editMenu.submenu?.addItem(withTitle: "Select All", action: #selector(NSText.selectAll(_:)), keyEquivalent: "a")
Make sure you have the other edit menu items. In order to 'undo' something you have to 'do' it to start with. Also need to set txtView.allowsUndo = true as you have correctly done. Please note that Selector(someString) is different from #selector(). It's actually old swift syntax, but it still works. Swift relies on objc for selectors.
Related Topics
Monitoring App Switching on Os X
Swift Access to Variable Length Array
Create View Based Nstableview Programmatically Using Bindings in Swift
Wrapping a Generic Method in a Class Extension
Cross Platform Aes Encryption Between iOS and Kotlin/Java Using Apples Cryptokit
Dynamic/Runtime Dispatch in Swift, or "The Strange Way Structs Behave in One Man's Opinion"
Uitextview Change Text Color of Specific Text
How to Load My Own Reality Composer Scene into Realitykit
How to Handle Hash Collisions for Dictionaries in Swift
How to Set Exit Code Value for a Command Line Utility in Swift
Swift - Reorder Uitableview Cells
Skaction Completion Handlers; Usage in Swift
Difference Between Text("") and Text(Verbatim: "") Initializers in Swiftui
Use Multiple Codingkeys for a Single Property
What Is the Use of the Validate() Method in Alamofire.Request