Mark
Mark

Reputation: 6947

NSTextField: end editing when user clicks outside of the text field

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

Answers (4)

rkudinov
rkudinov

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

ZekeZhang
ZekeZhang

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

MANIAK_dobrii
MANIAK_dobrii

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

vignesh kumar
vignesh kumar

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

Related Questions