I am having a NSOutlineView in which the textfield is editable by either pressing return or using the right click. I am also overriding the menu(for event: NSEvent) -> NSMenu? to menu based on which row is clicked
When I right click on the textfield, it opens the correct menu as expected but it also makes the textfield to go into edit mode. Is there a way to handle this behaviour?
However, when I click outside the textfield then it works without setting the textfield in edit mode:
I have a similar NSTableView where right-clicking is supported to display a menu, but the fields are also directly editable. I took a look to see why/how the editing doesn't seem to be a problem in my case, and I narrowed the behavior down to the presence of this line of code in my NSTableView subclass:
// This trick convinces the accessibility system to bother checking whether
// we have a menu to export to e.g. VoiceOver.
[self setMenu:[[NSMenu alloc] init]];
As you can see from the comment, the rationale for this "setting an empty menu" was for a different reason than avoiding the editing behavior you're seeing, but it appears to have the side-effect of also fixing that.
So, please try adding a line like the above to your NSOutlineView subclass and see if it fixes the problem!
I ran into this same problem trying to get my Swift-based app on Monterey working. I was surprised there was not a more simple/obvious solution, but here is what I came up with:
class MyOutlineView: NSOutlineView {
// prevent text field from becoming editable when context menu is used
override func validateProposedFirstResponder(_ responder: NSResponder,
for event: NSEvent?) -> Bool
{
guard let validEvent = event,
contextMenuTriggered(event: validEvent),
selectedRow != -1,
selectedRow == row(at: convert(validEvent.locationInWindow, from: nil))
else {
return super.validateProposedFirstResponder(responder, for: event)
}
return false
}
private func contextMenuTriggered(event: NSEvent) -> Bool {
return event.modifierFlags.contains(.control)
&& (event.type == .leftMouseDown || event.type == .leftMouseUp)
}
}
This detects if a control-click happened on the selected row in the outline view, then blocks it from receiving focus so the text field does not become editable.
Credit for this approach goes to the answer provided by #strangetimes here: Disable editing of a NSTextFieldCell on right clicking on a row
Related
According to link we should use a singleton UIMenuController instance which is referred to the editing menu.
The problem is I want to show extra items in different situations. For instance, I want to just show "copy" item when keyboard is up. and show "copy" and "reply" when tapping on a tableview row.
When I add "reply" to the UIMenuController instance it is shown when tapping on UITextField too. therefore, I added these codes:
func textViewDidBeginEditing(_ textView: UITextView) {
var nonReplyMenuItems: [UIMenuItem] = []
if let allMenuItems = UIMenuController.shared.menuItems {
for menuItem in allMenuItems {
if menuItem.title != "reply".localized {
nonReplyMenuItems.append(menuItem)
}
}
}
UIMenuController.shared.menuItems = nonReplyMenuItems
UIMenuController.shared.setMenuVisible(true, animated: true)
}
It fixed the problem in most situations, but not all.
when keyboard is up and tapping on a row in tableview "reply" will be added. Then when I tap on UITextView the reply will be shown there too.
It seems your scenario is like it:
tap on textfield ----> shows copy
tap on tableview ---> shows copy and reply
tab on textfield ----> shows copy and reply (you want only copy shows)
As I know the textViewDidBeginEditing calls when your text filed is not editing and you tap on that; So if you have two textfileds by switching on that method calls every time but when you are switching between a text filed and another action base object your text field is editing and its state has not changed.
When you touch on tableview you must call textfield.resignFirstResponder() so when you tap on text field again the textViewDidBeginEditing calls again, the problem of this is hiding keyboard; The better way I preferو is adding function to touch down of text field or on gesture to do what you write on textViewDidBeginEditing method
how would I go about making a previously selectedRow of an NSTableView stay selected after the user presses a button calling a method to work with that selectedRow?
It worked in a previous programm looking like this:
selecting it gives it the OSX selection color, clicking the save button makes the textField becomeFirstResponder() which makes the row go grey automatically and doesnt change the selectedRow property. This is not done by my code, so im happy it just works.
Now in a similar program I want to achieve exactly this, but after clicking a button (calling a method making the textField becomeFirstResponder()) the visual selection is gone and also the tableView property selectedRow goes back to -1 (no row selected).
I tried a lot of ideas, most answers online are objective-C. Here is my most recent code (beeing called by the same button and pretty much faking what worked automatically before):
the NSTableViewAction method and the selectedRowCache variable:
var selectedRowCache = -1
#IBAction func alarmDataTableView(_ sender: NSTableView) {
selectedRowCache = alarmDataTableView.selectedRow
}
the buttonAction:
#IBAction func editAlarmButton(_ sender: NSButton) {
let row = alarmDataTableView.rowView(atRow: self.selectedRowCache, makeIfNecessary: false)
row?.backgroundColor = .blue
}
the selectedRowCache is just a variable that saves the selectedRow whenever the user selects a row (i dont want this).
no with this code the background color of the row doesnt do a thing (besides losing its selection after the button method is called).
the NSTableView is view based.
Thank you for any help!
In an NSTextView I am trying to make tab and shift-tab play a role in editing text, without tabs being inserted in the text. Currently I am detecting keypresses via NSTextViewDelegate's keyUp method:
override func keyUp(with event: NSEvent) {
if let currChar = event.characters {
if event.keyCode == 48 { // detect tab key
if event.modifierFlags.rawValue == 256 { // detect no modifier key
// do something
} else if event.modifierFlags.rawValue == 131330 { // detect shift modifier
// do another thing
}
}
}
I can't see anything in the documentation how to indicate to the NSTextView that I want it to ignore the tab key (I have tried the answer shown here, but tabs do not appear to trigger this event), or to prevent the event from moving up the responder chain.
I have also tried calling interpretKeyEvents([event]) at the beginning of my keyUp method, and overriding insertTab and insertBacktab. These are successfully called with the right keypresses, but a tab is still inserted into the text. The documentation seems to suggest it should prevent the tab being inserted:
It [keyDown] can pass the event to Cocoa’s text input management system by invoking the NSResponder method interpretKeyEvents:. The input management system checks the pressed key against entries in all relevant key-binding dictionaries and, if there is a match, sends a doCommandBySelector: message back to the view. Otherwise, it sends an insertText: message back to the view, and the view implements this method to extract and display the text. (emphasis mine)
The documentation talks about an event continuing up the responder chain if it has not been handled - how is this indicated? Is it important that I am using keyUp, not keyDown?
Yes, it matters that you’re overriding keyUp: instead of keyDown:. The key-down event happens before the key-up event, and NSTextView acts on the key-down event. By the time the system has called your keyUp: override, it’s too late to prevent the default handling of the key-down event.
Use custom subclass. If these methods are not being called it means the first responder is someone else and has eaten your event. As long as your textview is first responder your keyDown method will be called.
class MyTextView: NSTextView {
override func insertTab(_ sender: Any?) {
self.insertText("HELLO", replacementRange: NSMakeRange(0, 0))
//OR ANY CUSTOM TEXT EDITING, ACTION TO CHANGE FIRST RESPONDER...
}
override func insertBacktab(_ sender: Any?) {
self.insertText("AAAAA", replacementRange: NSMakeRange(0, 0))
//OR ANY CUSTOM TEXT EDITING, ACTION TO CHANGE FIRST RESPONDER...
}
}
Educational: "Key Event Handling in Cocoa Applications from WWDC 2010"
Is there a way to change the responder or select another textfield by pressing tab on the keyboard, in Swift?
Notes:
It's for a fill in the blank type application.
My VC creates a list of Words [Word], and each of those words has its own WordView - word.wordView. The WordView is what is displayed. WordView is a child of NSTextField.
I tried to override keydown but it doesn't allow me to type anything in the text view.
You have to connect your textField nextKeyView to the next textField through the IB or programmatically:
textField1.nextKeyView = textField2
Assuming you want to go from textView1 to textView2. First set the delegate:
self.textView1.delegate = self
Then implement the delegate method:
func textView(textView: NSTextView, doCommandBySelector commandSelector: Selector) -> Bool {
if commandSelector == "insertTab:" && textView == self.textView1 {
self.window.makeFirstResponder(self.textView2)
return true
}
return false
}
If you want some control over how your field tabs or moves with arrow keys between fields in Swift, you can add this to your delegate along with some move meaningful code to do the actual moving like move next by finding the control on the superview visibly displayed below or just to the right of the control and can accept focus.
public func control(_ control: NSControl, textView: NSTextView, doCommandBy commandSelector: Selector) -> Bool {
switch commandSelector {
case #selector(NSResponder.insertTab(_:)), #selector(NSResponder.moveDown(_:)):
// Move to the next field
Swift.print("Move next")
return true
case #selector(NSResponder.moveUp(_:)):
// Move to the previous field
Swift.print("Move previous")
return true
default:
return false
}
return false // I didn't do anything
}
I had the same or a similar problem, in that I wanted to use an NSTextView field, to allow multiple lines of text to be entered, but it was the sort of field where entering a tab character would make no sense. I found an easy fix for this: NSTextView has an instance property of isFieldEditor, which is set to false by default; simply set this to true, and tabs will now skip to the next field.
I use a couple of buttons '0123456789.' (and some others) as an alternative for a keyboard. They are connected to (several) texfields. I made them all programmatically, so without storyboard. I also use UITextFieldDelegate. That works as expected, and I can input my text into the textfields.
I use the following code for my buttons:
func textFieldDidBeginEditing(textField: UITextField) {
activeField = textField
}
#IBAction func Pressed(sender: UIButton) {
if (activeField != nil) {
switch sender.tag {
case 0:
activeField!.text = activeField!.text+"0"
case 1:
The problem is that every-time I click in a textfield, the keyboard opens up too. I want to avoid that... since I made an alternative for input with the buttons. How can I get rid of the keyboard when I click in a textfield?
Just return NO from textFieldShouldBeginEditing: from the delegate you mention you are already using.
When the user performs an action that would normally initiate an editing session, the text field calls this method first to see if editing should actually proceed. In most circumstances, you would simply return YES from this method to allow editing to proceed.
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
if (textField == /* a text field that is using your custom layout */)
return NO;
}
[Source]
Though why don't you just use [textField setKeyboardType:UIKeyboardTypeNumberPad];
You should make your custom keypad the inputView of the textField. It will then be shown instead of the built-in keyboard.
i.e.:
textField.inputView = yourCustomKeypad;
I've made a similar input interface, using custom buttons...why not instead of a UITextField, use a UILabel to display the "8,900"(input).
Then just make the buttons manipulate the UILabel.text.