Slide view upon TextField selection is only on typing - iphone

I have used the demonstration code Apple has in their docs here:
http://developer.apple.com/library/ios/#documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/KeyboardManagement/KeyboardManagement.html#//apple_ref/doc/uid/TP40009542-CH5-SW7
I need to slide the view up when the keyboard appears from tapping a UITextField. I am using the code in the link above, Apple's own demo code for ding this and it makes sense.
However, my issue, using Apple's code virtually unchanged, the view only slides up when typing, vs actually after tapping on the text field.
In the - (void)keyboardWasShown:(NSNotification*)aNotification method, it is when this if statement is called that the view is slid up or not. Slid up if the textfield is below the keyboard, not slid up if it isn't below the keyboard.
if (!CGRectContainsPoint(aRect, activeField.frame.origin) ) {
CGPoint scrollPoint = CGPointMake(0.0, activeField.frame.origin.y-kbSize.height);
[scrollView setContentOffset:scrollPoint animated:YES];
}
Trouble is, upon tapping the text field, this if statement is skipped, the code inside it doesn't run.
I can't work it out, but it all happens in this method below. Why is it only sliding up on typing and not the initial tap?
- (void)keyboardWasShown:(NSNotification*)aNotification
{
NSDictionary* info = [aNotification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0);
scrollView.contentInset = contentInsets;
scrollView.scrollIndicatorInsets = contentInsets;
// If active text field is hidden by keyboard, scroll it so it's visible
// Your application might not need or want this behavior.
CGRect aRect = self.view.frame;
aRect.size.height -= kbSize.height;
if (!CGRectContainsPoint(aRect, activeField.frame.origin) ) {
CGPoint scrollPoint = CGPointMake(0.0, activeField.frame.origin.y-kbSize.height);
[scrollView setContentOffset:scrollPoint animated:YES];
}
}

