The time has come to, once again, blog over here as part of #idevblogaday.
Remember the project I was telling you last time? I can reveal it now, as it’s been on the AppStore for a while now – Clean Writer for iPad, 2.0, a fully recoded, feature rich redesigned version of my quite popular iPad plain-text editor for writers.
One thing that plagued the previous version was how the keyboard would cover up the text entry textview. I had tried to fix it by automatically adjusting the text view size, but there were always problems.
I ended up with a fix based on a recipe from Pragmatic Programmer’s iPad Programming book.
The original recipe, copy/pasted below, is:
1. In my view controller’s viewDidLoad method:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib....
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillAppear:)
name:UIKeyboardWillShowNotification object:self.view.window];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillDisappear:)
name:UIKeyboardWillHideNotification object:self.view.window];
}
2. the keyboard notification handlers:
-(void) matchAnimationTo:(NSDictionary *) userInfo {
[UIView setAnimationDuration: [[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]];
[UIView setAnimationCurve: [[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]];
}
-(CGFloat) keyboardEndingFrameHeight:(NSDictionary *) userInfo {
CGRect keyboardEndingUncorrectedFrame = [[ userInfo objectForKey:UIKeyboardFrameEndUserInfoKey ] CGRectValue];
CGRect keyboardEndingFrame = [self.view convertRect:keyboardEndingUncorrectedFrame fromView:nil];
return keyboardEndingFrame.size.height;
}
-(CGRect) adjustFrameHeightBy:(CGFloat) change multipliedBy:(NSInteger) direction {
CGRect r = CGRectMake(mainView.frame.origin.x, mainView.frame.origin.y, mainView.frame.size.width, mainView.frame.size.height + change * direction);
return r;
}
-(void)keyboardWillAppear:(NSNotification *)notification {
[UIView beginAnimations:nil context:NULL];
[self matchAnimationTo:[notification userInfo]];
mainView.frame = [self adjustFrameHeightBy:[self keyboardEndingFrameHeight: [notification userInfo]] multipliedBy:-1];
[UIView commitAnimations];
}
-(void)keyboardWillDisappear:(NSNotification *) notification {
[UIView beginAnimations:nil context:NULL];
[self matchAnimationTo:[notification userInfo]];
mainView.frame = [self adjustFrameHeightBy:[self keyboardEndingFrameHeight: [notification userInfo]] multipliedBy:1];
[UIView commitAnimations];
}
Now, the problem with the above code(as I discovered) is that on iOS5, on the iPad, something else can happen other than the keyboard appearing or disappearing. You can now Split or Undock your keyboard. In this case, the keyboard will receive a UIKeyboardWillHideNotification but it won’t receive any UIKeyboardWillShowNotification one, receiving UIKeyboardWillChangeFrameNotification instead.
To sum up, in iOS5 you might get more UIKeyboardWillHideNotification than UIKeyboardWillShowNotification, so it will mess up the pairing described above, as we are only interested in the pairs of keyboardWillShow/keyboardWillHide events. Since I wanted my textview to adjust size only when the keyboard actually appeared and not when it docked/undocked, I added the following simple fix:
1. add a boolean flag to my controller:
BOOL keyboard_needs_adjustment_status; // initialized to NO
2. change keyboardWillAppear and keyboardWillDisappear methods to to:
-(void)keyboardWillAppear:(NSNotification *)notification {
[UIView beginAnimations:nil context:NULL];
[self matchAnimationTo:[notification userInfo]];
mainView.frame = [self adjustFrameHeightBy:[self keyboardEndingFrameHeight: [notification userInfo]] multipliedBy:-1];
[UIView commitAnimations];
keyboard_needs_adjustment_status = YES;
}
-(void)keyboardWillDisappear:(NSNotification *) notification {
if (keyboard_needs_adjustment_status) {
[UIView beginAnimations:nil context:NULL];
[self matchAnimationTo:[notification userInfo]];
mainView.frame = [self adjustFrameHeightBy:[self keyboardEndingFrameHeight: [notification userInfo]] multipliedBy:1];
[UIView commitAnimations];
}
else{
// if kb is undocked or split
}
keyboard_needs_adjustment_status = NO;
}
That was it. A quick bug fix that saved the day for my users. If you want to see it in action and get a great app also, Clean Writer for iPad is a great app with way more to offer, and is on sale in the Productivity category, only $0.99
Happy Holidays, everyone!
