Reputation: 6947
I have an NSTextField
that I'm setting editable depending on a user action. I'd like to end editing when the user clicks anywhere outside of the text field inside the window.
Seems simple, but I could not get this to work. I implemented controlTextDidEndEditing
and textDidEndEditing
, but no luck, especially when I click on a user interface element that does not accept the first responder status.
Upvotes: 5
Views: 2741
Reputation: 313
I'd improve the answer of vignesh kumar for the cases when you can't subclass the window that contains the view.
For all sub-views/controls that handle mouseDown, including the super view itself, implement:
- (void)mouseDown:(NSEvent *)event
{
[[self window] makeFirstResponder:self];
[super mouseDown:event];
}
For some controls, like buttons, you could change to
- (void)mouseDown:(NSEvent *)event
{
[[self window] makeFirstResponder:[self superview]];
[super mouseDown:event];
}
otherwise a focus ring may appear
Upvotes: 1
Reputation: 55
Every NSEvent is pass through NSWindow's sendEvent:
method.
You can create a custom NSWindow and override the sendEvent:
method. If there is a mouse down event, broadcast it by the NSNotificationCenter:
- (void)sendEvent:(NSEvent *)event {
[super sendEvent:event];
if (event.type == NSLeftMouseDown) {
[[NSNotificationCenter defaultCenter] postNotificationName:kCustomWindowMouseDown object:self userInfo:@{@"event": event}];
}
}
In the ViewController which reference the NSTextField, observer this notification:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(customWindowMouseDown:)
name:kCustomWindowMouseDown
object:self.view.window];
End the editing if the mouse down event's location is outside of the text field:
- (void)customWindowMouseDown:(id)sender {
NSNotification *notification = (NSNotification *) sender;
NSEvent *event = notification.userInfo[@"event"];
NSPoint locationInWindow = event.locationInWindow;
if ([self.view.window.firstResponder isKindOfClass:NSTextView.class]) {
NSTextView *firstResponder = (NSTextView *) self.view.window.firstResponder;
//we only care about the text field referenced by current ViewController
if (firstResponder.delegate == (id <NSTextViewDelegate>) self.textField) {
NSRect rect = [self.textField convertRect:self.textField.bounds toView:nil];
//end editing if click out side
if (!NSPointInRect(locationInWindow, rect)) {
[self.view.window makeFirstResponder:nil];
}
}
}
}
Upvotes: 2
Reputation: 6032
May be a bit dirty but you could create a big transparent button on the "outside of the text field" area. Show it when editing starts and hide it when editing ends. If user taps this button you stop editing (and hide the button).
Solved that for me when I needed a fast solution.
Upvotes: 0
Reputation: 2330
You can write a subclass for the NSView and write the below method and change the class of the NSView in the NSWindow of the nib file to that subclass.
- (void)mouseDown:(NSEvent *)event
{
[text setEditable:NO];
NSLog(@"mouseDown");
}
Upvotes: 0