I have screen with multiple TextFields
. So main view has scrollview
with constraints to edges set to 0. Scrollview
has one subview which exactly fits scrollview
. And now this subview has all TextFields
.
Issue is when navigating between TextFields
when user pressed Previous/Next
button on toolbar above keyboard. Issue is in iOS 6
only.
It works fine when Next
button is pressed. For Previous
button, it works for all TextFields
except one which is positioned 2nd in row of all TextFields
. So what should happen when 5th TextField
is active and I keep pressing Previous
to go to 2nd TextField
, if the TextField
is behind NavigationBar
or even above that then scrollview's
contentOffset
should be changed so that TextField
comes down and becomes visible. But actually it moves even more upside. Also button at bottom should be placed 20pts
from its superview's bottom which is normally the case. But when this issue occurs the button is visible more than 20pts
from its superview's bottom. Here, when I scroll even a little bit then scrollview contents shift down sharply and reaches at their correct position.i.e. button is at correct position now.
First image has incorrect position. Second image has correct button position.
Code to navigation between TextFields : Fields are given tag value from 1 to 5.
// Called when previous/next button is pressed
- (void)navigateBetweenTextField:(UISegmentedControl *)segmentedControl
{
// Check which segment is selected and change textfield accordingly
UITextField * textFieldToMakeActive = nil;
// If Previous is pressed
if (segmentedControl.selectedSegmentIndex == 0)
{
textFieldToMakeActive = (UITextField *)[self.view viewWithTag:(self.selectedTextField.tag - 1)];
}
// Else if Next is pressed
else if (segmentedControl.selectedSegmentIndex == 1)
{
textFieldToMakeActive = (UITextField *)[self.view viewWithTag:(self.selectedTextField.tag + 1)];
}
segmentedControl.selectedSegmentIndex = UISegmentedControlNoSegment;
// If textfield is not nil
if (textFieldToMakeActive)
{
// Make it active
[textFieldToMakeActive becomeFirstResponder];
}
}
Method called when keyboard is about to be visible :
// Called when the UIKeyboardWillShowNotification is sent.
- (void)keyboardWillShow:(NSNotification*)aNotification
{
NSDictionary* info = [aNotification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;
NSTimeInterval animationDuration;
[[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] getValue:&animationDuration];
self.keyboardAndAccessoryViewSize = kbSize;
[self moveViewSoThatTextFieldIsVisible:kbSize];
}
// It checks if textfield is visible and then moves the view to make it visible if not.
- (void)moveViewSoThatTextFieldIsVisible:(CGSize)kbSize
{
// Change the contentInset
UIEdgeInsets contentInsets = self.scrollView.contentInset;
// If iOS 7 or above then scrollview ends on bottom of screen. Thus, bottom of content inset should be moved by keyboard's height.
if ([self iOS7OrAbove])
{
contentInsets.bottom = kbSize.height;
}
// Else scrollview ends above tabbar at bottom. Thus, bottom of content inset that should be moved is only by keyboard's height - tabbar's height
else
{
contentInsets.bottom = kbSize.height - self.tabBarController.tabBar.bounds.size.height;
DLog(@"Content inset bottom : %f", contentInsets.bottom);
}
[UIView animateWithDuration:0.25 animations:^{
self.scrollView.contentInset = contentInsets;
self.scrollView.scrollIndicatorInsets = contentInsets;
CGFloat textFieldDistanceFromOrigin = self.selectedTextField.frame.origin.y - self.scrollView.contentOffset.y + self.selectedTextField.frame.size.height + TEXTFIELD_MARGIN;
CGFloat visibleViewHeightAfterKeyboardDisplay = self.scrollView.bounds.size.height - kbSize.height + TOOLBAR_HEIGHT;
// If active text field is hidden by keyboard, scroll it so it's visible
if(textFieldDistanceFromOrigin > visibleViewHeightAfterKeyboardDisplay)
{
DLog(@"Textfield is hidden by keyboard.");
// Calculate offset y value to add to scrollview's contentOffset so that textfield is visible then.
CGFloat offsetToAdd = textFieldDistanceFromOrigin - visibleViewHeightAfterKeyboardDisplay;
[self.scrollView setContentOffset:CGPointMake(0, self.scrollView.contentOffset.y + offsetToAdd) animated:YES];
}
if (textFieldDistanceFromOrigin < self.selectedTextField.bounds.size.height)
{
DLog(@"Textfield is above visible screen area.");
[self.scrollView setContentOffset:CGPointMake(0, self.selectedTextField.frame.origin.y - TEXTFIELD_MARGIN) animated:YES];
}
}];
}
TextField delegate method :
// This is called when TextField becomes active
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
// Store TextField which has just become active
self.selectedTextField = textField;
[self moveViewSoThatTextFieldIsVisible:self.keyboardAndAccessoryViewSize];
}
Any ideas on what can be the issue here? I have already posted lot of codes, but I ask me if you need more of it.
I tried hard to find a method which was causing issue and no luck even if I set breakpoint in each method. Don't know if I miss one or something magical.
But finally issue was in the code of checking if textfield is hidden behind or above navigation bar. I rectified it and now it works.
In my case below is the correct way to check it. It can differ from yours.
if (self.selectedTextField.frame.origin.y < self.scrollView.contentOffset.y)
{
// TextField is hidden behind navigation bar
}