Present ViewController modally shows weird animation - ios

I'm trying to create a simple modal view controller that lets you edit text using a text view. However, when I present the view controller modally, it slides in from the bottom left direction instead of just sliding in from the bottom.
Here's a video of the weird effect: http://youtu.be/9M_MHA5mt1M
My controller simply watches for the keyboard to show and then resizes the text view using auto layout appropriately. Here's the code:
#import "TextPicker.h"
#interface TextPicker ()
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *keyboardHeight;
#end
#implementation TextPicker
- (id)initWithText:(NSString *)text
{
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard_iPhone" bundle:nil];
self = [storyboard instantiateViewControllerWithIdentifier:#"textPicker"];
if (self) {
self.text = text;
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self observeKeyboard];
//self.textView.text = self.text;
[self.textView becomeFirstResponder];
}
- (void) viewWillDisappear:(BOOL)animated {
[self.textView resignFirstResponder];
}
- (IBAction)savePressed:(id)sender {
[self dismissViewControllerAnimated:YES completion:nil];
}
- (IBAction)cancelPressed:(id)sender {
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
- (void) dealloc {
[self stopObervingKeyboard];
}
- (void)observeKeyboard {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow:) name:UIKeyboardWillChangeFrameNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
}
- (void)stopObervingKeyboard {
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillChangeFrameNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}
- (void)keyboardWillShow:(NSNotification *)notification {
NSDictionary *info = [notification userInfo];
NSValue *kbFrame = [info objectForKey:UIKeyboardFrameEndUserInfoKey];
CGRect keyboardFrame = [kbFrame CGRectValue];
self.keyboardHeight.constant = -keyboardFrame.size.height;
NSTimeInterval animationDuration = [[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
[UIView animateWithDuration:animationDuration animations:^{
[self.view layoutIfNeeded];
}];
}
- (void)keyboardWillHide:(NSNotification *)notification {
NSDictionary *info = [notification userInfo];
NSTimeInterval animationDuration = [[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
self.keyboardHeight.constant = 0;
[UIView animateWithDuration:animationDuration animations:^{
[self.view layoutIfNeeded];
}];
}
- (IBAction)dismissKeyboard:(id)sender {
[self.textView resignFirstResponder];
}
#end

Your view is animating as you have asked it to by wrapping the [self.view layoutIfNeeded] call inside an animation block.
In viewDidLoad you begin observing keyboard changes, and when you detect them you animate the adjustments, this is normally correct. But then, before the view does its first layout, you show the keyboard; this results in an animation for all the views from CGRectZero to their proper sizes. And this is the effect you are seeing.
So basically you need to give the view a chance to layout before your animated layoutIfNeeded call. Probably the easiest way to do this is simply to move [self.textView becomeFirstResponder]; to either viewWillAppear: or viewDidAppear:.
*As a side note, remember to call super in appearance calls. I noticed you did not call [super viewWillDisappear];.

Related

UIKeyboardWillChangeFrameNotification doesn't get fired while dictation (Speech to text) button pressed

I am developing an app with a chat functionality, so my requirement is to show the textfield on top of keyboard. As it was required to not use scroll view I have taken a textfield on a UIView and I am using keyborad notifications (UIKeyboardWillChangeFrameNotification and UIKeyboardWillHideNotification) to scroll textfield accordingly.
I am facing an issue for iPhone x device when "predictive text" is on and user press dictation button, there will be a gap between textfield and keyboard as dictation doesn't need "predictive text".
I am trying to detect a keyboard height via UIKeyboardWillChangeFrameNotification but it doesn't get fire while dictation button get pressed.
Adding my sample code below.
#interface ViewController ()
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *bottomConstraints;
#property (weak, nonatomic) IBOutlet UITextField *chatTextField;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardFrameChange:) name:UIKeyboardWillChangeFrameNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keywindowResign:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:YES];
}
- (void)keyboardFrameChange:(NSNotification *)notification
{
NSDictionary * userInfo = [notification userInfo];
CGRect keyboardRect = [userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
double duration = [userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
// Anchor the bottom of the message view to the top of the keyboard.
[UIView animateWithDuration:duration animations:^{
self.bottomConstraints.constant = 0 - CGRectGetHeight(keyboardRect) + self.view.safeAreaInsets.bottom;
[self.view layoutIfNeeded];
}];
}
- (void)keyboardWillHide:(NSNotification *)notification
{
NSDictionary * userInfo = [notification userInfo];
double duration = [userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
// Anchor the bottom of the message view to the bottom of the screen.
[UIView animateWithDuration:duration animations:^{
self.bottomConstraints.constant = 0;
[self.view layoutIfNeeded];
}];
}
- (void)keywindowResign:(NSNotification *)notification
{
NSLog(#"%s","Key window resign");
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self.view endEditing:true];
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
Please check screenshots to understand it more.
I just created a quick test project and it seems like the notification is coming through fine on my end when hitting the dictation button. I can only speculate as to what could be going awry for you since you didn't include any of your code at all, but a couple of guesses are:
The notification is coming through, but the calculations adjusting for the keyboard height aren't correct (perhaps not taking into account the safeAreaInsets.bottom?)
You're removing yourself as an observer of the keyboard notifications at some point before the notification for the keyboard frame change for dictation comes through.
Here's my sample code that worked for me:
#import "ViewController.h"
#interface ViewController ()
#property (strong, nullable) IBOutlet UITextField *tf;
#property (strong, nullable) IBOutlet UIView *redView;
#property (strong, nullable) IBOutlet NSLayoutConstraint *redViewBottomConstraint;
- (IBAction)resignActiveOnTF:(id)sender;
- (void)keyboardWillChangeFrame:(NSNotification *)notification;
- (void)keyboardWillHide:(NSNotification *)notification;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
// put the redView off the bottom of the screen initially (include safe area insets on the bottom
self.redViewBottomConstraint.constant = -(self.redView.frame.size.height + self.view.safeAreaInsets.bottom);
}
- (void)keyboardWillChangeFrame:(NSNotification *)notification {
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
CGFloat keyboardHeight = [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size.height;
[self.view layoutIfNeeded];
// adjust the constraint by the keyboard height and account for the safe area insets on the bottom as well
self.redViewBottomConstraint.constant = keyboardHeight - self.view.safeAreaInsets.bottom;
[UIView animateWithDuration:0.4f animations:^{
self.redView.alpha = 1.0f;
[self.view layoutIfNeeded];
}];
}];
}
- (void)keyboardWillHide:(NSNotification *)notification {
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self.view layoutIfNeeded];
self.redViewBottomConstraint.constant = -(self.redView.frame.size.height + self.view.safeAreaInsets.bottom);
[UIView animateWithDuration:0.25f animations:^{
self.redView.alpha = 0.0f;
[self.view layoutIfNeeded];
}];
}];
}
- (IBAction)resignActiveOnTF:(id)sender {
[self.tf resignFirstResponder];
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
#end
And an example of it an action on simulator:
If none of this is helpful for you, i'd highly recommend trying to reproduce this in a very simple example and post your code so others can help.
Edit:
I copied your sample code into a new view controller, and it seems to be working fine for me. I did make one minor modification on my end for the constraint constant calculation (my bottom constraint is the bottom of the view that's going over the keyboard down to the bottom of the safe area):
- (void)keyboardFrameChange:(NSNotification *)notification
{
NSDictionary * userInfo = [notification userInfo];
CGRect keyboardRect = [userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
double duration = [userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
// Anchor the bottom of the message view to the top of the keyboard.
[UIView animateWithDuration:duration animations:^{
self.bottomConstraints.constant = (CGRectGetHeight(keyboardRect) - self.view.safeAreaInsets.bottom);
[self.view layoutIfNeeded];
}];
}
although I'm assuming yours is a different constraint in your xib?
I've tried running this directly on an iPhone 6s and it works there as well. I don't have an iPhone X physical device handy to test it on.

Moving the view up (without the navigation bar) if keyboard hides ANY element and not only a text field

I need a way to be able to check if the keyboard when it shows up hides any element in the view. If so, i need the view to move up in a way that the element is shown but without the navigation bar moving.
Thanks in advance
#import "RequestViewController.h"
#define kOFFSET_FOR_KEYBOARD 80.0
#interface RequestViewController ()
#end
#implementation RequestViewController{
CGFloat keyboardHeight;
}
#synthesize descirptionTextView;
#synthesize scrollView;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view
descirptionTextView.text = #"Comment";
descirptionTextView.textColor = [UIColor lightGrayColor];
descirptionTextView.delegate = self;
descirptionTextView.layer.cornerRadius = 8;
// border
[descirptionTextView.layer setBorderColor:[UIColor lightGrayColor].CGColor];
[descirptionTextView.layer setBorderWidth:0.5f];
// drop shadow
[descirptionTextView.layer setShadowColor:[UIColor blackColor].CGColor];
[descirptionTextView.layer setShadowOpacity:0.8];
[descirptionTextView.layer setShadowRadius:3.0];
[descirptionTextView.layer setShadowOffset:CGSizeMake(2.0, 2.0)];
// register for keyboard notifications
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillHide)
name:UIKeyboardWillHideNotification
object:nil];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (BOOL) textViewShouldBeginEditing:(UITextView *)textView
{
descirptionTextView.text = #"";
descirptionTextView.textColor = [UIColor blackColor];
return YES;
}
-(void) textViewDidChange:(UITextView *)textView
{
if(descirptionTextView.text.length == 0){
descirptionTextView.textColor = [UIColor lightGrayColor];
descirptionTextView.text = #"Comment";
[descirptionTextView resignFirstResponder];
}
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
#pragma mark - Scrolling out of keyboard way
-(void)keyboardWillShow:(NSNotification *)nsNotification{
//first, get height of keyboard
NSDictionary *userInfo = [nsNotification userInfo];
CGRect kbRect = [[userInfo objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue];
keyboardHeight = kbRect.size.height;
scrollView.frame = CGRectMake(scrollView.frame.origin.x, scrollView.frame.origin.y, scrollView.frame.size.width, self.view.frame.size.height - keyboardHeight - scrollView.frame.origin.y);
return;
}
-(void)keyboardWillHide{
scrollView.frame = CGRectMake(scrollView.frame.origin.x, scrollView.frame.origin.y, scrollView.frame.size.width, scrollView.frame.size.height + keyboardHeight - 40 - 40 - 14 + scrollView.frame.origin.y);
return;
}
You can redraw your views in the keyboard delegate methods:
keyboardWillShow
keyboardWillHide
Declare a CGFloat property named keyboardHeight.
In your viewDidLoad method:
// register for keyboard notifications
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillHide)
name:UIKeyboardWillHideNotification
object:nil];
Keyboard methods:
-(void)keyboardWillShow:(NSNotification *)nsNotification{
//first, get height of keyboard
NSDictionary *userInfo = [nsNotification userInfo];
CGRect kbRect = [[userInfo objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue];
keyboardHeight = kbRect.size.height;
scrollView.frame = CGRectMake(scrollView.frame.origin.x, scrollView.frame.origin.y, scrollView.frame.size.width, self.view.frame.size.height - keyboardHeight - scrollView.frame.origin.y);
return;
}
-(void)keyboardWillHide{
scrollView.frame = CGRectMake(scrollView.frame.origin.x, scrollView.frame.origin.y, scrollView.frame.size.width, scrollView.frame.size.height + keyboardHeight - 40 - 40 - 14 + scrollView.frame.origin.y);
return;
}
You should also be able to substitute scrollView with self.view
When the keyboard appears, you are reducing the visible area by a considerable amount, so unless you have a layout that can be re-run with the smaller area, what you really desire is the ability to scroll.
In general, you'll want to choose where you scroll to based on which field is the firstResponder. This will guarantee that a user is never editing a field that they cannot see.
Revise your view hierarchy for this controller to be contained within a UIScrollView. Also, track which field is the firstResponder in an instance variable. Then, respond to keyboard notifications like this:
- (void)keyboardWillShow:(NSNotification*)notification
{
NSValue* keyboardFrameValue = [notification.userInfo objectForKey:UIKeyboardFrameEndUserInfoKey];
CGRect kbRect = [self.view convertRect:keyboardFrameValue.CGRectValue fromView:nil];
CGRect overlap = CGRectIntersection(self.view.bounds, kbRect);
self.scroller.contentInset = UIEdgeInsetsMake(self.scroller.contentInset.top, 0, overlap.size.height, 0);
if (self.firstResponderView)
{
CGRect fieldRect = [self.scroller convertRect:self.firstResponderView.frame fromView:self.firstResponderView.superview];
[self.scroller scrollRectToVisible:fieldRect animated:YES];
}
}
Well, one can use a library like this one: https://github.com/michaeltyson/TPKeyboardAvoiding, or he can do it programmatically. This is how I proceeded:
//adding the notification about when keyboard appears and disappears when the view loads and remove them when the view will disappear
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}
Then you add these selector methods
#pragma mark - keyboard movements
- (void)keyboardWillShow:(NSNotification *)notification
{
CGSize keyboardSize = [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;
if(keyboardSize.height> (self.view.frame.size.height - YourTextField.frame.size.height -YourTextField.frame.origin.y)){
[UIView animateWithDuration:0.3 animations:^{
CGRect f = self.view.frame;
f.origin.y = self.view.frame.size.height - YourTextField.frame.size.height -YourTextField.frame.origin.y-keyboardSize.height - 10;
self.view.frame = f;
}];
}
}
-(void)keyboardWillHide:(NSNotification *)notification
{
[UIView animateWithDuration:0.3 animations:^{
CGRect f = self.view.frame;
f.origin.y = 0.0f;
self.view.frame = f;
}];
}

Shrink UIWebView when keyboard appears

On my iPhone app i have a UIWebView with toolbars above an below it. the toolbar on the bottom contains a text box for the user to input some text. but when i click on the text box, the keyboard covers the bottom half of the screen.
how do i make it so that the toolbars stay above and below the webview but the the height of the webview shrinks for the keyboard to be displayed?
any guidance on this is appreciated.
thanks in advanced.
To shrink webView you need the following:
- (void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
}
- (void) viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}
- (void)keyboardWillShow:(NSNotification *)notification
{
CGRect keyboardFrame = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
keyboardFrame = [self.view convertRect:keyboardFrame fromView:self.view.window];
NSTimeInterval keyboardAnimationDuration = [notification.userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
UIViewAnimationOptions keyboardAnimationCurve = [notification.userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue] << 16;
CGFloat keyboardHeight = keyboardFrame.size.height;
[UIView animateWithDuration:keyboardAnimationDuration delay:0 options:keyboardAnimationCurve
animations:^{
_webView.contentInset = UIEdgeInsetsMake(0, 0, keyboardHeight, 0)
}
completion:NULL];
}
- (void)keyboardWillHide:(NSNotification *)notification
{
NSTimeInterval keyboardAnimationDuration = [notification.userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
UIViewAnimationOptions keyboardAnimationCurve = [notification.userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue] << 16;
[UIView animateWithDuration:keyboardAnimationDuration delay:0 options:keyboardAnimationCurve
animations:^{
_webView.contentInset = UIEdgeInsetsZero;
}
completion:NULL];
}
In the case of additional subviews you should slightly change this code (add change of the frames for other subview)
You can implement UITextFieldDelegate in your UIViewController and set the UITextField delegate value to your controller and then implement textFieldDidBeginEditing and textFieldDidEndEditing methods in your controller to detect when editing starts/ends.
- (void)textFieldDidBeginEditing:(UITextField *)textField{
// in case you have more than one text fields in the same view
if(textField == self.YOUR_FIELD_NAME)
// change the web view height here, you can also animate it using UIView beginAnimations
CGRect frame = self.webView.frame;
frame.size.height = 200;
self.webView.frame = frame;
}
- (void)textFieldDidEndEditing:(UITextField *)textField{
// do the opposite here
}

Trying to move the view up when the keyboard is showing

I trying to push my view up when the keyboard is showing (it overlays data I want the user to see while he's typing. I'm using this code:
KBKeyboardHandler.h:
#protocol KBKeyboardHandlerDelegate;
#interface KBKeyboardHandler : NSObject
- (id)init;
// Put 'weak' instead of 'assign' if you use ARC
#property(nonatomic, assign) id<KBKeyboardHandlerDelegate> delegate;
#property(nonatomic) CGRect frame;
#end
KBKeyboardHandler.m:
#import "KBKeyboardHandler.h"
#import "KBKeyboardHandlerDelegate.h"
#implementation KBKeyboardHandler
- (id)init
{
self = [super init];
if (self)
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification
object:nil];
}
return self;
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
[super dealloc];
}
#synthesize delegate;
#synthesize frame;
- (void)keyboardWillShow:(NSNotification *)notification
{
CGRect oldFrame = self.frame;
[self retrieveFrameFromNotification:notification];
if (oldFrame.size.height != self.frame.size.height)
{
CGSize delta = CGSizeMake(self.frame.size.width - oldFrame.size.width,
self.frame.size.height - oldFrame.size.height);
if (self.delegate)
[self notifySizeChanged:delta notification:notification];
}
}
- (void)keyboardWillHide:(NSNotification *)notification
{
if (self.frame.size.height > 0.0)
{
[self retrieveFrameFromNotification:notification];
CGSize delta = CGSizeMake(-self.frame.size.width, -self.frame.size.height);
if (self.delegate)
[self notifySizeChanged:delta notification:notification];
}
self.frame = CGRectZero;
}
- (void)retrieveFrameFromNotification:(NSNotification *)notification
{
CGRect keyboardRect;
[[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardRect];
self.frame = [[UIApplication sharedApplication].keyWindow.rootViewController.view convertRect:keyboardRect fromView:nil];
}
- (void)notifySizeChanged:(CGSize)delta notification:(NSNotification *)notification
{
NSDictionary *info = [notification userInfo];
UIViewAnimationCurve curve;
[[info objectForKey:UIKeyboardAnimationCurveUserInfoKey] getValue:&curve];
NSTimeInterval duration;
[[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] getValue:&duration];
void (^action)(void) = ^{
[self.delegate keyboardSizeChanged:delta];
};
[UIView animateWithDuration:duration
delay:0.0
options:curve
animations:action
completion:nil];
}
#end
KBKeyboardHandlerDelegate.h:
#protocol KBKeyboardHandlerDelegate
- (void)keyboardSizeChanged:(CGSize)delta;
#end
Sample MyViewController.h:
#interface MyViewController : UIViewController<KBKeyboardHandlerDelegate>
...
#end
Sample MyViewController.m:
#implementation MyViewController
{
KBKeyboardHandler *keyboard;
}
- (void)dealloc
{
keyboard.delegate = nil;
[keyboard release];
[super dealloc];
}
- (void)viewDidLoad
{
[super viewDidLoad];
keyboard = [[KBKeyboardHandler alloc] init];
keyboard.delegate = self;
}
- (void)viewDidUnload
{
[super viewDidUnload];
keyboard.delegate = nil;
[keyboard release];
keyboard = nil;
}
- (void)keyboardSizeChanged:(CGSize)delta
{
// Resize / reposition your views here. All actions performed here
// will appear animated.
// delta is the difference between the previous size of the keyboard
// and the new one.
// For instance when the keyboard is shown,
// delta may has width=768, height=264,
// when the keyboard is hidden: width=-768, height=-264.
// Use keyboard.frame.size to get the real keyboard size.
// Sample:
CGRect frame = self.view.frame;
frame.size.height -= delta.height;
self.view.frame = frame;
}
That I've found here. I'm trying to figure out why my view isn't pushing up. The keyboard is showing and keyboardSizeChanged is fired up but the view doesn't move. I opened a new project and copied the KBKeyboardHandler and delegate files to it, implemented the code and in the new project it works fine so I know for a fact that it's something in my original project that is messed up. Any idea what it can be?
If you are not using a UITableView use UIScrollView as the parent view of your elements and add the code below
Adjust as needed,
You will need a NSArray of NSObjects, I called mine scrollToObjects and in viewDidLoad add the elements to the array that would need scrolling so that when keyboardWasShown is called you can check to see if the currently selected element should be scrolled to.
I used self.activeField to store my currently selected element which is set when a element triggers an event(the event needs to be one of the first called and that is always called)
Then I check to see if the scrollToObjects contains activeField and if it does scroll the UIScrollView up and then back down when the activeField resignsFirstResponder
// Call this method somewhere in your view controller setup code.
- (void)registerForKeyboardNotifications
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWasShown:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillBeHidden:) name:UIKeyboardWillHideNotification object:nil];
}
// Called when the UIKeyboardDidShowNotification is sent.
- (void)keyboardWasShown:(NSNotification*)aNotification
{
NSDictionary* info = [aNotification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0);
self.scrollView.contentInset = contentInsets;
self.scrollView.scrollIndicatorInsets = contentInsets;
// If active text field is hidden by keyboard, scroll it so it's visible
// Your application might not need or want this behavior.
CGRect aRect = self.view.frame;
aRect.size.height -= kbSize.height;
// Wouldn't go true so used an array that contains text fields that need to be scrolled to
// if (!CGRectContainsPoint(aRect, self.activeField.frame.origin) ) {
// CGPoint scrollPoint = CGPointMake(0.0, self.activeField.frame.origin.y- kbSize.height);
// [self.scrollView setContentOffset:scrollPoint animated:YES];
// }
if ([self.scrollToObjects containsObject:self.activeField]) {
CGPoint scrollPoint = CGPointMake(0.0, self.activeField.frame.origin.y+ (self.activeField.frame.size.height*2)-kbSize.height);
[self.scrollView setContentOffset:scrollPoint animated:YES];
}
}
// Called when the UIKeyboardWillHideNotification is sent
- (void)keyboardWillBeHidden:(NSNotification*)aNotification
{
// self.scrollView.contentInset = UIEdgeInsetsZero;
[self.scrollView setContentOffset:CGPointMake(0.0, 0.0) animated:YES];
self.scrollView.scrollIndicatorInsets = UIEdgeInsetsZero;
}
A simpler solution would be to handle the UITextFieldDelegate methods below with following code or something similar to activation and deactivation of the object
-(void)textFieldDidBeginEditing:(UITextField *)textField
{
CGPoint scrollPoint = CGPointMake(0.0, textField.frame.origin.y);
[self.theScrollView setContentOffset:scrollPoint animated:YES];
}
-(void)textFieldDidEndEditing:(UITextField *)textField
{
[self.theScrollView setContentOffset:CGPointMake(0.0, 0.0) animated:YES];
self.theScrollView.scrollIndicatorInsets = UIEdgeInsetsZero;
}
I'd check your calculation for delta.
CGSize delta = CGSizeMake(self.frame.size.width - oldFrame.size.width,
self.frame.size.height - oldFrame.size.height);
My guess is oldFrame and self.frame might have the same dimensions.
I'm trying to think if there's anything else that could have been missed, like properly linking views to these objects or something in the interface editor?
The problem is in keyboardSizeChanged: - what you want to adjust is frame.origin.y, not frame.size.height. At least that is what I wanted it to do.
You can also try another library, it's called TPKeyboardAvoiding. It is working great, and is very easy to setup:
Add a UIScrollView into your view controller's xib
Set the scroll view's class to TPKeyboardAvoidingScrollView (still
in the xib, via the identity inspector)
Place all your controls within that scrollview
It also automatically hooks up "Next" buttons on the keyboard to switch through the text fields.

UITextView doesn't not resize when keyboard appear if loaded from a tab bar cotroller

I have a simple view controller (SecondViewController) used to manage a UITextview
(I'm building a simple editor)
this is the code of the SecondViewController.h
#interface SecondViewController : UIViewController {
IBOutlet UITextView *textView;
}
#property (nonatomic,retain) IBOutlet UITextView *textView;
#end
and this is the SecondViewController.m
//
// EditorViewController.m
// Editor
//
// Created by elio d'antoni on 13/01/11.
// Copyright 2011 none. All rights reserved.
//
#implementation SecondViewController
#synthesize textView;
/*
// Implement loadView to create a view hierarchy programmatically, without using a nib.
- (void)loadView {
}
*/
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"uiViewBg.png"]];
textView.layer.borderWidth=1;
textView.layer.cornerRadius=5;
textView.layer.borderColor=[[UIColor darkGrayColor] CGColor];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillAppear:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillDisappear:) name:UIKeyboardWillHideNotification object:nil];
}
-(void) matchAnimationTo:(NSDictionary *) userInfo {
NSLog(#"match animation method");
[UIView setAnimationDuration:[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]];
[UIView setAnimationCurve:[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]];
}
-(CGFloat) keyboardEndingFrameHeight:(NSDictionary *) userInfo {
NSLog(#"keyboardEndingFrameHeight method");
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 {
NSLog(#"adjust method");
return CGRectMake(20,
57,
self.textView.frame.size.width,
self.textView.frame.size.height + change * direction);
}
-(void)keyboardWillAppear:(NSNotification *)notification {
NSLog(#"keyboard appear");
[UIView beginAnimations:nil context:NULL];
[self matchAnimationTo:[notification userInfo]];
self.textView.frame =
[self adjustFrameHeightBy:[self keyboardEndingFrameHeight:
[notification userInfo]]
multipliedBy:-1];
[UIView commitAnimations];
}
-(void)keyboardWillDisappear:(NSNotification *) notification {
NSLog(#"keyboard disappear");
[UIView beginAnimations:nil context:NULL];
[self matchAnimationTo:[notification userInfo]];
self.textView.frame =
[self adjustFrameHeightBy:[self keyboardEndingFrameHeight:
[notification userInfo]]
multipliedBy:1];
[UIView commitAnimations];
}
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
(void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
(void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
(void)dealloc {
[super dealloc];
}
#end
the problem is that if load the view controller from a tab bar controller the textView doesn't resize when the keyboard appear, but the SAME code works if loaded as a single view based app. I hope I was clear enough.
I used the tabBar template provided by xcode no modifications.
Your code looks right, did notice you are not releasing "textView" in dealloc and "viewDidUnload" but this should make any difference.
I'd be checking the notications are received and everthing is wired up ie textView is not nil

Resources