Archive for the ‘Cocoa’ Category

Resize UIView/UITableView when Keyboard displays

Tuesday, June 2nd, 2009

I was having a hell of a time trying to get my UITableView to resize itself after the iPhone keyboard displayed itself. After being just a little surprised that the iPhone doesn’t resize the underlying UIView for free I figured it was up to me to do resize.

Firstly add a few variables and method declares into your ViewController.h header file:

Boolean keyboardIsShowing;
CGRect keyboardBounds;
- (void)resizeViewControllerToFitScreen;

Now we need to register for the UIKeyboardWillShowNotification and the UIKeyboardWillHideNotification:

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

And these notifications need somewhere to go:

#pragma mark -
#pragma mark Keyboard Handling
 
- (void)keyboardWillShow:(NSNotification *)notification {
	NSDictionary *userInfo = [notification userInfo];
	NSValue *keyboardBoundsValue = [userInfo objectForKey:UIKeyboardBoundsUserInfoKey];
	[keyboardBoundsValue getValue:&keyboardBounds];
	keyboardIsShowing = YES;
	[self resizeViewControllerToFitScreen];
}
 
- (void)keyboardWillHide:(NSNotification *)note {
	keyboardIsShowing = NO;
	keyboardBounds = CGRectMake(0, 0, 0, 0);
	[self resizeViewControllerToFitScreen];
}

And now add the magic method to resize the view:

- (void)resizeViewControllerToFitScreen {
	// Needs adjustment for portrait orientation!
	CGRect applicationFrame = [[UIScreen mainScreen] applicationFrame];
	CGRect frame = self.view.frame;
	frame.size.height = applicationFrame.size.height;
 
	if (keyboardIsShowing)
		frame.size.height -= keyboardBounds.size.height;
 
	[UIView beginAnimations:nil context:NULL];
	[UIView setAnimationBeginsFromCurrentState:YES];
	[UIView setAnimationDuration:0.3f];
	self.view.frame = frame;
	[UIView commitAnimations];
}

And super importantly de-register the ViewController from those notifications.

- (void)viewWillDisappear:(BOOL)animated {
	[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
	[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}

See how this all comes together?

1. We register for Notifications when the Keyboard shows/hides,
2. We react when the keyboard is shown/hidden,
3. The resizeViewControllerToFitScreen method handles our resize, including animating the underlying view so it looks pretty.

There’s a few caveats:

1. I’ve not tested on Landscape mode, I’m pretty sure this will fail.
2. UIKeyboardWillShowNotification can get fired every time you enter a textbox (as I’ve only one textbox it’s not a problem for me). So you might need to look at using …TextDidBeginEditing/…TextDidEndEditing or maintaining state differently so that the view isn’t jumping all over the place.

Core Animation CAConstraint Grid-Cell Layout

Tuesday, February 3rd, 2009

I really wanted to get a nice Grid Layout using CAConstraint, but I found the situation getting very complex very fast as each Cell needed to be able to reference the location of the last Cell and whether or not the Cell needed to drop down to a new Row, and at times needed to also reference the @”superlayer”. There has to be a better way, right?

CAConstraint Grid-Cell Layout with Animations

After a little digging around I was able to find an amazing Hot Chocolate blog post which showed a fantastic trick when laying out the Cells without having to reference the prior Cell. This approach takes advantage of knowing the Cell Row/Column position plus using the Scale: attribute of the CAConstraint on the @”superlayer” and it works like a charm.

[CAConstraint constraintWithAttribute: kCAConstraintWidth
relativeTo: @"superlayer"
attribute: kCAConstraintWidth
scale: 1.0 / columns
offset: 0]];

Want to change the number of Cells in the Grid? Modify these variables in this method:

- (void)layoutCellsInGridLayer:(CALayer *)layer {
	int columns = 6;
	int rows = 6;...

Also to give the project a little bit of “wow” here’s some random Y rotation on a Cell Layer:

- (void)setupFlipAnimationOnLayer:(CALayer *)layer {
	float duration = (float)frandom(0.5, 5.0);
 
	CABasicAnimation* animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.y"];
	animation.fromValue = [NSNumber numberWithDouble:-1.0f * M_PI];
	animation.toValue = [NSNumber numberWithDouble:1.0f * M_PI];
	animation.duration = duration;
	animation.repeatCount = 1e100f;
	animation.beginTime = CACurrentMediaTime() + frandom(0.1, 30);
	animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
 
	[layer addAnimation:animation forKey:@"rotationY"];
}

To get the nice 3D perspective look the project needs the following code:

- (void)setupPerspectiveWithX:(float)x andY:(float)y {
	CATransform3D transform = CATransform3DMakeRotation(x, 0, 1, 0);
	transform = CATransform3DRotate(transform, y, 1, 0, 0);
	float zDistance = -450;
	transform.m34 = 1.0 / -zDistance;
	gridLayer.sublayerTransform = transform;
}

And one last final note: take a look in createCellInParentLayer: and you will see layer.contents. If you can provide your own image the layer will automatically Fill (layer.contentsGravity = kCAGravityResizeAspectFill;) and Mask (layer.masksToBounds = YES;) to make it fit.

You can grab the code here or download the binary here (requires Leopard).

Why is CAConstraint not working?

Monday, February 2nd, 2009

So you’re having problems with CAConstraint? It seems like no matter what you do the Constraint isn’t being applied to the Sublayer? And you’re CALayers are appearing in weird positions? Or not appearing at all?

Well the answer for me is surprisingly simple (and embarrassing)

1
2
3
4
[sublayer addConstraint:
[CAConstraint constraintWithAttribute:kCAConstraintMidX 
relativeTo:@"superlayer"
attribute:kCAConstraintMidX]]

Now see that that @”superlayer”? Notice that it’s not a Uppercase “L” as I was using “superLayer” where upon Core Animation was unable to find the layer “superLayer” and therefore have nothing to do with the constraints.