I looked again at the Apple doc. Yuck - don't they write software for a living?? Try this instead: Remember the default position of the main view.
#interface ViewController ()
#property (assign, nonatomic) CGFloat viewOriginY;
#end
Register for keyboard notification and a tap (to dismiss first responder).
- (void)viewDidLoad
{
[super viewDidLoad];
self.viewOriginY = self.view.frame.origin.y;
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserver:self selector:#selector(keyboardDidShow:) name:UIKeyboardDidShowNotification object:nil];
[center addObserver:self selector:#selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
// add a tap gesture to drop first responder
UITapGestureRecognizer *tapGR = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapped:)];
[self.view addGestureRecognizer:tapGR];
}
Write a function to (recursively) find the first responder.
- (UIView *)firstResponderWithin:(UIView *)view {
if ([view isFirstResponder]) return view;
for (UIView *subview in view.subviews) {
UIView *answer = [self firstResponderWithin:subview];
if (answer) return answer;
}
return nil;
}
Here's the punchline: When the keyboard is shown, compute it's frame and the frame of the view that needs to be visible. Important to do this in a common coordinate system, like this vc's view. Then slide the view to make the first responder visible.
- (void)keyboardDidShow:(NSNotification *)notification {
CGRect keyboardFrameW = [[notification.userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
UIWindow *window = [[UIApplication sharedApplication] keyWindow];
CGRect keyboardFrame = [window convertRect:keyboardFrameW toView:self.view];
UIView *firstResponder = [self firstResponderWithin:self.view];
CGRect firstResponderFrame = [firstResponder.superview convertRect:firstResponder.frame toView:self.view];
// let's put the bottom of the first responder's frame just above the top of the keyboard
CGFloat firstResponderBottom = CGRectGetMaxY(firstResponderFrame);
CGFloat targetBottom = keyboardFrame.origin.y - 8.0;
CGFloat offsetY = MAX(0.0, firstResponderBottom - targetBottom);
[UIView animateWithDuration:0.3 animations:^{
self.view.frame = CGRectOffset(self.view.frame, 0.0, -offsetY);
}];
}
- (void)keyboardWillHide:(NSNotification *)notification {
[UIView animateWithDuration:0.3 animations:^{
self.view.frame = CGRectMake(0.0, self.viewOriginY, self.view.frame.size.width, self.view.frame.size.height);
}];
}
Optional - When user taps anyplace else, dismiss the keyboard.
- (void)tapped:(UITapGestureRecognizer *)gr {
UIView *firstResponder = [self firstResponderWithin:self.view];
[firstResponder resignFirstResponder];
}
Working project can be found here.
I think this idea is one ivar away from a nice category on view controller. You could import it, and invoke the setup on viewDidLoad. All it needs is a way to get the main view's default y origin without writing it in an ivar. I need to noodle on that.

Related

Why does the view scroll up when keyboard is present for a textview but not for a textfield?

I have a textfield and a textview in a scroll view. When I begin to edit the textview the view scrolls up appropriately so the keyboard does not hide the text. The textfield, however, does not scroll up but is hidden by the keyboard. I have set the delegates of the textfields and textviews to the view controllers. The code I have so far is below. Thanks for the help in advance.
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
activeField = textField;
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
activeField = nil;
}
- (void)registerForKeyboardNotifications
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWasShown:)
name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillBeHidden:)
name:UIKeyboardWillHideNotification object:nil];
}
- (void)keyboardWasShown:(NSNotification*)aNotification
{
NSDictionary* info = [aNotification userInfo];
CGRect kbRect = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue];
kbRect = [self.view convertRect:kbRect toView:nil];
CGSize kbSize = kbRect.size;
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0);
pageScrollView.contentInset = contentInsets;
pageScrollView.scrollIndicatorInsets = contentInsets;
// If active text field is hidden by keyboard, scroll it so it's visible
// Your application might not need or want this behavior.
CGRect aRect = self.view.frame;
aRect.size.height -= kbSize.height;
if (!CGRectContainsPoint(aRect, activeField.frame.origin) ) {
CGPoint scrollPoint = CGPointMake(0.0, activeField.frame.origin.y-kbSize.height);
[pageScrollView setContentOffset:scrollPoint animated:YES];
}
}
- (void)keyboardWillBeHidden:(NSNotification*)aNotification
{
UIEdgeInsets contentInsets = UIEdgeInsetsZero;
pageScrollView.contentInset = contentInsets;
pageScrollView.scrollIndicatorInsets = contentInsets;
}
michael tyson created TPKeyboardAvoiding which is very easy to use you can get it from github. https://github.com/michaeltyson/TPKeyboardAvoiding
I do not know why it works for your textview, but for a scrollview with textfields I am doing this manually via
// Called when the UIKeyboardDidShowNotification is sent.
- (void)keyboardWillBeShown:(NSNotification*)aNotification
{
if(keyboardShown) return;
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.5];
[UIView setAnimationDelegate:self];
NSDictionary* info = [aNotification userInfo];
// Get the size of the keyboard.
NSValue* keyboardFrameValue = [info objectForKey:UIKeyboardFrameEndUserInfoKey];
CGRect keyboardRectWrtScreen = [keyboardFrameValue CGRectValue];
CGRect keyboardRectWrtView = [scrollView convertRect:[[scrollView window] convertRect:keyboardRectWrtScreen fromWindow:nil] fromView: nil];
float keyboardHeight = keyboardRectWrtView.size.height;
scrollView.frame = CGRectMake(scrollView.frame.origin.x, scrollView.frame.origin.y, scrollView.frame.size.width,scrollView.frame.size.height-keyboardHeight);
if(activeTextField) {
CGRect rect = [contentView convertRect:[activeTextField bounds] fromView:activeTextField];
rect.origin.y -= 25;
rect.size.height += 50;
[scrollView scrollRectToVisible:rect animated:YES];
}
[UIView commitAnimations];
keyboardShown = YES;
}
as you see, I am manually moving the scroll view to the visible part, then ensuring that the text field is in the center of the scroll view.

Why is my UIScrollView scrolling the wrong way when keyboard pops up?

