iOS - Interface elements overlapping when move the view - ios

I have the following interface layout on my application:
When I click on a text field (if you lick add tag a new text field is added) I make my view scroll up as to not be obstructed by the keyboard so the user can properly see what he's typing, but the following happens when I do so:
This is the code I use to scroll:
- (void)keyboardWillShow:(NSNotification *)notification {
if (self.keyboardIsShown) {
return;
}
NSValue *keyboardBoundsValue = [[notification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey];
CGSize keyboardSize = [keyboardBoundsValue CGRectValue].size;
NSTimeInterval animationDuration = 0.3;
CGRect frame = self.view.frame;
frame.origin.y -= keyboardSize.height - 94 + self.firstResponder.superview.superview.frame.origin.y;
frame.size.height += keyboardSize.height - 94 + self.firstResponder.superview.superview.frame.origin.y;
[UIView beginAnimations:#"ResizeForKeyboard" context:nil];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:animationDuration];
self.view.frame = frame;
[UIView commitAnimations];
self.keyboardIsShown = YES;
}
- (void)keyboardWillHide:(NSNotification *)notification {
NSValue *keyboardBoundsValue = [[notification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey];
CGSize keyboardSize = [keyboardBoundsValue CGRectValue].size;
NSTimeInterval animationDuration = 0.3;
CGRect frame = self.view.frame;
frame.origin.y += keyboardSize.height - 94 + self.firstResponder.superview.superview.frame.origin.y;
frame.size.height -= keyboardSize.height - 94 + self.firstResponder.superview.superview.frame.origin.y;
[UIView beginAnimations:#"ResizeForKeyboard" context:nil];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:animationDuration];
self.view.frame = frame;
[UIView commitAnimations];
self.keyboardIsShown = NO;
}
Any idea how I can make the view elements show behind the navbar or disappear or another workaround that would work properly?

A quick way to achieve what you want, would be to have your navigation bar below the "content"-view in your view tree. That way the navigation bar will remain on top. If you add the content dynamically. Make sure you add them as subviews of a view that is behind the navigation bar.
// Edit
As proposed in the comments:
self.view.clipsToBounds = YES;
CGRect frame = self.yourWrappingView.frame;
NSUInteger distanceToMove = 200;
self.yourWrappingView.frame = CGRectMake(frame.origin.x - distanceToMove, frame.origin.y, frame.size.width, frame.size.height);
This should work for you mate :)

Probably you are not using a real UINavigationController. But you should. So you wouldn't have those problems and a correct resizing behaviour. But to solve it in your current architecture:
Make sure the Navigation Bar is the top most object in your nib file / storyboard
add an IBOutlet for your UINavigationBar and if you insert new views dynamically, use [self insertSubview: newView belowSubview: navigationBarOutlet];

Related

Move up keyboard when editing UITextField on iOS9

For my keyboards to move up to uncover UITextField in my iOS app, I used to implement this answer: https://stackoverflow.com/a/6908258/3855618 on iOS7 and 8 and it has worked perfectly for now. However on iOS 9.1, it doesn't work anymore.
To be more accurate, even if the background view does move up, the UITextField doesn't.
Any idea of what has changed so much since iOS9 and iOS 9.1?
The answer you have linked is not recommended. You should not set the view controller view's frame directly, especially not if you are using auto layout. Instead of changing the view's frame you should add a scrollview as a subview to the view, and adjust the content inset when the keyboard is shown or hidden.
From the official apple doc:
When asked to display the keyboard, the system slides it in from the bottom of the screen and positions it over your app’s content. Because it is placed on top of your content, it is possible for the keyboard to be placed on top of the text object that the user wanted to edit. When this happens, you must adjust your content so that the target object remains visible.
Adjusting your content typically involves temporarily resizing one or more views and positioning them so that the text object remains visible. The simplest way to manage text objects with the keyboard is to embed them inside a UIScrollView object (or one of its subclasses like UITableView). When the keyboard is displayed, all you have to do is reset the content area of the scroll view and scroll the desired text object into position. Thus, in response to a UIKeyboardDidShowNotification, your handler method would do the following:
Get the size of the keyboard.
Adjust the bottom content inset of your scroll view by the keyboard height.
Scroll the target text field into view.
// Called when the UIKeyboardDidShowNotification is sent.
- (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 app might not need or want this behavior.
CGRect aRect = self.view.frame;
aRect.size.height -= kbSize.height;
if (!CGRectContainsPoint(aRect, activeField.frame.origin) ) {
[self.scrollView scrollRectToVisible:activeField.frame animated:YES];
}
}
// Called when the UIKeyboardWillHideNotification is sent
- (void)keyboardWillBeHidden:(NSNotification*)aNotification
{
UIEdgeInsets contentInsets = UIEdgeInsetsZero;
scrollView.contentInset = contentInsets;
scrollView.scrollIndicatorInsets = contentInsets;
}
Zero lines of Code
Devoid of hacks, kludges, workaround and listeners.
The present question has been asked over and over since the dawn of iOS time. No answer on StackOverflow survived more than 2 iOS iterations. Rightly so, because the UIKit keeps changing from underneath your feet. There exists a design as opposed to implementation solution to this ancient problem. Use a UITableViewController.
Use a UITableViewController
When a UITableView is managed by a UITableViewController, the scrolling is managed automatically for you. Never tinker with UIKeyboardWillShowNotification, ever again. Merely create static or dynamic UITableViewCells to layout your interface, add UITextView or UITextField as needed ; merely becoming first responder will scroll the the proper location.
#availability(iOS, introduced=2.0)
Notes
Works on all iOS since 2.0.
Quote: «Waste no time optimizing a poor algorithm ; pick a better one»
See https://stackoverflow.com/a/32390936/218152.
We need to take keyboard frame from notification. When get reference of scrollView, tableView, etc. Convert low border of view to window`s coordinates. When determine how much keyboard covers our view, and if difference is greater than 0, we can add inset below.
Try this code:
- (void)subscribeKeyboardNotifications
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification
object:nil];
}
- (void)unsubscribeKeyboardNotifications
{
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIKeyboardWillHideNotification
object:nil];
}
- (void)keyboardWillShow:(NSNotification *)aNotification
{
CGRect keyBoardFrame = [[[aNotification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
UIWindow *keyWindow = [[[UIApplication sharedApplication] delegate] window];
UIScrollView *someScrollView = ......
CGPoint tableViewBottomPoint = CGPointMake(0, CGRectGetMaxY([someScrollView bounds]));
CGPoint convertedTableViewBottomPoint = [someScrollView convertPoint:tableViewBottomPoint
toView:keyWindow];
CGFloat keyboardOverlappedSpaceHeight = convertedTableViewBottomPoint.y - keyBoardFrame.origin.y;
if (keyboardOverlappedSpaceHeight > 0)
{
UIEdgeInsets tableViewInsets = UIEdgeInsetsMake(0, 0, keyboardOverlappedSpaceHeight, 0);
[someScrollView setContentInset:tableViewInsets];
}
}
- (void)keyboardWillHide:(NSNotification *)aNotification
{
UIEdgeInsets tableViewInsets = UIEdgeInsetsZero;
UIScrollView *someScrollView = ......
[someScrollView setContentInset:tableViewInsets];
}
Add all UITextField on UIScrollView and use TPKeyboardAvoiding
I'm usually listening to keyboard notifications and make according changes to layout constraints. See my other answer for more details and a sample project.
Try this code that I have used in my previous projects:
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
[self didBeginEditingIn:textField];
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
[self didEndEditing];
}
static const CGFloat KEYBOARD_ANIMATION_DURATION = 0.3;
static const CGFloat MINIMUM_SCROLL_FRACTION = 0.2;
static const CGFloat MAXIMUM_SCROLL_FRACTION = 0.8;
static const CGFloat PORTRAIT_KEYBOARD_HEIGHT = 216+100;
static const CGFloat LANDSCAPE_KEYBOARD_HEIGHT = 162+100;
- (void)didBeginEditingIn:(UIView *)view
{
CGRect textFieldRect = [self.view.window convertRect:view.bounds fromView:view];
CGRect viewRect = [self.view.window convertRect:self.view.bounds fromView:self.view];
CGFloat midline = textFieldRect.origin.y + 0.5* textFieldRect.size.height;
CGFloat numerator = midline - viewRect.origin.y- MINIMUM_SCROLL_FRACTION * viewRect.size.height;
CGFloat denominator = (MAXIMUM_SCROLL_FRACTION - MINIMUM_SCROLL_FRACTION)* viewRect.size.height;
CGFloat heightFraction = numerator / denominator;
if (heightFraction < 0.0)
{
heightFraction = 0.0;
}
else if (heightFraction > 1.0)
{
heightFraction = 1.0;
}
UIInterfaceOrientation orientation =
[[UIApplication sharedApplication] statusBarOrientation];
if (orientation == UIInterfaceOrientationPortrait ||
orientation == UIInterfaceOrientationPortraitUpsideDown)
{
_animatedDistance = floor(PORTRAIT_KEYBOARD_HEIGHT * heightFraction);
}
else
{
_animatedDistance = floor(LANDSCAPE_KEYBOARD_HEIGHT * heightFraction);
}
CGRect viewFrame = self.view.frame;
viewFrame.origin.y -= _animatedDistance;
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:KEYBOARD_ANIMATION_DURATION];
[self.view setFrame:viewFrame];
[UIView commitAnimations];
}
- (void)didEndEditing
{
CGRect viewFrame = self.view.frame;
viewFrame.origin.y += _animatedDistance;
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:KEYBOARD_ANIMATION_DURATION];
[self.view setFrame:viewFrame];
[UIView commitAnimations];
}
i followed the doc from #Istvan to the apple site, and there are a lot of stuff missing to make it work:
1. Set your .h document to <UITextFieldDelegate> (to be able to work with "activefield")
2. In the viewDidLoad, set the delegates to your UITextfields, and set the height of your scrollview content with a bigger height (in my case i've setted 500 more):
CGRect screenRect = [[UIScreen mainScreen] bounds];
CGFloat screenWidth = screenRect.size.width;
CGFloat screenHeight = screenRect.size.height + 500;
_scrollView.contentSize = CGSizeMake(screenWidth, screenHeight);
And now it's all working...

Textview Frame is become squeez and cannot come back to its original form?

keyboardWillShow method implementation :
- (void)keyboardWillShow:(NSNotification *)notification
{
[UIView beginAnimations:nil context:nil];
CGRect endRect = [[notification.userInfo
objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGRect newRect = txtNotes.frame;
//Down size your text view
newRect.size.height -= endRect.size.height;
txtNotes.frame = newRect;
[UIView commitAnimations];
}
keyboardWillHide method implementation :
- (void)keyboardWillHide:(NSNotification *)notification
{
// Resize your textview when keyboard is going to hide
UIEdgeInsets contentInsets = UIEdgeInsetsZero;
txtNotes.contentInset = contentInsets;
txtNotes.scrollIndicatorInsets = contentInsets;
}
Couple of things:
1) META: You should add 4 spaces before each of the code lines to get the code to display properly.
2) This looks like a basic constraints issue. You should examine the Auto Layout of your storyboard.

iOS Animating main UIViewController's View height

I have a text field pinned to the bottom of my main UIViewController's view (self.view), and when the user clicks on it this function is called (via a UIKeyboardWillShowNotification) which will alter the height of the self.view.frame:
-(void)keyboardWillShow: (NSNotification*) notification {
NSDictionary* info = [notification userInfo];
CGSize keyboardSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
CGFloat textFieldHeight = activeField.frame.size.height;
CGPoint textFieldOrigin = activeField.frame.origin;
textFieldOrigin.y += textFieldHeight;
CGRect visibleRect = self.view.frame;
visibleRect.size.height -= keyboardSize.height;
if (!CGRectContainsPoint(visibleRect, textFieldOrigin)) {
CGRect r = self.view.frame;
r.size.height -= keyboardSize.height;
[UIView animateWithDuration:1.0
animations:^{
self.view.frame = r;
}];
}
}
It resizes the self.view.frame fine, so it is all being called and run, but for some reason refuses to animate it over a second - it just appears in place immediately.
What do I need to do in order to animate this change in height?
If you have auto layout on you shouldn't change the frame directly, instead you should change on of the constrains.
Add an IBOutlet to the constrain (the bottom constrain), and change the constant like so:
myTextFieldConstrain.constant -= YOUR_VALUE
Also, if you want it to animate, call [YOURSUPERVIEW layoutIfNeeded]; after you change the constant.
Example:
[UIView animateWithDuration:1.0
animations:^{
myTextFieldConstrain.constant -= YOUR_VALUE;
[YOURSUPERVIEW layoutIfNeeded];
}];
Try to dispatch it with a slight delay, like:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[UIView animateWithDuration:1.0
animations:^{
self.view.frame = r;
}];
}
It usually makes the trick.

UITableView slightly goes up when keyboard hides

I am using UITableView (chatTable) along with UITabBar (chatTabBar) and one textField inside imageView. I am using autolayout. I used the following code to change the views when keyboard appears and disappears.
- (void)keyboardWasShown:(NSNotification*)aNotification
{
NSDictionary* info = [aNotification userInfo];
// get animation info from userInfo
NSTimeInterval animationDuration;
CGRect keyboardFrame;
[[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] getValue:&animationDuration];
[[info objectForKey:UIKeyboardFrameBeginUserInfoKey] getValue:&keyboardFrame];
// resize the frame
[UIView animateWithDuration:animationDuration delay:0.0 options:UIViewAnimationOptionCurveEaseOut animations:^{
self.keyboardHeight.constant = keyboardFrame.size.height - TABBAR_HEIGHT ;
[self.view layoutIfNeeded];
} completion:nil];
if ([chatData count] != VALUE_ZERO)
{
[chatTable scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:([chatData count] - VALUE_ONE) inSection:VALUE_ZERO] atScrollPosition:UITableViewScrollPositionBottom animated:NO];
}
}
- (void)keyboardWillHide:(NSNotification*)aNotification
{
NSDictionary* info = [aNotification userInfo];
// get animation info from userInfo
NSTimeInterval animationDuration;
CGRect keyboardFrame;
[[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] getValue:&animationDuration];
[[info objectForKey:UIKeyboardFrameBeginUserInfoKey] getValue:&keyboardFrame];
// Set view frame
[UIView animateWithDuration:animationDuration delay:2.0 options:UIViewAnimationOptionCurveEaseOut animations:^{
self.keyboardHeight.constant -= keyboardFrame.size.height - TABBAR_HEIGHT;
[self.view layoutIfNeeded];
} completion:nil];
}
Now when I press return the tableview goes up a littel bit (from screen 2 to screen 3). keyboardHeight is the bottom space constraint between the tabBar and main view.
(screen 2)
(screen3)
I have tried many things but I can't able to find why the tableview is going up for a while. (problem is there is no smooth animation.) (Note: I have put delay as 2.0 only to show what happens in following screenshot(screen 3) othewise it's value would be 0)
Your problem is that you're changing the table view frame when the keyboard appears, which is wrong. You need to change the contentInset property of the table view, instead of meddling with frames.
- (void)keyboardWillShow:(NSNotification *)notification {
CGFloat height = [notification.userInfo[UIKeyboardFrameBeginUserInfoKey] CGRectValue].size.height - self.tabBarController.tabBar.frame.size.height;
UIEdgeInsets edgeInsets = UIEdgeInsetsMake(0.0f, 0.0f, height, 0.0f);
_tableView.contentInset = edgeInsets;
_tableView.scrollIndicatorInsets = edgeInsets;
}
- (void)keyboardWillHide:(NSNotification *)notification {
UIEdgeInsets edgeInsets = UIEdgeInsetsZero;
_tableView.contentInset = edgeInsets;
_tableView.scrollIndicatorInsets = edgeInsets;
}
Solved the problem with contentInset property. I am using contentInset as mentiond by the #Eugene and also changing the constant property of bottom constraint of the textfiled to move up and doen whenever keyboard is shown and hidden.

UIKeyboardWillShowNotification incorrectly changes frame of UIWebView

I am using a UIKeyboardWillShowNotification to know when the keyboard is shown and adjust the size of my UIWebView so that it isn't hidden behind the keyboard.
The strange thing is, when I change the frame in the method that gets called by NSNotificationCenter it changes the frame in a way that lets me scroll my UIWebView content (red in screenshot), but also a large portion of the UIWebView scrolls into view (yellow in screenshot). The yellow should never be shown.
- (void)keyboardWillShowOrHide:(NSNotification *)notification {
// User Info
NSDictionary *info = notification.userInfo;
CGFloat duration = [[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] floatValue];
int curve = [[info objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue];
CGRect keyboard = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue];
if ([notification.name isEqualToString:UIKeyboardWillShowNotification]) {
[UIView animateWithDuration:duration delay:0 options:curve animations:^{
CGRect frame = self.toolbarHolder.frame;
frame.origin.y = (self.view.frame.size.height - keyboard.size.height) - 44;
self.toolbarHolder.frame = frame;
// Editor View
CGRect editorFrame = self.editorView.frame;
editorFrame.size.height = (self.view.frame.size.height - keyboard.size.height) - 44;
self.editorView.frame = editorFrame;
} completion:nil];
} else {
[UIView animateWithDuration:duration delay:0 options:curve animations:^{
CGRect frame = self.toolbarHolder.frame;
frame.origin.y = self.view.frame.size.height;
self.toolbarHolder.frame = frame;
// Editor View
CGRect editorFrame = self.editorView.frame;
editorFrame.size.height = self.view.frame.size.height;
self.editorView.frame = editorFrame;
} completion:nil];
}
}
If I change the UIWebView frame in a different method than the one called from NSNotificationCenter, the frame changes correctly and the area above the keyboard is only filled with my HTML content within the UIWebView (red).
What could be causing this issue?
Use UIKeyboardFrameEndUserInfoKey key that returns the final expected frame for keyboard

Resources