Search code examples
iphoneobjective-cipaduitextfielduikeyboard

Adjust view for keyboard when switching UITextField?


I have the ability for my view to slide up when the keyboard appears in my app so the text field can be seen and it worked just fine. However, because its based on keyboard notifications it only works when the keyboard appears.

Meaning, I select a textfield, the keyboard appears and the view slides up accordingly, but if I then tap directly onto another textfield the view doesn't adjust because the keyboard is already present.

Here is the code I am using:

-(void)registerForKeyboardNotifications
{
    NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
    [center addObserver:self selector:@selector(keyboardDidShow:) name:UIKeyboardDidShowNotification object:nil];
    [center addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
    
    // add a tap gesture to drop first responder
    UITapGestureRecognizer *tapGR = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapToHideKeyboard:)];
    [self.view addGestureRecognizer:tapGR];
}

-(void)keyboardDidShow:(NSNotification *)notification
{
    CGRect keyboardFrameW = [[notification.userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
    UIWindow *window = [[UIApplication sharedApplication] keyWindow];
    CGRect keyboardFrame = [window convertRect:keyboardFrameW toView:self.view];
    
    //Have a minimum space between the keyboard and textfield
    CGFloat textFieldBuffer = 40;
    CGFloat textFieldKeyboardDifference = 0;
    
    if (activeTextField.frame.origin.y + activeTextField.frame.size.height > keyboardFrame.origin.y) textFieldKeyboardDifference = (activeTextField.frame.origin.y + activeTextField.frame.size.height + textFieldBuffer) - keyboardFrame.origin.y;
    else if (activeTextField.frame.origin.y + activeTextField.frame.size.height < keyboardFrame.origin.y) textFieldKeyboardDifference = 0;
    
    [self translateView:self.view toRect:CGRectMake(self.view.frame.origin.x, self.view.frame.origin.y - textFieldKeyboardDifference, self.view.frame.size.width, self.view.frame.size.height) withDuration:0.3];
}

-(void)keyboardWillHide:(NSNotification *)notification
{
    //Revert to y origin 0
    [self translateView:self.view toRect:CGRectMake(self.view.frame.origin.x, 0, self.view.frame.size.width, self.view.frame.size.height) withDuration:0.3];
}

Edit

I have tried calling the keyboard notification manually when textFieldDidBeginEditing is called like this:

[self keyboardDidShow:[NSNotification notificationWithName:UIKeyboardDidShowNotification object:nil]] without luck. The method gets called, but no adjustment is made for a reason I can't work out.


Solution

  • What you probably want to do here is provide a delegate to all your UITextFields and implement - (void)textFieldDidBeginEditing:(UITextField *)textField on the delegate to trigger your scrolling action. This will be called any time someone starts editing on a text field, and the text field is passed in as a parameter to the method.

    EDIT: Using your code as a starting point, this is what I came up with. It gets called on every change of text fields:

    @interface SOViewController () <UITextFieldDelegate>
    @property (nonatomic, readwrite, assign) UITextField* activeTextField;
    @property (nonatomic, readwrite, assign) CGRect keyboardFrame;
    @end
    
    @implementation SOViewController
    
    @synthesize activeTextField;
    @synthesize keyboardFrame;
    
    - (void)viewDidLoad
    {
        [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib.
        [self registerForKeyboardNotifications];
        self.keyboardFrame = CGRectNull;
    }
    
    - (void)textFieldDidBeginEditing:(UITextField *)textField
    {
        self.activeTextField = textField;
        [self updatePosition];
    }
    
    - (void)textFieldDidEndEditing:(UITextField *)textField
    {
        self.activeTextField = nil;
    }
    
    -(void)registerForKeyboardNotifications
    {
        NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
        [center addObserver:self selector:@selector(keyboardDidShow:) name:UIKeyboardDidShowNotification object:nil];
        [center addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
    
        //    // add a tap gesture to drop first responder
        //    UITapGestureRecognizer *tapGR = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapToHideKeyboard:)];
        //    [self.view addGestureRecognizer:tapGR];
    }
    
    - (void)translateView: (UIView*)view toRect: (CGRect)rect withDuration: (NSTimeInterval)duration
    {
        NSLog(@"Translating view to rect: %@ overDuration: %g", NSStringFromCGRect(rect), duration);
    }
    
    - (void)updatePosition
    {
        if (self.activeTextField && !CGRectIsNull(self.keyboardFrame))
        {
            UIWindow *window = [[UIApplication sharedApplication] keyWindow];
            CGRect localKeyboardFrame = [window convertRect: self.keyboardFrame toView:self.view];
    
            //Have a minimum space between the keyboard and textfield
            CGFloat textFieldBuffer = 40;
    
            CGRect textFieldFrame = self.activeTextField.frame;
    
            CGRect viewFrame = self.view.frame;
            viewFrame.origin.y = 0;
    
            if (CGRectGetMaxY(textFieldFrame) + textFieldBuffer > CGRectGetMinY(localKeyboardFrame))
            {
                viewFrame.origin.y = -1.0 * (CGRectGetMaxY(textFieldFrame) + textFieldBuffer - CGRectGetMinY(localKeyboardFrame));
            }
    
            [self translateView: self.view toRect: viewFrame withDuration: 0.3];
    
        }
        else
        {
            CGRect viewFrame = self.view.frame;
            viewFrame.origin.y = 0;
            [self translateView: self.view toRect: viewFrame withDuration: 0.3];
        }
    }
    
    -(void)keyboardDidShow:(NSNotification *)notification
    {
        self.keyboardFrame = [[notification.userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
        [self updatePosition];
    }
    
    -(void)keyboardWillHide:(NSNotification *)notification
    {
        self.keyboardFrame = CGRectNull;
        [self updatePosition];
    }
    
    @end