So, as often suggested for people who want the view to be pushed up when the keyboard appears, I use a UIScrollView. I copied and pasted the information from this page -- http://developer.apple.com/library/ios/#documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/KeyboardManagement/KeyboardManagement.html#//apple_ref/doc/uid/TP40009542-CH5-SW7 -- to do that...
But what happens is, when I tap the text fields, the keyboard pops up, but the view actually scrolls DOWN, revealing black on top. After that, I can actually manually scroll the view up and reveal the hidden text fields...obviously, that's not what I'm trying to do!
Any ideas? I have the following in the viewDidLoad() method:- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWasShown:) name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillBeHidden:) name:UIKeyboardWillHideNotification object:nil];
}
I've tried this version of keyboardWasShown:
- (void) keyboardWasShown:(NSNotification *)aNotification
{
NSDictionary * info = [aNotification userInfo];
CGSize keyboardSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0, 0,keyboardSize.height, 0);
[[self introScroll] setContentInset:contentInsets];
[[self introScroll] setScrollIndicatorInsets:contentInsets];
CGRect ltdRect=[[self view] frame];
ltdRect.size.height=keyboardSize.height;
if (!CGRectContainsPoint(ltdRect,[self activeField].frame.origin))
{
CGPoint scrollPoint = CGPointMake(0,[self activeField].frame.origin.y-keyboardSize.height);
[[self introScroll] setContentOffset:scrollPoint animated:YES];
}
NSLog(#"Keyboard out!");
}
as well as this version of keyboardWasShown:
- (void)keyboardWasShown:(NSNotification*)aNotification {
NSDictionary* info = [aNotification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
CGRect bkgndRect = [[[self activeField] superview ]frame];
bkgndRect.size.height += kbSize.height;
[[[self activeField] superview] setFrame:bkgndRect];
[[self introScroll] setContentOffset:CGPointMake(0.0, [self activeField].frame.origin.y-kbSize.height) animated:YES];
}
Both give me the exact same results.
What's going on?? I'm using Xcode 4.6.
I had a similar problem that my text fields did not scroll into view.
the apple code is not perfect, I had to modify it:
Try to solve that by adding the height of active field in line:
frame.origin.y + frame.size.height - keyboardSize.height
If that does not work, add 44 to scrollpoint.y (size of navigation bar)
Update for ios7:
I improved unwanted and wrong scrolling in ios7 for text fields, that are already in view
by
if (scrollPtY < 0) {
return;
}

TableView content Height Changed after keyboard show

I have a Scroll View, Table View, and textfield at the bottom which will trigger a keyboard show after clicked.
Table View is just a subview inside the Scroll view which to show some comments for that photos.
At the beginning, the tableView height shows correctly. However, after clicked any textField in the class, the tableView height changed. Anyone has solutions for this.
I have tested for the keyboard height. It will affect the additional height of the UITableView.
But I have no any ideas on how to keep the height the same as before the keyboard shows.
Please help.
Here is some codes,
//---when a TextField view begins editing---
-(BOOL) textFieldDidBeginEditing:(UITextField *)textFieldView {
currentTextField = textFieldView;
return YES;
}
-(BOOL) textFieldShouldReturn:(UITextField *) textFieldView {
[textFieldView resignFirstResponder];
return NO;
}
//---when a TextField view is done editing---
-(void) textFieldDidEndEditing:(UITextField *) textFieldView {
currentTextField = nil;
}
//---when the keyboard appears---
-(void) keyboardDidShow:(NSNotification *) notification {
if (keyboardIsShown) return;
NSDictionary* info = [notification userInfo];
//---obtain the size of the keyboard---
NSValue *aValue = [info objectForKey:UIKeyboardFrameEndUserInfoKey];
CGSize keyboardSize = [aValue CGRectValue].size;
//---resize the scroll view (with keyboard)---
CGRect viewFrame = [v_comment_editor frame];
viewFrame.size.height -= keyboardSize.height;
v_comment_editor.frame = viewFrame;
//---scroll to the current text field---
CGRect textFieldRect = [currentTextField frame];
[v_comment_editor scrollRectToVisible:textFieldRect animated:YES];
keyboardIsShown = YES;
}
//---when the keyboard disappears---
-(void) keyboardDidHide:(NSNotification *) notification {
NSDictionary* info = [notification userInfo];
//---obtain the size of the keyboard---
NSValue *aValue = [info objectForKey:UIKeyboardFrameEndUserInfoKey];
CGSize keyboardSize = [aValue CGRectValue].size;
//---resize the scroll view back to the original size (without keyboard)---
CGRect viewFrame = [v_comment_editor frame];
viewFrame.size.height += keyboardSize.height;
v_comment_editor.frame = viewFrame;
keyboardIsShown = NO;
}
-(void) viewWillDisappear:(BOOL)animated {
//---removes the notifications for keyboard---
[[NSNotificationCenter defaultCenter]
removeObserver:self
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter]
removeObserver:self
name:UIKeyboardWillHideNotification
object:nil];
}
The code you show does resize the views when the keyboard is shown. It looks like it is supposed to return to the correct size when keyboard is hidden again.
If you have trouble with one of the subviews, it is possible that it's autoresize masks are set up in a weird way.
The simplest way around it would be to have an instance variable in your handling class like:
CGRect tableframe;
and store the correct frame to it in the keyboarddidshow function, and restore the table to the original frame in ** keyboardDidHide** method.
Might help to look into UIScrollView's contentOffset and contentInset properties. Also it will help to understand the difference between the bounds of the scrollView and the frame.
I strongly advise creating a simple test project and experimenting with the above concepts. Having a thorough understanding will make your life much easier.
Note: be careful of translucent navigationBars and their affect on the above properties.
For all of other people reference, in case you have this problem again.
The solution i used to go through this is to reset the size again in the keyboardDidHide, and code is as below:
CGRect frame = tbl_comment.frame;
frame.size.height = 145;
tbl_comment.frame = frame;

