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

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.

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];
}
}
}
}

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");
}

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.

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

Related

How to restrict the location of an NSWindow when dragging from a non key state

I am trying to restrict the movement of an NSWindow to the top of my screen. I have implemented mouse and window event handlers for click and drag. It does work when the window has focus and is key but if I click on another application the click drag on my window I can move it anywhere on the screen and it will flicker between where I am dragging and the top of the screen.
I would really like to capture mouse movement events for the window when it doesn't have focus.
Here is a summary of my project:
#interface CustomWindow : NSWindow <NSWindowDelegate>
- (id)init
[self setDeligate:self];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(gainedFocus:) name:NSWindowDidBecomeKeyNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(lostFocus:) name:NSWindowDidResignKeyNotification object:nil];
-(void) gainedFocus:(NSNotification*)notification
{
hasFocus = true;
[self setMovableByWindowBackground:NO];
}
-(void) lostFocus:(NSNotification*)notification
{
hasFocus = false;
[self setMovableByWindowBackground:YES];
}
- (void)mouseDragged:(NSEvent *)theEvent // Only called if window has focus (is key)
{
[self positionWindow]; // Works only if window started drag with focus (key)
}
- (void)mouseMoved:(NSEvent *)event // Only called if window has focus (is key)
- (void)mouseUp:(NSEvent *)event // Only called if window has focus (is key)
- (void)mouseDown:(NSEvent *)event // Only called if window has focus (is key)
// Deligate functions
- (void)windowWillMove:(NSNotification *)notification // Is called when not key
- (void)windowDidMove:(NSNotification *)notification // Is called when not key but only when the movement stops and the window flickers between where i’m dragging and where the code moves it to.
{
[self positionWindow];
}
I can a function that moves the window to a desired region with:
-(void) positionWindow {
[self setFrameOrigin: ...];
}
Thanks

How to draw custom window controls (close, minimize, and zoom buttons)

