b2cloud

23rd January 2013

UIKeyboard transitions

Guides | Tutorial By 4 years ago

Often I see programmers hardcode keyboard animations into their apps. This will work fine when they test it out but will fail on a non-English keyboard (such as the Chinese keyboard), landscape keyboard, or if the standard keyboard size changes in a future version of iOS. They always assume the keyboard is 216 pixels high, which is not always the case.

Shouldn’t there be a way to automatically figure out where to move your content to? Yep there is!

This method involves listening to the UIKeyboard notifications, looking at the user info from the NSNotification and then doing a few calculations to figure out where the keyboard will come up to in our view.

Download the project start point and run it. I’ve got a text field, a scroll view and a label. When the keyboard is presented, the bottom content of the scroll view will stay behind the keyboard, you can tell this happens because the scroll bar ends below the top of the keyboard where we can’t see it. Also that label that’s supposed to be always visible gets hidden behind the keyboard. At the end of the tutorial we want the content inset of the scroll view adjusted so we can scroll all the way to the bottom, and we want the label to always be visible, even when the keyboard is present.

  

First we need to subscribe for keyboard notifications. Open ViewController.m and paste the following into -viewDidLoad. These two methods allow us to listen to both keyboard hide and keyboard show notifications.

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillAnimate:) name:UIKeyboardWillHideNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillAnimate:) name:UIKeyboardWillShowNotification object:nil];

Now implement the following method

- (void) keyboardWillAnimate:(NSNotification*) notification
{
	NSLog(@"%@", notification);
}

Run the app and you will notice a bunch of user info with the notification, including animation duration, animation end frame and some other stuff too. Now we need to pull out some of this and figure out where the keyboard will animate to relative to our view (the frame listed is based on the window).

NSDictionary* userInfo = notification.userInfo;

const NSTimeInterval duration = [[userInfo valueForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
const CGRect endFrame = [[userInfo valueForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];

UIWindow* mainWindow = [[UIApplication sharedApplication].delegate window];

//	Keyboard end frame relative to our view
const CGRect endSelfViewFrame = [mainWindow convertRect:endFrame toView:self.view];

And then from this, work out the difference between the bottom of our scrollview/label and the top of the keyboard’s end frame

CGFloat labelDifference = 0;
CGFloat scrollViewDifference = 0;

if([notification.name isEqualToString:UIKeyboardWillShowNotification])
{
	labelDifference = CGRectGetMaxY(label.frame) - CGRectGetMinY(endSelfViewFrame);
	scrollViewDifference = CGRectGetMaxY(scrollView.frame) - CGRectGetMinY(endSelfViewFrame);
}

And finally, use our result. You will notice I’ve got part in an animation block and the other outside. This will animate the label and scroll bar but not the content, this combination works the best.

const UIEdgeInsets scrollInsets = UIEdgeInsetsMake(0, 0, scrollViewDifference, 0);

[scrollView setContentInset:scrollInsets];

[UIView animateWithDuration:duration animations:^{
	[label setTransform:CGAffineTransformMakeTranslation(0, -labelDifference)];
	[scrollView setScrollIndicatorInsets:scrollInsets];
}];

Now our complete keyboardWillAnimate: method is as follows:

- (void) keyboardWillAnimate:(NSNotification*) notification
{
	NSLog(@"%@", notification);

	NSDictionary* userInfo = notification.userInfo;

	const NSTimeInterval duration = [[userInfo valueForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
	const CGRect endFrame = [[userInfo valueForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];

	UIWindow* mainWindow = [[UIApplication sharedApplication].delegate window];

	//	Keyboard end frame relative to our view
	const CGRect endSelfViewFrame = [mainWindow convertRect:endFrame toView:self.view];

	CGFloat labelDifference = 0;
	CGFloat scrollViewDifference = 0;

	if([notification.name isEqualToString:UIKeyboardWillShowNotification])
	{
		labelDifference = CGRectGetMaxY(label.frame) - CGRectGetMinY(endSelfViewFrame);
		scrollViewDifference = CGRectGetMaxY(scrollView.frame) - CGRectGetMinY(endSelfViewFrame);
	}

	const UIEdgeInsets scrollInsets = UIEdgeInsetsMake(0, 0, scrollViewDifference, 0);

	[scrollView setContentInset:scrollInsets];

	[UIView animateWithDuration:duration animations:^{
		[label setTransform:CGAffineTransformMakeTranslation(0, -labelDifference)];
		[scrollView setScrollIndicatorInsets:scrollInsets];
	}];
}

Run the project again. Now when the keyboard is presented, dismissed or rotated the scroll view and label both adjust so the user can always see them. Also the same desired effect when run on an iPad with a completely different keyboard height.

Download the project end point if needed.

Recommended Posts

Yammer integrations in ReactJS

Post by 4 years ago

I am writing this blog while I am working on a project for our client’s intranet website. The client requires the website has the ability to share, like and write comments in the website through

Got an idea?

We help entrepreneurs, organizations and established brands from around
the country bring ideas to life. We would love to hear from you!