avoid view when opening keyboard from a UITextView inside a UITableView with custom cell

I have a UIViewController that contains a UITableView with custom cells, inside the cell are UILabels, a couple of uneditable UITextView and one editable UITextView. Now, when I tap on one of the UITextView that is near the bottom or the bottom part of the table, the UITextView is covered by the keyboard. I've tried http://cocoawithlove.com/2008/10/sliding-uitextfields-around-to-avoid.html which works great for textfield/textview but not working on the table with custom cell. Any help or suggestions how to go about this?
When your table view contains data entry fields like a UITextField or a UITextView and the table view is long enough to cover the screen, you will have a problem accessing data entry fields that are hidden by the keyboard.
To overcome this problem two solutions are:
The easiest and recommended way is to use a UITableViewController instead of UIViewController, which automatic make sure keypad won't hide the editable field (If possible use this approach to avoid U.I. adjustment inconvenience)
If you use a UIViewController and a UITableView as its subview. You can scroll your UI’s frame by observing the UIKeyboardWillShowNotification and UIKeyboardWillHideNotification
- (void)registerForKeyboardNotifications
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification object:nil]; //Posted immediately prior to the display of the keyboard
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification object:nil]; //Posted immediately prior to the dismissal of the keyboard.
}
- (void)keyboardWillShow:(NSNotification *)aNotification
{
CGRect keyboardBounds = [[[aNotification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue];
[UIView beginAnimations:nil context:nil];
[UIView setAnimationBeginsFromCurrentState:YES];
self.tableView.contentInset = UIEdgeInsetsMake(0, 0, keyboardBounds.size.height, 0); //when keyboard is up, that time just bring your text filed above the keyboard
self.tableView.scrollIndicatorInsets = UIEdgeInsetsMake(0, 0, keyboardBounds.size.height, 0);
[self.tableView scrollToRowAtIndexPath:[self findIndexPathToScroll]
atScrollPosition:UITableViewScrollPositionTop
animated:YES]; //findIndexPathToScroll implementation not shown
[UIView commitAnimations];
}
- (void)keyboardWillHide:(NSNotification *)aNotification
{
[UIView beginAnimations:nil context:nil];
[UIView setAnimationBeginsFromCurrentState:YES];
self.tableView.contentInset = UIEdgeInsetsZero; //Once keyboard is hidden then bring back your table into your original position.
self.tableView.scrollIndicatorInsets = UIEdgeInsetsZero;
[UIView commitAnimations];
}
registerForKeyboardNotifications - call this method when you load the UITableView, ie: viewDidLoad
findIndexPathToScroll - (Implementation not shown) Its your business logic to prepare IndexPath where table view should scroll
removeObserver 'UIKeyboardWillShowNotification' and 'UIKeyboardWillHideNotification' both in dealloc and viewDidUnload
I fixed the issue. Please see my solution below:
1. First declare a global varibale called "activeFileld"
#property(nonatomic,strong)id activeFiled;
2. Create a method called "registerForKeyboardNotifications"
- (void)registerForKeyboardNotifications
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification object:nil]; //Posted immediately prior to the display of the keyboard
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification object:nil]; //Posted immediately prior to the dismissal of the keyboard.
}
3. Called the above method in viewWillAppear:
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
//Register kryboard Notification
[self registerForKeyboardNotifications];
}
4. Call the Delegate method for UitextFieldd Or UitextView
- (void)textFieldDidBeginEditing:(UITextField *)sender {
self.activeField = sender;
}
- (void)textFieldDidEndEditing:(UITextField *)sender{
self.activeField = nil;
}
- (void)textViewDidBeginEditing:(UITextView *)textView
{
// save the text view that is being edited
_notes = textView.text;
}
- (void)textViewDidEndEditing:(UITextView *)textView
{
// release the selected text view as we don't need it anymore
_activeField = nil;
}
5.
- (void)keyboardWillShow:(NSNotification *)notification
{
if([_activeField isKindOfClass:[UITextField class]]) {
NSDictionary* info = [notification userInfo];
NSLog(#"Dictionary %#",info);
CGRect kbRect = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue];
kbRect = [self.view convertRect:kbRect fromView:nil];
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbRect.size.height, 0.0);
self.tableView.contentInset = contentInsets;
self.tableView.scrollIndicatorInsets = contentInsets;
CGRect aRect = self.view.frame;
aRect.size.height -= kbRect.size.height;
UITextField *textField = (UITextField*)_activeField;
if (!CGRectContainsPoint(aRect, textField.frame.origin) ) {
[self.tableView scrollRectToVisible:textField.frame animated:YES];
}
}else if([_activeField isKindOfClass:[UITextView class]]) {
NSDictionary* info = [notification userInfo];
NSLog(#"Dictionary %#",info);
CGRect kbRect = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue];
kbRect = [self.view convertRect:kbRect fromView:nil];
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbRect.size.height, 0.0);
self.tableView.contentInset = contentInsets;
self.tableView.scrollIndicatorInsets = contentInsets;
CGRect aRect = self.view.frame;
aRect.size.height += kbRect.size.height;
UITextView *activeTextView = (UITextView*)_activeField;
if (!CGRectContainsPoint(aRect, textField.superview.superview.frame.origin) ) {
[self.tableView scrollRectToVisible:activeTextView.superview.superview.frame animated:YES];
}
}
}
// Called when the UIKeyboardWillHideNotification is received
- (void)keyboardWillHide:(NSNotification *)aNotification
{
UIEdgeInsets contentInsets = UIEdgeInsetsZero;
self.tableView.contentInset = contentInsets;
self.tableView.scrollIndicatorInsets = contentInsets;
}
Two solutions:
Preferred: use a UITableViewController instead of a UIViewController as that one will automatically make sure that your keypad won't hide the editable field.
Hacky: How to make a UITextField move up when keyboard is present?
A simple solution. Implement the heightForFooter method, and let it return a value of (say) 100, and when you select the a cell in UITableView, they will simply slide up by that height, and the keyboard will not cover the view.
I've always used a two fold solution for this.
Resize table so it now fits in the smaller area.
Scroll to the cell we want visible. (we needed to re-size the table for this or you'd still wind up being unable to get to the last couple of cells in the table.)
To do this, I register keyboard show/hide events and act accordingly when they get called.
- (void)keyboardWillShow:(NSNotification *)note {
[self updateForKeyboardShowHide:note appearing:YES];
}
- (void)keyboardWillHide:(NSNotification *)note {
[self updateForKeyboardShowHide:note appearing:NO];
}
- (void)updateForKeyboardShowHide:(NSNotification *)note appearing:(BOOL)isAppearing {
// ignore notifications if our view isn't attached to the window
if (self.view.window == nil)
return;
CGFloat directionalModifier = isAppearing?-1:1;
CGRect keyboardBounds = [[note.userInfo valueForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue];
CGFloat animationDuration = [[note.userInfo valueForKey:UIKeyboardAnimationDurationUserInfoKey] floatValue];
// figure out table re-size based on keyboard
CGFloat keyboardHeight;
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
if (UIInterfaceOrientationIsPortrait(orientation))
keyboardHeight = keyboardBounds.size.height;
else
keyboardHeight = keyboardBounds.size.width;
[UIView animateWithDuration:animationDuration animations:^{
// resize table
CGRect newFrame = table.frame;
newFrame.size.height += [self calculateKeyboardOffsetWithHeight:keyboardHeight] * directionalModifier;
table.frame = newFrame;
} completion:^(BOOL finished){
// scroll to selected cell
if (isAppearing) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:textFieldInEdit.tag inSection:0];
[table scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}
}];
}
- (CGFloat)calulateKeyboardOffsetWithHeight:(CGFloat)keyboardHeight {
// This depends on the size and position of your table.
// If your table happen to go all the way to the bottom of
// the screen, you'll needs to adjust it's size by the whole keyboard height.
// You might as well ditch this method and inline the value.
return keyboardHeight;
// My table did not go to the bottom of the screen and the position was
// change dynamically so and there was long boring calculation I needed to
// do to figure out how much my table needed to shrink/grow.
}

How do I determine when the background area is tapped?

I am trying to determine when a user taps on some area other than the single UITextView I have on the screen. This is similar to this question about UITableViews, but I have a few problems with the solutions presented there. When the keyboard is dismissed, I scroll the screen a bit to hide where the keyboard was. My problem is that when I use UITapGestureRecognizer to determine if the screen was tapped, the tap doesn't go through to the other controls on the screen. I am using gestureRecognizer.cancelsTouchesInView = NO, it's a problem with the timing. The screen scrolls away before the control recognizes that it was clicked. Any idea how I can solve the problem? I'm perfectly happy using something other than gesture recognition.
I've used a custom (invisible) button as the background layer to do this in the past.
Keep the gesture recognizer. Have it use a method like this:
- (void)dismissKeyboard:(UIGestureRecognizer *)gesture
{
[self.view endEditing:NO];
}
I found the solution to the problem. I'm still using the UITapGestureRecognizer, but I now have a zero-length delay before hiding the keyboard, which allows the tap event to propagate correctly to the subviews, such as buttons. The basic view is a text field and button controls filling the screen. To allow the screen to be viewed correctly when the keyboard is shown, the whole thing is wrapped in a scroll view, and a placeholder view for the area the keyboard takes up is added to the bottom. It is only expanded when the keyboard is shown. Here are all the relevant pieces of code, which should allow anyone to implement the tap-anywhere-to-dismiss-keyboard idea as well as solving the problem of controls being hidden by the keyboard:
- (void)viewDidLoad {
[super viewDidLoad];
...
UITapGestureRecognizer *tapRecognizer = [[[UITapGestureRecognizer alloc] initWithTarget: self action: #selector(hideKeyboardWithDelay)] autorelease];
tapRecognizer.cancelsTouchesInView = NO;
[self.view addGestureRecognizer: tapRecognizer];
[[NSNotificationCenter defaultCenter] addObserver: self selector: #selector(keyboardWillBeShown:) name: UIKeyboardWillShowNotification object: nil];
[[NSNotificationCenter defaultCenter] addObserver: self selector: #selector(keyboardWillBeHidden:) name: UIKeyboardWillHideNotification object: nil];
}
- (void) hideKeyboardWithDelay {
// using afterDelay allows the event to go through to any button before scrolling
[self performSelector: #selector(hideKeyboard) withObject: nil afterDelay: 0.0f];
}
- (void) hideKeyboard {
[self.myTextField1 resignFirstResponder];
[self.myTextField2 resignFirstResponder];
}
- (void) keyboardWillBeShown: (NSNotification *) notification {
NSDictionary* info = [notification userInfo];
CGSize keyboardSize = [[info objectForKey: UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
CGRect frame = self.keyboardPlaceholder.frame;
self.keyboardPlaceholder.frame = CGRectMake(frame.origin.x, frame.origin.y, frame.size.width, keyboardSize.height);
CGFloat contentHeight = self.scrollView.contentSize.height + keyboardSize.height + 20; // in my case, save 20px for the status bar
self.scrollView.contentSize = CGSizeMake(frame.size.width, contentHeight);
[self.scrollView scrollRectToVisible: self.keyboardPlaceholder.frame animated: YES];
}
- (void) keyboardWillBeHidden: (NSNotification *) notification {
CGRect frame = self.keyboardPlaceholder.frame;
NSDictionary* info = [notification userInfo];
NSValue* value = [info objectForKey:UIKeyboardAnimationDurationUserInfoKey];
NSTimeInterval duration = 0.3f; // default keyboard animation time
[value getValue: &duration];
[UIView beginAnimations: #"hideKeyboardAnimation" context: nil];
[UIView setAnimationDuration: duration];
[UIView setAnimationCurve: UIViewAnimationCurveEaseInOut];
self.keyboardPlaceholder.frame = CGRectMake(frame.origin.x, frame.origin.y, frame.size.width, 0);
self.scrollView.contentSize = self.view.frame.size;
[UIView commitAnimations];
}
- (void)viewDidUnload {
[[NSNotificationCenter defaultCenter] removeObserver: self];
[super viewDidUnload];
}
Hope someone finds this useful.