I've made an attempt to draw custom NSButtons, but it seems I'm reinventing the wheel here. Is there a way to just replace the default images used for the close, minimize and zoom buttons?
Several apps already do it:
OSX 10.8's Reminders app (they appear dark grey when the window is not key, vs most appear light grey)
Tweetbot (All buttons look totally custom)
More info:
I can generate the system defaults as such standardWindowButton:NSWindowCloseButton. But from there the setImage setter doesn't change the appearance of the buttons.
Edit: Since I wrote this, INAppStore has implemented a pretty nice way to do this with INWindowButton. If you're looking for a drag and drop solution check there, but the code below will still help you implement your own.
So I couldn't find a way to alter the standardWindowButtons. Here is a walkthrough of how I created my own buttons.
Note: There are 4 states the buttons can be in
Window inactive
Window active - normal
Window active - hover
Window active - press
On to the walkthrough!
Step 1: Hide the pre-existing buttons
NSButton *windowButton = [self standardWindowButton:NSWindowCloseButton];
[windowButton setHidden:YES];
windowButton = [self standardWindowButton:NSWindowMiniaturizeButton];
[windowButton setHidden:YES];
windowButton = [self standardWindowButton:NSWindowZoomButton];
[windowButton setHidden:YES];
Step 2: Setup the view in Interface Builder
You'll notice on hover the buttons all change to their hover state, so we need a container view to pick up the hover.
Create a container view to be 54px wide x 16px tall.
Create 3 Square style NSButtons, each 14px wide x 16px tall inside the container view.
Space out the buttons so there is are 6px gaps in-between.
Setup the buttons
In the attributes inspector, set the Image property for each button to the window-active-normal image.
Set the Alternate image property to the window-active-press image.
Turn Bordered off.
Set the Type to Momentary Change.
For each button set the identifier to close,minimize or zoom (Below you'll see how you can use this to make the NSButton subclass simpler)
Step 3: Subclass the container view & buttons
Container:
Create a new file, subclass NSView. Here we are going to use Notification Center to tell the buttons when they should switch to their hover state.
HMTrafficLightButtonsContainer.m
// Tells the view to pick up the hover event
- (void)viewDidMoveToWindow {
[self addTrackingRect:[self bounds]
owner:self
userData:nil
assumeInside:NO];
}
// When the mouse enters/exits we send out these notifications
- (void)mouseEntered:(NSEvent *)theEvent {
[[NSNotificationCenter defaultCenter] postNotificationName:#"HMTrafficButtonMouseEnter" object:self];
}
- (void)mouseExited:(NSEvent *)theEvent {
[[NSNotificationCenter defaultCenter] postNotificationName:#"HMTrafficButtonMouseExit" object:self];
}
Buttons:
Create a new file, this time subclass NSButton. This one's a bit more to explain so I'll just post all the code.
HMTrafficLightButton.m
#implementation HMTrafficLightButton {
NSImage *inactive;
NSImage *active;
NSImage *hover;
NSImage *press;
BOOL activeState;
BOOL hoverState;
BOOL pressedState;
}
-(id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
[self setup];
}
return self;
}
- (id)initWithFrame:(NSRect)frameRect {
self = [super initWithFrame:frameRect];
if (self) {
[self setup];
}
return self;
}
- (void)setup {
// Setup images, we use the identifier to chose which image to load
active = [NSImage imageNamed:[NSString stringWithFormat:#"window-button-%#-active",self.identifier]];
hover = [NSImage imageNamed:[NSString stringWithFormat:#"window-button-%#-hover",self.identifier]];
press = [NSImage imageNamed:[NSString stringWithFormat:#"window-button-%#-press",self.identifier]];
inactive = [NSImage imageNamed:#"window-button-all-inactive"];
// Checks to see if window is active or inactive when the `init` is called
if ([self.window isMainWindow] && [[NSApplication sharedApplication] isActive]) {
[self setActiveState];
} else {
[self setInactiveState];
}
// Watch for hover notifications from the container view
// Also watches for notifications for when the window
// becomes/resigns main
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(setActiveState)
name:NSWindowDidBecomeMainNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(setInactiveState)
name:NSWindowDidResignMainNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(hoverIn)
name:#"HMTrafficButtonMouseEnter"
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(hoverOut)
name:#"HMTrafficButtonMouseExit"
object:nil];
}
- (void)mouseDown:(NSEvent *)theEvent {
pressedState = YES;
hoverState = NO;
[super mouseDown:theEvent];
}
- (void)mouseUp:(NSEvent *)theEvent {
pressedState = NO;
hoverState = YES;
[super mouseUp:theEvent];
}
- (void)setActiveState {
activeState = YES;
if (hoverState) {
[self setImage:hover];
} else {
[self setImage:active];
}
}
- (void)setInactiveState {
activeState = NO;
[self setImage:inactive];
}
- (void)hoverIn {
hoverState = YES;
[self setImage:hover];
}
- (void)hoverOut {
hoverState = NO;
if (activeState) {
[self setImage:active];
} else {
[self setImage:inactive];
}
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
#end
In IB set the Custom Class of the container view and all 3 buttons to their respective classes that we just created.
Step 4: Set the button actions
These methods, called from the view controller, are the same as the standardWindowButtons'. Link them to the buttons in IB.
- (IBAction)clickCloseButton:(id)sender {
[self.view.window close];
}
- (IBAction)clickMinimizeButton:(id)sender {
[self.view.window miniaturize:sender];
}
- (IBAction)clickZoomButton:(id)sender {
[self.view.window zoom:sender];
}
Step 5: Add the view to the window
I have a separate xib and view controller setup specifically for the window controls. The view controller is called HMWindowControlsController
(HMWindowControlsController*) windowControlsController = [[HMWindowControlsController alloc] initWithNibName:#"WindowControls" bundle:nil];
NSView *windowControlsView = windowControlsController.view;
// Set the position of the window controls, the x is 7 px, the y will
// depend on your titlebar height.
windowControlsView.frame = NSMakeRect(7.0, 10.0, 54.0, 16.0);
// Add to target view
[targetView addSubview:windowControlsView];
Hope this helps. This is a pretty lengthy post, if you think I've made a mistake or left something out please let me know.

Hide UISearchBar Cancel Button

I have a UISearchDisplayController and UISearchBar hooked up to my ViewController via Outlets from my nib.
I'd like to hide the cancel button so that the user never sees it. The problem is that the following code hides the button, but only after displaying it to the user for a millisecond (e.g., it flashes on the simulator and device and then disappears out of view).
- (void)searchDisplayControllerDidBeginSearch:(UISearchDisplayController *)controller
{
controller.searchBar.showsCancelButton = NO;
}
Is there a better way to hide it?
I managed to hide the "Cancel" button by subclassing UISearchBar and override this method:
-(void)layoutSubviews{
[super layoutSubviews];
[self setShowsCancelButton:NO animated:NO];
}
I had the same issue, but fixed it a different way.
For those who can't or don't want to subclass UISearchDisplayController, I fixed the issue by adding a listener on UIKeyboardWillShowNotification, and setting [self setShowsCancelButton:NO animated:NO] there.
In viewWillAppear::
// Add keyboard observer:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillAppear:)
name:UIKeyboardWillShowNotification
object:nil];
Then you create:
- (void)keyboardWillAppear:(NSNotification *)notification
{
[YOUR-SEARCHBAR-HERE setShowsCancelButton:NO animated:NO];
}
Don't forget to add,
[[NSNotificationCenter defaultCenter] removeObserver:self];
in viewWillDisappear:!
Hope this helps!
Similar to Nimrod's answer, you can also subclass UISearchDisplayController and implement the setActive:animated: method:
- (void)setActive:(BOOL)visible animated:(BOOL)animated {
[super setActive:visible animated:animated];
self.searchBar.showsCancelButton = NO;
}
This seems to be a bug within Xcode. I submitted this error to Apple's bug reporting site, and they've followed up asking for more sample code and use-cases.
Thanks everyone for your attempt at solving this problem.
class CustomSearchBar: UISearchBar {
override func setShowsCancelButton(showsCancelButton: Bool, animated: Bool) {
super.setShowsCancelButton(false, animated: false)
}
}
class CustomSearchController: UISearchController, UISearchBarDelegate {
lazy var _searchBar: CustomSearchBar = {
[unowned self] in
let customSearchBar = CustomSearchBar(frame: CGRectZero)
customSearchBar.delegate = self
return customSearchBar
}()
override var searchBar: UISearchBar {
get {
return _searchBar
}
}
}
Had this problem when using the UISearchBar with UISearchController. I'm using my own cancel button, as the cancel button wasn't showing on iPad with showsCancelButton = YES, now it won't hide on iPhone with showsCancelButton = NO!
The following worked for me.
Set the delegate, and initial value:
- (void)viewDidLoad
{
// ...
self.searchController.searchBar.showsCancelButton = NO;
self.searchController.searchBar.delegate = self;
}
Reset showsCancelButton to NO 0.1s after the text bar begins editing.
#pragma mark - UISearchBarDelegate
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar
{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
self.searchController.searchBar.showsCancelButton = NO;
});
}
If you want to avoid the subclassing, implement
searchController.searchBar.showsCancelButton = false;
in these two delegate methods (Do not forget to assign delegates):
- (void)updateSearchResultsForSearchController:(UISearchController *)searchController
- (void)didPresentSearchController:(UISearchController *)searchController
The first one is called everytime you update the searchBar (Cancel button is visible by default) and the second one is for the first searchBar activation.
Just based on issues I've had before have you tried setting it in:
- (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller
I don't know how to ask this question in your question sorry if this is out of place.
If the cancel button shows up when editing the search field of the search bar you could do the following; subclass the search bar and have it implement the UITextFieldDelegateprotocol:
#interface CustomAlignedSearchBar : UISearchBar<UITextFieldDelegate>
Then implement textFieldDidBeginEditing: and do something like:
- (void)textFieldDidBeginEditing:(UITextField *)textField{
[self setShowsCancelButton:self.cancelButtonShown animated:NO];
}
This will make sure that the cancel button will not show up.
After UISearchDisplayController deprecated in iOS8, Apple give handle search presentation to UISearchControllerDelegate.
so you can override searchBar to hide the Cancel button, like below :
- (void)didPresentSearchController:(UISearchController *)searchController {
[searchController.searchBar setShowsCancelButton:NO];
}
if you need hidden Cancel button from inactive state, you need set searchBar on init :
search = [[UISearchController alloc] initWithSearchResultsController:nil];
[search.searchBar setShowsCancelButton:NO];
On iOS 13.0 and later, UISearchController has this property you can use:
#property (nonatomic) BOOL automaticallyShowsCancelButton API_AVAILABLE(ios(13.0)); // Default YES

Is there a way to make a custom NSWindow work with Spaces

I'm writing an app that has a custom, transparent NSWindow created using a NSWindow subclass with the following:
- (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)aStyle backing:(NSBackingStoreType)bufferingType defer:(BOOL)flag
{
self = [super initWithContentRect:contentRect styleMask:NSBorderlessWindowMask backing:bufferingType defer:flag];
if (self)
{
[self setOpaque:NO];
[self setBackgroundColor:[NSColor clearColor]];
}
return self;
}
- (BOOL)canBecomeKeyWindow
{
return YES;
}
- (BOOL)canBecomeMainWindow
{
return YES;
}
I have everything working perfectly, including dragging and resizing, except the window doesn't work with Spaces. I cannot move the window to another space by either holding the window while switching spaces via keyboard shortcut, or by dragging to the bottom/top/left/right of the window. Is there anyway to have a custom window behave exactly like a normal window with regards to Spaces?
After a long time I found a solution to this annoying problem.
Indeed [window setMovableByWindowBackground:YES]; conflicts with my own resizing methods, the window trembles, it looks awful!
But overriding mouse event methods like below solved the problem in my case :)
- (void)mouseMoved:(NSEvent *)event
{
//set movableByWindowBackground to YES **ONLY** when the mouse is on the title bar
NSPoint mouseLocation = [event locationInWindow];
if (NSPointInRect(mouseLocation, [titleBar frame])){
[self setMovableByWindowBackground:YES];
}else{
[self setMovableByWindowBackground:NO];
}
//This is a good place to set the appropriate cursor too
}
- (void)mouseDown:(NSEvent *)event
{
//Just in case there was no mouse movement before the click AND
//is inside the title bar frame then setMovableByWindowBackground:YES
NSPoint mouseLocation = [event locationInWindow];
if (NSPointInRect(mouseLocation, [titleBar frame])){
[self setMovableByWindowBackground:YES];
}else if (NSPointInRect(mouseLocation, bottomRightResizingCornerRect)){
[self doBottomRightResize:event];
}//... do all other resizings here. There are 6 more in OSX 10.7!
}
- (void)mouseUp:(NSEvent *)event
{
//movableByBackground must be set to YES **ONLY**
//when the mouse is inside the titlebar.
//Disable it here :)
[self setMovableByWindowBackground:NO];
}
All my resizing methods start in mouseDown:
- (void)doBottomRightResize:(NSEvent *)event {
//This is a good place to push the appropriate cursor
NSRect r = [self frame];
while ([event type] != NSLeftMouseUp) {
event = [self nextEventMatchingMask:(NSLeftMouseDraggedMask | NSLeftMouseUpMask)];
//do a little bit of maths and adjust rect r
[self setFrame:r display:YES];
}
//This is a good place to pop the cursor :)
//Dispatch unused NSLeftMouseUp event object
if ([event type] == NSLeftMouseUp) {
[self mouseUp:event];
}
}
Now I have my Custom window and plays nice with Spaces :)
Two things here.
You need to set the window to allow dragging by background, [window setMovableByWindowBackground:YES];
And If your custom window areas you expect to be draggable are custom NSView subclasses, you must override the method - (BOOL)mouseDownCanMoveWindow to return YES in any NSView subclass that needs to be able to move the window by dragging.
Did you override isMovable?
The Apple documentation says, that it changes Spaces behavior:
If a window returns NO, that means it
can only be dragged between spaces in
F8 mode, ...
Another method that might be related:
NSWindow setCollectionBehavior

Easy way to dismiss keyboard?

I have quite a few controls scattered throughout many table cells in my table, and I was wondering if there's an easier way to dismiss the keyboard without having to loop through all my controls and resigning them all as the first responder. I guess the question is.. How would I get the current first responder to the keyboard?
Try:
[self.view endEditing:YES];
You can force the currently-editing view to resign its first responder status with [view endEditing:YES]. This hides the keyboard.
Unlike -[UIResponder resignFirstResponder], -[UIView endEditing:] will search through subviews to find the current first responder. So you can send it to your top-level view (e.g. self.view in a UIViewController) and it will do the right thing.
(This answer previously included a couple of other solutions, which also worked but were more complicated than is necessary. I've removed them to avoid confusion.)
You can send a nil targeted action to the application, it'll resign first responder at any time without having to worry about which view currently has first responder status.
Objective-C:
[[UIApplication sharedApplication] sendAction:#selector(resignFirstResponder) to:nil from:nil forEvent:nil];
Swift 3.0:
UIApplication.shared.sendAction(#selector(resignFirstResponder), to: nil, from: nil, for: nil)
Nil targeted actions are common on Mac OS X for menu commands, and here's a use for them on iOS.
To be honest, I'm not crazy about any of the solutions proposed here. I did find a nice way to use a TapGestureRecognizer that I think gets to the heart of your problem: When you click on anything besides the keyboard, dismiss the keyboard.
In viewDidLoad, register to receive keyboard notifications and create a UITapGestureRecognizer:
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self selector:#selector(keyboardWillShow:) name:
UIKeyboardWillShowNotification object:nil];
[nc addObserver:self selector:#selector(keyboardWillHide:) name:
UIKeyboardWillHideNotification object:nil];
tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(didTapAnywhere:)];
Add the keyboard show/hide responders. There you add and remove the TapGestureRecognizer to the UIView that should dismiss the keyboard when tapped. Note: You do not have to add it to all of the sub-views or controls.
-(void) keyboardWillShow:(NSNotification *) note {
[self.view addGestureRecognizer:tapRecognizer];
}
-(void) keyboardWillHide:(NSNotification *) note
{
[self.view removeGestureRecognizer:tapRecognizer];
}
The TapGestureRecognizer will call your function when it gets a tap and you can dismiss the keyboard like this:
-(void)didTapAnywhere: (UITapGestureRecognizer*) recognizer {
[textField resignFirstResponder];
}
The nice thing about this solution is that it only filters for Taps, not swipes. So if you have scrolling content above the keyboard, swipes will still scroll and leave the keyboard displayed. By removing the gesture recognizer after the keyboard is gone, future taps on your view get handled normally.
This is a solution to make the keyboard go away when hit return in any textfield, by adding code in one place (so don't have to add a handler for each textfield):
consider this scenario:
i have a viewcontroller with two textfields (username and password).
and the viewcontroller implements UITextFieldDelegate protocol
i do this in viewDidLoad
- (void)viewDidLoad
{
[super viewDidLoad];
username.delegate = self;
password.delegate = self;
}
and the viewcontroller implements the optional method as
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
[textField resignFirstResponder];
return YES;
}
and irrespective of the textfield you are in, as soon as i hit return in the keyboard, it gets dismissed!
In your case, the same would work as long as you set all the textfield's delegate to self and implement textFieldShouldReturn
A better approach is to have something "steal" first responder status.
Since UIApplication is a subclass of UIResponder, you could try:
[[UIApplication sharedApplication] becomeFirstResponder]
[[UIApplication sharedApplication] resignFirstResponder]
Failing that, create a new UITextField with a zero sized frame, add it to a view somewhere and do something similar (become followed by resign).
Tuck this away in some utility class.
+ (void)dismissKeyboard {
[self globalResignFirstResponder];
}
+ (void) globalResignFirstResponder {
UIWindow * window = [[UIApplication sharedApplication] keyWindow];
for (UIView * view in [window subviews]){
[self globalResignFirstResponderRec:view];
}
}
+ (void) globalResignFirstResponderRec:(UIView*) view {
if ([view respondsToSelector:#selector(resignFirstResponder)]){
[view resignFirstResponder];
}
for (UIView * subview in [view subviews]){
[self globalResignFirstResponderRec:subview];
}
}
#Nicholas Riley & #Kendall Helmstetter Geln & #cannyboy:
Absolutely brilliant!
Thank you.
Considering your advice and the advice of others in this thread, this is what I've done:
What it looks like when used:
[[self appDelegate] dismissKeyboard]; (note: I added appDelegate as an addition to NSObject so I can use anywhere on anything)
What it looks like under the hood:
- (void)dismissKeyboard
{
UITextField *tempTextField = [[[UITextField alloc] initWithFrame:CGRectZero] autorelease];
tempTextField.enabled = NO;
[myRootViewController.view addSubview:tempTextField];
[tempTextField becomeFirstResponder];
[tempTextField resignFirstResponder];
[tempTextField removeFromSuperview];
}
EDIT
Amendment to my answer to included tempTextField.enabled = NO;. Disabling the text field will prevent UIKeyboardWillShowNotification and UIKeyboardWillHideNotification keyboard notifications from being sent should you rely on these notifications throughout your app.
Quick tip on how to dismiss the keyboard in iOS when a user touches anywhere on the screen outside of the UITextField or keyboard. Considering how much real estate the iOS keyboard can take up, it makes sense to have an easy and intuitive way for your users to dismiss the keyboard.
Here's a link
A lot of overly-complicated answers here, perhaps because this is not easy to find in the iOS documentation. JosephH had it right above:
[[view window] endEditing:YES];
Here's what I use in my code. It works like a charm!
In yourviewcontroller.h add:
#property (nonatomic) UITapGestureRecognizer *tapRecognizer;
Now in the .m file, add this to your ViewDidLoad function:
- (void)viewDidLoad {
//Keyboard stuff
tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(didTapAnywhere:)];
tapRecognizer.cancelsTouchesInView = NO;
[self.view addGestureRecognizer:tapRecognizer];
}
Also, add this function in the .m file:
- (void)handleSingleTap:(UITapGestureRecognizer *) sender
{
[self.view endEditing:YES];
}
Even Simpler than Meagar's answer
overwrite touchesBegan:withEvent:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[textField resignFirstResponder];`
}
This will dismiss the keyboardwhen you touch anywhere in the background.
You should send endEditing: to working window being the subclass of UIView
[[UIApplication sharedApplication].windows.firstObject endEditing:NO];
In your view controller's header file add <UITextFieldDelegate> to the definition of your controller's interface so that it conform to the UITextField delegate protocol...
#interface someViewController : UIViewController <UITextFieldDelegate>
... In the controller's implementation file (.m) add the following method, or the code inside it if you already have a viewDidLoad method ...
- (void)viewDidLoad
{
// Do any additional setup after loading the view, typically from a nib.
self.yourTextBox.delegate = self;
}
... Then, link yourTextBox to your actual text field
- (BOOL)textFieldShouldReturn:(UITextField *)theTextField
{
if (theTextField == yourTextBox) {
[theTextField resignFirstResponder];
}
return YES;
}
The best way to dismiss keyboard from UITableView and UIScrollView are:
tableView.keyboardDismissMode = UIScrollViewKeyboardDismissModeOnDrag
In swift 3 you can do the following
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
Jeremy's answer wasn't quite working for me, I think because I had a navigation stack in a tab view with a modal dialog on top of it. I'm using the following right now and it is working for me, but your mileage may vary.
// dismiss keyboard (mostly macro)
[[UIApplication sharedApplication].delegate dismissKeyboard]; // call this in your to app dismiss the keybaord
// --- dismiss keyboard (in indexAppDelegate.h) (mostly macro)
- (void)dismissKeyboard;
// --- dismiss keyboard (in indexAppDelegate.m) (mostly macro)
// do this from anywhere to dismiss the keybard
- (void)dismissKeyboard { // from: http://stackoverflow.com/questions/741185/easy-way-to-dismiss-keyboard
UITextField *tempTextField = [[UITextField alloc] initWithFrame:CGRectZero];
UIViewController *myRootViewController = <#viewController#>; // for simple apps (INPUT: viewController is whatever your root controller is called. Probably is a way to determine this progragrammatically)
UIViewController *uivc;
if (myRootViewController.navigationController != nil) { // for when there is a nav stack
uivc = myRootViewController.navigationController;
} else {
uivc = myRootViewController;
}
if (uivc.modalViewController != nil) { // for when there is something modal
uivc = uivc.modalViewController;
}
[uivc.view addSubview:tempTextField];
[tempTextField becomeFirstResponder];
[tempTextField resignFirstResponder];
[tempTextField removeFromSuperview];
[tempTextField release];
}
You may also need to override UIViewController disablesAutomaticKeyboardDismissal to get this to work in some cases. This may have to be done on the UINavigationController if you have one.
Subclass your textfields... and also textviews
In the subclass put this code..
-(void)conformsToKeyboardDismissNotification{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(dismissKeyBoard) name:KEYBOARD_DISMISS object:nil];
}
-(void)deConformsToKeyboardDismissNotification{
[[NSNotificationCenter defaultCenter] removeObserver:self name:KEYBOARD_DISMISS object:nil];
}
- (void)dealloc{
[[NSNotificationCenter defaultCenter] removeObserver:self];
[self resignFirstResponder];
}
In the textfield delegates (similarly for textview delegates)
-(void)textFieldDidBeginEditing:(JCPTextField *)textField{
[textField conformsToKeyboardDismissNotification];
}
- (void)textFieldDidEndEditing:(JCPTextField *)textField{
[textField deConformsToKeyboardDismissNotification];
}
All set.. Now just post the notification from anywhere in your code. It will resign any keyboard.
And in swift we can do
UIApplication.sharedApplication().sendAction("resignFirstResponder", to: nil, from: nil, forEvent: nil)
To dismiss a keyboard after the keyboard has popped up, there are 2 cases,
when the UITextField is inside a UIScrollView
when the UITextField is outside a UIScrollView
2.when the UITextField is outside a UIScrollView
override the method in your UIViewController subclass
you must also add delegate for all UITextView
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self.view endEditing:YES];
}
In a scroll view, Tapping outside will not fire any event, so in that case use a Tap Gesture Recognizer,
Drag and drop a UITapGesture for the scroll view and create an IBAction for it.
to create a IBAction, press ctrl+ click the UITapGesture and drag it to the .h file of viewcontroller.
Here I have named tappedEvent as my action name
- (IBAction)tappedEvent:(id)sender {
[self.view endEditing:YES]; }
the abouve given Information was derived from the following link, please refer for more information or contact me if you dont understand the abouve data.
http://samwize.com/2014/03/27/dismiss-keyboard-when-tap-outside-a-uitextfield-slash-uitextview/
I hate that there's no "global" way to programmatically dismiss the keyboard without using private API calls. Frequently, I have the need to dismiss the keyboard programmatically without knowing what object is the first responder. I've resorted to inspecting the self using the Objective-C runtime API, enumerating through all of its properties, pulling out those which are of type UITextField, and sending them the resignFirstResponder message.
It shouldn't be this hard to do this...
It's not pretty, but the way I resign the firstResponder when I don't know what that the responder is:
Create an UITextField, either in IB or programmatically. Make it Hidden. Link it up to your code if you made it in IB.
Then, when you want to dismiss the keyboard, you switch the responder to the invisible text field, and immediately resign it:
[self.invisibleField becomeFirstResponder];
[self.invisibleField resignFirstResponder];
You can recursively iterate through subviews, store an array of all UITextFields, and then loop through them and resign them all.
Not really a great solution, especially if you have a lot of subviews, but for simple apps it should do the trick.
I solved this in a much more complicated, but much more performant way, but using a singleton/manager for the animation engine of my app, and any time a text field became the responder, I would assign assign it to a static which would get swept up (resigned) based on certain other events... its almost impossible for me to explain in a paragraph.
Be creative, it only took me 10 minutes to think through this for my app after I found this question.
A slightly more robust method I needed to use recently:
- (void) dismissKeyboard {
NSArray *windows = [UIApplication sharedApplication].windows;
for(UIWindow *window in windows) [window endEditing:true];
// Or if you're only working with one UIWindow:
[[UIApplication sharedApplication].keyWindow endEditing:true];
}
I found some of the other "global" methods didn't work (for example, UIWebView & WKWebView refused to resign).
Add A Tap Gesture Recognizer to your view.And define it ibaction
your .m file will be like
- (IBAction)hideKeyboardGesture:(id)sender {
NSArray *windows = [UIApplication sharedApplication].windows;
for(UIWindow *window in windows) [window endEditing:true];
[[UIApplication sharedApplication].keyWindow endEditing:true];
}
It's worked for me
Yes, endEditing is the best option. And From iOW 7.0, UIScrollView has a cool feature to dismiss the keyboard on interacting with the scroll view. For achieving this, you can set keyboardDismissMode property of UIScrollView.
Set the keyboard dismiss mode as:
tableView.keyboardDismissMode = UIScrollViewKeyboardDismissModeOnDrag
It has few other types. Have a look at this apple document.
In swift :
self.view.endEditing(true)
the easist way is to call the method
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
if(![txtfld resignFirstResponder])
{
[txtfld resignFirstResponder];
}
else
{
}
[super touchesBegan:touches withEvent:event];
}
You have to use one of these methods,
[self.view endEditing:YES];
or
[self.textField resignFirstResponder];