How to detect when NSTextField has focus or cocoa content is selected

I have an NSTextField inside an NSTableCellView, and I want an event that tells me when my NSTextField has focus to disable multiple buttons, I found this method:

-(void)controlTextDidBeginEditing:(NSNotification *)obj{ NSTextField *textField = (NSTextField *)[obj object]; if (textField != _nombreDelPaqueteTextField) { [_nuevaCuentaActivoButton setEnabled:FALSE]; [_nuevaCuentaPasivoButton setEnabled:FALSE]; [_nuevaCuentaIngresosButton setEnabled:FALSE]; [_nuevaCuentaEgresosButton setEnabled:FALSE]; } } 

but it only starts when my text box starts editing, as it says, I want the buttons to be disabled when I get focus on the text box, and not when I started typing


EDIT: We compile my code based on the help received by Joshua Nozzi, it still does not work.

MyNSTextField.h

 #import <Cocoa/Cocoa.h> @class MyNSTextField; @protocol MyNSTextFieldDelegate @optional -(BOOL)textFieldDidResignFirstResponder:(NSTextField *)sender; @optional -(BOOL)textFieldDidBecomeFirstResponder:(NSTextField *)sender; @end @interface MyNSTextField : NSTextField @property (strong, nonatomic) id <MyNSTextFieldDelegate> cellView; @end 

MyNSTextField.m

 #import "MyNSTextField.h" @implementation MyNSTextField - (BOOL)becomeFirstResponder { BOOL status = [super becomeFirstResponder]; if (status) [self.cellView textFieldDidBecomeFirstResponder:self]; return status; } - (BOOL)resignFirstResponder { BOOL status = [super resignFirstResponder]; if (status) [self.cellView textFieldDidResignFirstResponder:self]; return status; } @end 

on my view controller EdicionDeCuentasWC.m

 #import "MyNSTextField.h" @interface EdicionDeCuentasWC ()<NSTableViewDataSource, NSTableViewDelegate, NSControlTextEditingDelegate, NSPopoverDelegate, MyNSTextFieldDelegate> @end @implementation EdicionDeCuentasWC #pragma mark MyNSTextFieldDelegate -(BOOL)textFieldDidBecomeFirstResponder:(NSTextField *)sender{ NSLog(@"textFieldDidBecomeFirstResponder"); return TRUE; } -(BOOL)textFieldDidResignFirstResponder:(NSTextField *)sender{ NSLog(@"textFieldDidResignFirstResponder"); return TRUE; } #pragma mark -- @end 

This is important to say in the visual editor, already changed all my NSTextFields to the MyNSTextField class and assigned a delegate to my file owner (EdicionDeCuentasWC)

+5
source share
4 answers

I think I nailed it. I tried to subclass NSTextFiled to override becomeFirstResponder() and resignFirstResponder() , but as soon as I click on it, becomeFirstResponder() is called and resignFirstResponder() is called right after that. A? But the search box is still under editing, and the focus is still on it.

I realized that when you click on the search field, the search field will become the first responder once, but NSText will be prepared somewhere later, and the focus will be transferred to NSText .

I found out that when an NSText prepared, it is set to self.currentEditor() . The problem is that when the call becomeFirstResponder() , self.currentEditor() is not set yet. Thus, becomeFirstResponder() not a focus detection method.

On the other hand, when the focus is moved to an NSText , the resignFirstResponder() text field is resignFirstResponder() , and you know what? self.currentEditor() . So, the time has come to inform the delegate that this text box has been focused.

Then, how to determine when the search field has lost focus. Again, this is about an NSText . Then you need to listen to the NSText delegate NSText , such as textDidEndEditing() , and make sure that you allow the superclass to process this method and see if self.currentEditor() . If so, NSText lost focus and informed the delegate of the text field about it.

