How to make NSTextfield lose focus when Enter is pressed? - objective-c

I want to make a text field in which if I click out of it, or press enter, it makes it lose focus. As in the focus ring disappears. I've have seen situations like this, but I do not know where to place the code for it. Can anyone show me how to make the NSTextfield lose it's focus?

One method would be to implement the NSTextFieldDelegate, assign the delegate to your text field, and have it call a selector (a method in your code that changes makes the first responder = nil). The delegate will be called with a message when the text field is finished receiving input. Check out the API here for more information:
https://developer.apple.com/library/mac/documentation/cocoa/reference/NSTextFieldDelegate_Protocol/Reference/Reference.html#//apple_ref/occ/intf/NSTextFieldDelegate

For people out there as lazy as me, here is some code.
This is the cut down NSTextfieldDelegate method i used:
func control(_ control: NSControl, textShouldEndEditing fieldEditor: NSText) -> Bool {
DispatchQueue.main.async {
self.textField2.window?.makeFirstResponder(nil)
}
return true
}
https://developer.apple.com/documentation/appkit/nstextview/1807135-resignfirstresponder?language=objc
resignFirstResponder Docu says the following:
Use the NSWindow method makeFirstResponder:, not this method, to make
a text view the first responder. Never invoke this method directly.
// don't use this as the name may suggest (at least it did for me)
self.textField2.resignFirstResponder()

Related

