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.
Related
I have an ImageView on a UIView. I'm trying to move view keyboard size but all content move properly instead of UIImageView.
-(void) keyboardWillShow:(NSNotification *) notification {
NSDictionary* keyboardInfo = [notification userInfo];
// Work out where the keyboard will be
NSValue* keyboardFrameBegin = [keyboardInfo valueForKey:UIKeyboardFrameBeginUserInfoKey];
CGRect keyboardFrameBeginRect = [keyboardFrameBegin CGRectValue];
// Work out animation duration
NSTimeInterval animationDuration =[[keyboardInfo valueForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
UIViewAnimationOptions keyboardAnimationCurve = [[keyboardInfo valueForKey:UIKeyboardAnimationCurveUserInfoKey] integerValue];
// Animate this
[UIView animateWithDuration:animationDuration
delay:0.0
options:keyboardAnimationCurve
animations:^(){
self.view.frame = CGRectOffset(self.view.frame, 0, -keyboardFrameBeginRect.size.height);
}
completion:NULL];
[UIImageView animateWithDuration:animationDuration
delay:0.0
options:keyboardAnimationCurve
animations:^(){
self.imgBankLogo.frame = CGRectOffset(self.imgBankLogo.frame, 0, -keyboardFrameBeginRect.size.height);
}
completion:NULL];
}
Anyone had same issue or some suggestion? I even tried to move UIImage but it is not moving.
As you can see everything moved except imageview!
If you are trying to move two views together, and one is a subview of the other, you shouldn't need to animate them individually.
Animating the parent view should move all of the subviews with it automatically. Trying to animate them individually at the same time can cause weird results in my experience. This animation block should be all you need. You may also want to check the Auto Layout settings in the views File Inspector tab.
If you want to perform more than one animation at once you can add multiple calls in the same animation block as well.
[UIView animateWithDuration:animationDuration
delay:0.0
options:keyboardAnimationCurve
animations:^(){
//you can add multiple here
self.view.frame = CGRectOffset(self.view.frame, 0, -keyboardFrameBeginRect.size.height);
}
completion:NULL];
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.
I am doing an animation to show the text view on top keyboard when UIKeyboardWillShowNotification is being broadcasted like following
-(void) keyboardWillShow:(NSNotification *)notification{
// get keyboard size and loctaion
NSDictionary *info = [notification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;
NSNumber *duration = [notification.userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey];
__block CGRect containerFrame = containerView.frame;
containerFrame.origin.y = self.view.bounds.size.height - (kbSize.height + containerFrame.size.height);
[UIView animateWithDuration:[duration doubleValue]
delay:0
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{
containerView.frame = containerFrame;
} completion:^(BOOL finished) {
nil;
}
];
}
An animation is not good enough.If you can give it a try and will see small issue at the end of animation.
I googled and found out that there is another way to do an animation and it works perfectly :
-(void) keyboardWillShow:(NSNotification *)note{
// get keyboard size and loctaion
CGRect keyboardBounds;
[[note.userInfo valueForKey:UIKeyboardFrameEndUserInfoKey] getValue: &keyboardBounds];
NSNumber *duration = [note.userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey];
NSNumber *curve = [note.userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey];
// Need to translate the bounds to account for rotation.
keyboardBounds = [self.view convertRect:keyboardBounds toView:nil];
// get a rect for the textView frame
CGRect containerFrame = containerView.frame;
containerFrame.origin.y = self.view.bounds.size.height - (keyboardBounds.size.height + containerFrame.size.height);
// animations settings
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:[duration doubleValue]];
[UIView setAnimationCurve:[curve intValue]];
// set views with new info
containerView.frame = containerFrame;
// commit animations
[UIView commitAnimations];
}
However, according to the documentation, we are not recommended to use this animation in Ios 4 and later on...
I can not tell any differences between my codes compared to the latter one. Why my codes can not do perfect animation. Please help if you have any ideas.
It's the animation curve. The example you cite is matching its animation curve as well its duration to those of the keyboard. Your code doesn't. Your structure should look more like this:
NSNumber* curve = info[UIKeyboardAnimationCurveUserInfoKey]; // *
NSNumber* duration = info[UIKeyboardAnimationDurationUserInfoKey];
[UIView animateWithDuration:duration.floatValue delay:0
options:curve.integerValue << 16 // *
animations: ^{ // ...
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
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];