I provide code, in fact an NSSearchField subclass, to do the same. And the same principle should work for NSTextField .

 protocol ZSearchFieldDelegate: NSTextFieldDelegate { func searchFieldDidBecomeFirstResponder(textField: ZSearchField) func searchFieldDidResignFirstResponder(textField: ZSearchField) } class ZSearchField: NSSearchField, NSTextDelegate { var expectingCurrentEditor: Bool = false // When you clicked on serach field, it will get becomeFirstResponder(), // and preparing NSText and focus will be taken by the NSText. // Problem is that self.currentEditor() hasn't been ready yet here. // So we have to wait resignFirstResponder() to get call and make sure // self.currentEditor() is ready. override func becomeFirstResponder() -> Bool { let status = super.becomeFirstResponder() if let _ = self.delegate as? ZSearchFieldDelegate where status == true { expectingCurrentEditor = true } return status } // It is pretty strange to detect search field get focused in resignFirstResponder() // method. But otherwise, it is hard to tell if self.currentEditor() is available. // Once self.currentEditor() is there, that means the focus is moved from // serach feild to NSText. So, tell it delegate that the search field got focused. override func resignFirstResponder() -> Bool { let status = super.resignFirstResponder() if let delegate = self.delegate as? ZSearchFieldDelegate where status == true { if let _ = self.currentEditor() where expectingCurrentEditor { delegate.searchFieldDidBecomeFirstResponder(self) // currentEditor.delegate = self } } self.expectingCurrentEditor = false return status } // This method detect whether NSText lost it focus or not. Make sure // self.currentEditor() is nil, then that means the search field lost its focus, // and tell it delegate that the search field lost its focus. override func textDidEndEditing(notification: NSNotification) { super.textDidEndEditing(notification) if let delegate = self.delegate as? ZSearchFieldDelegate { if self.currentEditor() == nil { delegate.searchFieldDidResignFirstResponder(self) } } } } 

You will need to change the NSSerachField to ZSearchField , and your client class should match ZSearchFieldDelegate not NSTextFieldDelegate . Here is an example. When the user clicked on the search field, it expanded its width, and when you click on another place, the search field lost focus and reduced its width by changing the NSLayoutConstraint value set by the interface constructor.

 class MyViewController: NSViewController, ZSearchFieldDelegate { // [snip] @IBOutlet weak var searchFieldWidthConstraint: NSLayoutConstraint! func searchFieldDidBecomeFirstResponder(textField: ZSearchField) { self.searchFieldWidthConstraint.constant = 300 self.view.layoutSubtreeIfNeeded() } func searchFieldDidResignFirstResponder(textField: ZSearchField) { self.searchFieldWidthConstraint.constant = 100 self.view.layoutSubtreeIfNeeded() } } 

It may depend on the behavior of the OS, I tried El Capitan 10.11.4 and it worked.

Code can also be copied from Gist. https://gist.github.com/codelynx/aa7a41f5fd8069a3cfa2

+3
source

I have my own subclass of NSTextField that overrides -becomeFirstResponder and -resignFirstResponder . The -cellView property requires a protocol -textDidBecome/ResignFirstResponder:(NSTextField *)sender declaring -textDidBecome/ResignFirstResponder:(NSTextField *)sender , but enough to give you a general idea. It can be easily modified to send notifications for which your controller can register as an observer. Hope this helps.

 - (BOOL)becomeFirstResponder { BOOL status = [super becomeFirstResponder]; if (status) [self.cellView textFieldDidBecomeFirstResponder:self]; return status; } - (BOOL)resignFirstResponder { BOOL status = [super resignFirstResponder]; if (status) [self.cellView textFieldDidResignFirstResponder:self]; return status; } 
+2
source

I found the following code on macrumors forums .

  • It is the first responder to a text view (the field editor is a text view).
  • Is there a field editor?
  • Is the text field a delegate of the field editor

This seems to work.

 - (BOOL)isTextFieldInFocus:(NSTextField *)textField { BOOL inFocus = NO; inFocus = ([[[textField window] firstResponder] isKindOfClass:[NSTextView class]] && [[textField window] fieldEditor:NO forObject:nil]!=nil && [textField isEqualTo:(id)[(NSTextView *)[[textField window] firstResponder]delegate]]); return inFocus; } 
0
source

Swift3.0

Check if text is selected in NSTextField

 if theSearchTextField.currentEditor()?.selectedRange.length == 0{ print("IS SeletedAll") }else{ print("NOT SeletedAll") } 
0
source

Source: https://habr.com/ru/post/1201937/


All Articles