In my view controller I'm subscribed to UIKeyboardWillShowNotification:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:nil];
so in my keyboardWillShow: method I'm able to retrieve some info about the keyboard from the notification. But what I want is to get the reference to the actual text field which brings the keyboard up. I couldn't find how to do that in google and I suspect it might be impossible, but someone might know. If it is indeed impossible then I would like to know if there's a possibility to make it reverse way - to get the info about the keyboard by having the reference to the text field. Thanks
Let me emphasize that #iphonic comment is the right way to go.
You should use UITextField delegate function textFieldShouldBeginEditing:
Anything short of that is a kludge: UIKeyboardWillShowNotification assumes the software keyboard will show up, with is a very dangerous assumption and is likely to fail in all sorts of situations, starting with but not limited to Bluetooth keyboards. Try cmd-K in Simulator.
Here is aforementioned kludge, inspired by Get the current first responder without using a private API
func keyboardWillShow() {
let firstResponder = self.findFirstResponder(inView: self.view)
println("keyboardWillShow for \(firstResponder)")
}
func findFirstResponder(inView view: UIView) -> UIView? {
for subView in view.subviews as! [UIView] {
if subView.isFirstResponder() {
return subView
}
if let recursiveSubView = self.findFirstResponder(inView: subView) {
return recursiveSubView
}
}
return nil
}
There is one manual way
if ([firstName isFirstResponder]) {
// caused due to firstName
} else if ([lastName isFirstResponder]) {
// caused due to lastName
}
Swift
if firstName.isFirstResponder { // caused due to firstName }
else
if lastName.isFirstResponder { // caused due to lastName }
The easiest (and almost only) way to do this happens to also be the best way (as #iphonic mentioned)
Implement the UITextFieldDelegate delegate method textFieldShouldBeginEditing:textField
This method will be called before any other event (including any keyboard notifications)
Store the UITextField that received this delegate call to be referenced when determining which field triggered this event.
What I also recommend doing is, if you are looking to determine which keyboard fired the current keyboard appearance, within this delegate method simply register for receiving keyboard notifications.
- (BOOL)textFieldShouldBeginEditing:(UITextView *)textView {
[NSNotificationCenter.defaultCenter addObserver:self selector:#selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
return YES;
}
This way, you can be certain that when keyboardWillShow: is called, it was in fact triggered from this specific text field.
Of course, to make sure you keep track of your text fields correctly, stop listening when the text field stops editing.
- (void)textFieldDidEndEditing:(UITextView *)textView {
[NSNotificationCenter.defaultCenter removeObserver:self name:UIKeyboardWillShowNotification object:nil];
}
There is much better way to do this via did begin editing notifications of UITextField and UITextView:
UITextFieldTextDidBeginEditingNotification
and
UITextViewTextDidBeginEditingNotification
- (void)startListeningForKeyboardNotification {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(responderDidBeginEditing:) name:UITextFieldTextDidBeginEditingNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(responderDidBeginEditing:) name:UITextViewTextDidBeginEditingNotification object:nil];
}
- (void)responderDidBeginEditing:(NSNotification *)notification {
if ([notification.object isKindOfClass:[UITextField class]]) {
UITextField *textField = notification.object;
// Do something with text field
} else if ([notification.object isKindOfClass:[UITextView class]]) {
UITextView *textView = notification.object;
// Do something with text view
}
}
Related
When I click on textfield, execute this method keyboardDidShow and when I started to typing, the view will be go down. What is the reason for this ? Here is my code,
else if (textField.tag==2) {
[self.viewUi setFrame:CGRectMake(0,-110,320,460)];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardDidShow:) name:UIKeyboardDidShowNotification object:nil];
}
return YES;
}
- (void)keyboardDidShow:(NSNotification *)notification
{
// Assign new frame to your view
[self.viewUi setFrame:CGRectMake(0,-110,320,460)];
}
Alwin, this particular post might help you in getting this done
How to make a UITextField move up when keyboard is present?
Or trying adding the NSNotification observer at the time of loading of View Controller as in viewDidLoad.
- (void)keyboardDidShow:(NSNotification *)notification
{
// Assign new frame to your view
[self.viewUi setFrame:CGRectMake(0,-110,320,460)];
}
When i touch a label UIMenuController appears with my custom list of items, and that works well. But when UIMenuController appear in a standard UISearchBar i see all my custom items there too. Why?
I need to show only standard (copy, paste) items for standard UISearchBar and custom items when i touch the label. Can you explain how should i do that?
UPDATE
What i did (not good solution):
If we have keyboard, that is searchbar, if we don't, that is label.
Flag, which mean which items list i will use
BOOL standardList;
Register for keyboard appear/disappear
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
}
-(void)viewWillDisappear:(BOOL)animated
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
[super viewWillDisappear:animated];
}
-(void)keyboardWillShow:(id)sender
{
standardList = YES;
}
-(void)keyboardWillHide:(id)sender
{
standardList = NO;
}
And check flag, in next method:
- (BOOL) canPerformAction:(SEL)selector withSender:(id) sender
{
if (selector == #selector(copy:))
{
return YES;
}
if (!standardList)
{
if ((selector == #selector(makeCall:)) || (selector == #selector(createNewContact:)))
{
return YES;
}
}
return NO;
}
That work well, BUT: iPad keyboard have "hide keyboard" button, and keyboard can hide without [UISearchBar resingFirstResponder].
Even if i add next lines:
-(void)keyboardWillHide:(id)sender
{
standardList = NO;
[mySearchController setActive:NO];
}
that's still bad solution - i can't hide keyboard while searching and scroll search results.
Any suggestions?
One solution:
u can always reset the UIMenuItems after u customize UIMenuController.
addObserver of UIMenuControllerWillHideMenuNotification.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(willHideEditMenu:) name:UIMenuControllerWillHideMenuNotification object:nil];
then in the willHideEditMenu: function
-(void)willHideEditMenu:(NSNotification *)object
{
//any other thing u want to do.
[[UIMenuController shareMenuController] setMenuItems:nil];
}
this way, UIMenuController singleton in other places won't get affected by what u have done where u customize it.
Other solution:
i think the root reason why u have this problem is u have some function with the same name of "makeCall:" or "createNewContact:" in other view or viewcontroller (basically may in any UIResponder) that is the parent view (viewcontroler) of the place where u have this problem.
so check out the responder tree, see if u can find any selectors with the same name of the UIMenuItem selectors. since
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender;
is a function of any UIResponder, it could get called. and if it does happen to have a selector with the same name, it could return YES, and u will have this problem. To solve the problem, just rename the selector of the UIMenuItem.
My English isn't good. Hope u get my point and try it.
I have a project that contains a UIScrollView and many UITextField inside it.
For the first time I select a UITextField, UIKeyboardWillShowNotification is called, which is fine. But whenever I select new UITextField (THE KEYBOARD IS STILL THERE), UIKeyboardWillShowNotification is called again !!!, which is weird.
I also set a symbolic breakpoint for [UIResponder resignFirstResponder] and I see that it is hit before and after UIKeyboardWillShowNotification is called !!!
The other thing is that UIKeyboardWillHideNotification is only called when I hit the "Done" button on the keyboard
I'm sure to not call any resignFirstResponder, becomeFirstResponder, endEditing anywhere. (I mean not call wrongly)
What can cause this problem ?
Here is the stacktrace
To workaround the problem, I used the following code to cancel the UIKeyboardWillShowNotification callback if the keyboard's frame is not changing.
func keyboardWillShow(notification: NSNotification) {
let beginFrame = notification.userInfo![UIKeyboardFrameBeginUserInfoKey]!.CGRectValue()
let endFrame = notification.userInfo![UIKeyboardFrameEndUserInfoKey]!.CGRectValue()
// Return early if the keyboard's frame isn't changing.
guard CGRectEqualToRect(beginFrame, endFrame) == false else {
return
}
...
}
For Swift 3/4:
func keyboardWillShow(notification: Notification) {
let userInfo = notification.userInfo!
let beginFrameValue = (userInfo[UIKeyboardFrameBeginUserInfoKey] as? NSValue)!
let beginFrame = beginFrameValue.cgRectValue
let endFrameValue = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)!
let endFrame = endFrameValue.cgRectValue
if beginFrame.equalTo(endFrame) {
return
}
// Do something with 'will show' event
...
}
The problem is I set inputAccessoryView for the UITextField, and this cause UIKeyboardWillShowNotification being called again when new UITextField is selected
This article Working With Keyboard on iOS explains this well
Additional changes take place when we connect an external keyboard to
the iPad. In this particular case, the notification behavior depends
on the inputAccessoryView property of the control which was the reason
for displaying the keyboard.
If inputAccessoryView is not present or its height is equal to 0
points, no keyboard notifications are sent. My guess is that this is
because in this case, no visual changes take place in application.
Otherwise, all notifications behave as expected – which means they are
being sent as in the majority of cases when the keyboard is displayed
or hidden in a normal (not undocked or split) state.
Whenever new UITextField is selected, the OS needs to compute the frame for the keyboard again, and the following notifications are posted
UIKeyboardWillChangeFrameNotification
UIKeyboardWillShowNotification
UIKeyboardDidChangeFrameNotification
UIKeyboardDidShowNotification
The same applies for when the TextField loses its first responder status
Note that using the same View for inputAccessoryView will cause UIKeyboardWillShowNotification only called once
In general I find that many things can cause spurious UIKeyboardWillShow and UIKeyboardWillHide notifications. My solution is to use a property to track whether the keyboard is already showing:
func keyboardShow(_ n:Notification) {
if self.keyboardShowing {
return
}
self.keyboardShowing = true
// ... other stuff
}
func keyboardHide(_ n:Notification) {
if !self.keyboardShowing {
return
}
self.keyboardShowing = false
// ... other stuff
}
Those guards block exactly the spurious notifications, and all is well after that. And the keyboardShowing property can be useful for other reasons, so that it is something worth tracking anyway.
The best approach is to Add notification & remove it once your purpose is solve.
like this .
- (void)viewWillAppear:(BOOL)animated
{
// register for keyboard notifications
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillShow)
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillHide)
name:UIKeyboardWillHideNotification
object:nil];
}
Now write your code for movement of views & textField in keyboardWillShow & revert them back to position in keyboardWillHide methods.
Also remove the observers
- (void)viewWillDisappear:(BOOL)animated
{
// unregister for keyboard notifications while not visible.
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIKeyboardWillHideNotification
object:nil];
}
You can also resign the responder when you press return key.
-(BOOL)textFieldShouldReturn:(UITextField *)textField {
[_txtFieldEmail resignFirstResponder];
[_txtFieldPassword resignFirstResponder];
return YES;
}
That should solve your issue.
For those not using inputAccessoryView but are still having problems, it may be due to using sensitive (password) fields. See this Stack Overflow post and answer: keyboardWillShow in IOS8 with UIKeyboardWillShowNotification
I have struggled with this, after half a day of searching around and experimenting I think this is the shortest and most reliable code. It is a mix of a number of answers most of which I forget where I found (parts of which are mentioned here).
My problem was a WKWebView which (when the user changed fields) would spawn a load of WillShow, WillHide, etc notifications. Plus I had problem with the external keyboard which still has the onscreen touch bar thing.
This solution uses the same animation code to "Open" and "Close" the keyboard, it will also cope with external keyboard being attached and custom keyboard views.
First register for the UIKeyboardWillChangeFrameNotification.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil];
Then you simply need to map the changes to your view in however you do it (change a hight or bottom constraint constant).
- (void)keyboardWillChangeFrame:(NSNotification *)notification
{
NSDictionary *userInfo = notification.userInfo;
CGRect keyboardEnd = [userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGRect convertedEnd = [self.view convertRect:keyboardEnd fromView:nil];
// Convert the Keyboard Animation to an Option, note the << 16 in the option
UIViewAnimationCurve keyAnimation = [userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue];
// Change the Height or Y Contraint to the new value.
self.keyboardHeightConstraint.constant = self.view.bounds.size.height - convertedEnd.origin.y;
[UIView animateWithDuration:[userInfo[UIKeyboardAnimationDurationUserInfoKey] floatValue]
delay:0.0
options:keyAnimation << 16
animations:^{
[self.view layoutIfNeeded];
} completion:nil];
}
The Animation to Option conversion seems to work (I can only find examples of it being used and not how/why), however, I am not convinced it will stay that way so it may be wise to use a "stock" option. It seems that the Keyboard uses some none specified Animation.
So I've set up a notification for the keyboard appearance event. Now let us consider a UITextView and a UITextField.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:nil];
The selector is:
- (void)keyboardWillShow:(NSNotification *)notification {
keyboardSize = [[[notification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
}
In case of a UITextView, the delegate method - (void)textViewDidBeginEditing:(UITextView *)textView is fired AFTER the keyboardWillShow: method. So keyboardSize has the keyboard's actual size and I'm able to use that inside the textview delegate method.
However in case of a UITextField, the corresponding delegate method - (void)textFieldDidBeginEditing:(UITextField *)textField is fired BEFORE the keyboardWillShow: method.
Why is this so? How do I get the keyboard's CGSize in the textfield's case as now it just returns zero because the textfield delegate is called first and not the keyboard selector.
I've had this same problem. Try using:
- (BOOL)textViewShouldBeginEditing:(UITextView *)textView
Weird… Sounds like a mistake on Apple's end.
Maybe you could delay the keyboard popping up? Here's my unfortunately very messy "work around" suggestion -- You could send a notification when the text field is selected, but then only actually begin editing a fraction of a second later so that the text field is in fact known before keyboardWillShow: is called. For example:
-(BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
// Notification corresponding to "textFieldSelected:" method
[[NSNotificationCenter defaultCenter] postNotificationName:NOTIFICATION_TEXT_FIELD_SELECTED object:nil userInfo:[[NSDictionary alloc] initWithObjectsAndKeys:textField, #"textField", nil]];
// "textFieldReallyShouldBeginEditing" is initially set as FALSE elsewhere in the code before the text field is manually selected
if (textFieldReallyShouldBeginEditing)
return YES;
else
return NO:
}
- (void)textFieldSelected:(NSNotification*)notification {
// Done in a separate method so there's a guaranteed delay and "textFieldReallyShouldBeginEditing" isn't set to YES before "textFieldShouldBeginEditing:" returns its boolean.
[self performSelector:#selector(startTextFieldReallyEditing:) withObject:(UITextField*)notification[#"textField"] afterDelay:.01];
}
- (void)startTextFieldReallyEditing:(UITextField*)textField {
textFieldReallyShouldBeginEditing = YES;
// To trigger the keyboard
[textField becomeFirstResponder];
}
Then depending on how you're creating the notification, you can insert the value of this now known text field even before it begins editing.
UITextField shouldChangeCharactersInRange delegate method is not called when a word is selected from autocorrection bar with simplified Chinese keyboard. When I type 'ni hao', this delegate method gets called for each character, but when I selected a word from autocorrect suggestion bar this delegate method is not called. This is only happening in iOS 7 with simplified chinese, it is working fine with japanese keyboard. I want to do my application specific action when selecting the word. Any help is much appreciated.
I meet the same issue today. I fixed it as below:
Don't use delegate, just use Notification:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(textChanged:)
name:UITextFieldTextDidChangeNotification
object:nil];
I encountered this issue, too. Thanks to #AnkyHe 's solution. Here is my implemention.
In the ViewController which hold your UITextField:
- (void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(textChanged:)
name:UITextFieldTextDidChangeNotification
object:nil];
}
- (void)textChanged:(NSNotification *)notif
{
UITextField *editingTextField = (UITextField *)notif.object;
NsLog(#"changed textL %#", editingTextField.text);
}