Click textfield and button disappears (it doesn't, but i want it to)

I have a textfield and a button. When I click inside the textfield, I want the button to disappear. I defined the textfield as both outlet and action ( with event “Did end on exit”). In the method for the textfield, I have self.testButton.hidden = YES; When I click inside the textfield, the button does not go away. Instead, it remains until I hit the return key on the keyboard – causing the keyboard to go away. I tried the same thing w/ touchup inside as the event on the text field. When you click in the text field, nothing happens to the button.
Instead of using the Target-Action mechanism ("Did end on exit" and "Touch Up Inside") use the Delegate mechanism.
First, make your class conform to the UITextFieldDelegate protocol. In your *.h (header) file add the following:
// Here I'm assuming your class is inheriting from UIViewcontroller but it
// may be inheriting from some other class. The really important part here
// is: <UITextFieldDelegate>. That's how you make your class conform to that protocol
#interface THE_NAME_OF_YOUR_CLASS : UIViewController <UITextFieldDelegate>
.
Second, implement the -(void)textFieldDidBeginEditing:(UITextField *)textField method. Also, remember to set yourself as the delegate too: self.textField.delegate = self. That way, the method will get called every time the user starts editing. Inside that methdod call self.testButton.hidden = YES;. In your *.m (implementation) file add the following:
-(void)viewDidLoad {
// here I'm assuming you have a 'strong' reference to your text field.
// You're going to need one to set yourself as the delegate.
self.textField.delegate = self;
}
// This is one of the methods defined in the UITextFieldDelegate protocol
-(void)textFieldDidBeginEditing:(UITextField *)textField {
self.testButton.hidden = YES;
}
.
Similarly, to make your button appear again, implement the - (void)textFieldDidEndEditing:(UITextField *)textField method. Inside it un-hide your button. Again, in your *.m file add the following:
// This is another method defined in the UITextFieldDelegate protocol
-(void)textFieldDidEndEditing:(UITextField *)textField {
self.testButton.hidden = NO;
}
Although delegates may be a mystery to you right now once you become familiar with them
you will realize they're very easy. And this is very important because iOS programming
relies heavily on delegates.
A delegate is a "notification" mechanism based on the "Hollywood" principle which is: don't call us; we'll call you.
In your case the class that contains the UITextField is interested in knowing when the UITextField begins editing and when it ends editing. But your class cannot be "polling" (that is, constantly asking) the text field to find out if the state changed. Instead you register your class with the text field and it will be the text field the one that will let you know when something happened. That will be thanks to the methods that you implemented.
Further reading: protocols and delegates
Hope this helps!
Have you made sure that testButton has its IBOutlet set before you hide it?
If you want to button to disappear when the user begins editing the text field, try UIControlEventEditingDidBegin.

NSComboBox action selector fires when setHidden:YES

I have an NSComboBox. I've set an action selector. When the box gets hidden the selector fires, even if the user never touched it. Yes, I need to hide it.
IBOutlet NSComboBox *comboBox;
[comboBox setAction:#selector(onComboBoxSelection:)];
- (void)onComboBoxSelection:(id)sender
{
NSLog(#"Why does this fire twice");
//My code doesn't actually set hidden here, it's just for proof while debugging the issue.
[comboBox setHidden:YES];
}
Why would hiding an NSControl fire it's selector? What's the best way to fix it?
Update:
I've fixed it by wrapping the method. But I'd still like to understand why, or other ways to fix it.
- (void)onComboBoxSelection:(id)sender
{
if(![sender isHidden]{
NSLog(#"Now only fires once");
//My code doesn't actually set hidden here, it's just for proof while debugging the issue.
[comboBox setHidden:YES];
}
}
Set a breakpoint in onComboBoxSelection: and look at the backtrace when it's called the second time (type bt in the debugger to see the backtrace). That will explain what's going.
A combo box is both a text field and a popup, and it will fire actions for both. The text field action is fired when editing ends, either by hitting the Return key or when it resigns first responder (e.g., tabbing out to another field).
When you hide the combo box, the text field resigns first responder and fires its action.
What you probably want to do is check if combo box value has actually changed, and only then proceed with hiding the combo box, etc.
Another option is to use data bindings to observe changes to the combo box. Bind the combo box value to a property on your controller. Then implement the property setter in your controller.
try this [comboBox setHidden:1];

NSStepper in NSTableCellView has no effect

I’m using a regular (not subclassed) NSTableCellView in a view-based table view. It has the initial image and text field views. I added an NSStepper to the view.
The text field is bound to tableCellView.objectValue.quantity.
The stepper’s value is bound to tableCellView.objectValue.quantity too.
The problem is that when running the app, when I click the stepper it doesn’t seem to get the mouse event, neither arrow gets highlighted, the value is not incremented or decremented.
If I set the double action of the table view it gets triggered if I double-click the stepper as if it was transparent.
What am I missing?
Thanks!
You should look at the documentation but easiest is that you need to subclass NSTableView and override this method to validate the proposed first responder. As the document states NSTableViews disallow some controls to be used unless the row is first selected. Even then it still may discard some.
- (BOOL)validateProposedFirstResponder:(NSResponder *)responder forEvent:(NSEvent *)event {
return YES;
}
Further to the correct answer from Robert Payne, with Swift you could add an extension to NSTableView and not subclass it.
extension NSTableView {
override public func validateProposedFirstResponder(responder: NSResponder, forEvent event: NSEvent?) -> Bool {
return true
}
}
And I'd like to emphasis that it's the NSTableView not the NSTableViewCell.

Don't change NSButton appearance when pressed or disabled

I have two NSButtons with images for both their on states and off states. Only one should be active at a time; click one and then click the other to change a property back and forth.
The problem is that if I disable a button when it's clicked so it cannot be clicked again, then the image is dimmed when the button is disabled--and I don't want it dimmed, I just want to use the alternate image.
On the other hand, if I just leave the button enabled, but programmatically just don't run any code when it is clicked, then there's a flashing effect as the mouse clicks--which is distracting, when the button should not do anything.
So I either need to prevent the button from being dimmed when it is disabled, or prevent it from changing the button appearance while the mouse button is held down.
After reading up, it sounds like I need to subclass NSButtonCell and override - (BOOL)imageDimsWhenDisabled to do the former. But I can't figure out exactly how to subclass it (what sort of NSButtonCell class I should inherit from) and if the "setCell" method of NSButton is enough to use the new NSButtonCell class, or if I need to subclass NSButton as well.
Some tips on that would be appreciated, or perhaps there's a completely different approach that would achieve my objectives.
Check this out:
[btnInfo.cell setImageDimsWhenDisabled:NO];
When you want to disable it without changing appearance do this:
On MacOS - NSButton:
Only option is to subclass NSButton and override mouseDown function
class RadioButton: NSButton {
override func mouseDown(with event: NSEvent) {}
}
On iOS - UIButton:
Simple disable UserInteraction
mybutton.isUserInteractionEnabled = false
For a more up to date answer in Swift, this works for me:
(theButton.cell! as! NSButtonCell).imageDimsWhenDisabled = false

Toggle-style button : how to toggle title in this case?

I have a CoreData app presenting data with a TableView, textfields, buttons... It deals with people situations and one of the button is a toggle-style button with title "Close". When we consider the user's case closed, we press and it changes the state of a boolean-type attribute in the entity, representing the closed/open state of the case, using a simple binding to the attribute value. The button title also becomes "Reopen" as the case may be reopened in the future.
Then additional things had to be done with the data on pressing the button, so I had to create an IBAction method instead of simply use the former binding. Problem: when button is pressed, the action is done, but the button title is not toggled. It makes sense since nothing tells it to toggle anymore.
I decided to remove the action on the boolean from the IBAction and use again the value binding, so the boolean change is performed by the binding and the other operations are performed by the IBAction. Problem: it modifies the data unexpectedly, sometimes working fine, sometimes not doing all things in a coherent way as expected.
So I'm back with all changes handled by the IBAction and this time, I'm using the Title/Alternate title bindings instead of the value binding. Now the button title toggles, but instead of displaying the word "Close" and "Reopen", it displays the boolean values "0" and "1".
I should perhaps handle the button title change in the IBAction as well, using "setTitle", but then I see a new problem coming. On app start-up, how will it pick the appropriate entity record for reference? And what if the table is in a "No Selection" situation? Looks like a quite extensive piece of code to handle such a small issue...
Any advice on a short, more direct way of handling this is welcome. Thanks.
Sounds like you probably have a couple of different options.
This first option is a bit more involved than the others, but is still good to know for the record. This option basically would not use bindings for the close/re-open button, but instead, set the title, etc. programmatically. The basic game plan would be as follows:
The close/re-open button's initial title when the document opens is set in the nib file (e.g. Close).
Optionally, when the document opens, you could disable the button in -awakeFromNib, since the table view will have no initial selection.
The only actions that would necessitate the button's title or enabled state to be changed is 1) when the tableview's selection is changed, and 2) when you've clicked the close/re-open button to toggle the case's state.
To achieve the desired result, you'd create an IBOutlet to the close/re-open button if you don't already have one. You'd then connect up the tableview's delegate outlet to your controller class, which will let your controller class know when the tableview's selection has been changed (via the - (void)tableViewSelectionDidChange:(NSNotification *)notification <NSTableViewDelegate> protocol method). You would also need to update the close/reopen IBAction method to make sure it switches the title on the button after being clicked (since the tableview selection wouldn't change during that operation). The controller class's code might look something like this:
// add this declaration to avoid compiler warnings:
#interface LHController (LHPrivate)
- (void)updateCloseReopenButtonTitle;
#end
- (void)awakeFromNib {
[self updateCloseReopenButtonTitle];
}
- (void)tableViewSelectionDidChange:(NSNotification *)notification {
[self updateCloseReopenButtonTitle];
}
- (IBAction)toggleCloseReopenButton:(id)sender {
// do your existing code here and then add:
[self updateCloseReopenButtonTitle];
}
- (void)updateCloseReopenButtonTitle {
// assuming 'casesController' is an IBOutlet to your NSArrayController
NSArray *selectedObjects = [casesController selectedObjects];
// loop through the selected cases to determine whether
// they're all closed, all open, or mixed to set
// the title of the button appropriately
BOOL allClosed = YES;
for (LHCase *case in selectedCases) {
if ([case isOpen]) {
allClosed = NO;
break;
}
}
[closeReopenButton setTitle:(allClosed ? #"Reopen" : #"Close")];
[closeReopenButton setEnabled:[selectedObjects count] > 0];
}
Before bindings came along, this is how we used to have to do things.
Another option might be reconsidering your user interface: maybe rather than a push/toggle button whose title you need to toggle, you could instead just have a checkbox titled Closed, which would signify whether the selected cases were closed or not. You could use bindings for the checkbox's state, like shown in the image below:
You could then have an IBAction method that handles the extra stuff that needs processing. You can ask the casesController for the -selectedObjects and then loop through them. As long as you make sure the checkbox Allows Mixed states, it has the added advantage of better representing mixed-case scenarios, in case of a selection of cases with mixed open/closed states (the checkbox a dash instead of a full check).
Another option if you want to stick with a toggle button is to create and specify a custom NSValueTransformer for the title and alternate title bindings. This value transformer would take in the boolean closed/open state of the case and turn it into a string more fitting than just 0 or 1 (which is what's being displayed now). It might look something like this:
+ (Class)transformedValueClass {
return [NSString class];
}
+ (BOOL)allowsReverseTransformation {
return NO;
}
- (id)transformedValue:(id)value {
BOOL isClosed;
if (value == nil) return nil;
// Attempt to get a reasonable value from the
// value object.
if ([value respondsToSelector: #selector(boolValue)]) {
isClosed = [value boolValue];
} else {
[NSException raise: NSInternalInconsistencyException
format: #"Value (%#) does not respond to -boolValue.",
[value class]];
}
return (isClosed ? #"Reopen" : #"Close");
}