0

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.

halfer
  • 19,824
  • 17
  • 99
  • 186
Josh Kahane
  • 16,765
  • 45
  • 140
  • 253
  • (void)textFieldDidBeginEditing:(UITextField *)textField used this delegate method it will solve your problem. – jamil Feb 24 '13 at 16:00

2 Answers2

2

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
ipmcc
  • 29,581
  • 5
  • 84
  • 147
  • I've given this a try without luck, check my question, I've made an edit to explain what I tried. – Josh Kahane Feb 24 '13 at 15:49
  • For starters, you don't want to send the notification, you want to move your scrolling logic out of the notification handler and into a method that's called by both the notification handler and `textFieldDidBeginEditing`. A notification you send (as opposed to UIKit sending it) won't have the right userInfo or source object, and you specifically use the userInfo to determine your scrolling action.) You may need to stash the keyboard frame in an ivar when it first appears (and clear it out when it goes away). – ipmcc Feb 24 '13 at 16:01
  • That did the trick! As well as storing the keyboard frame, I had to store the view offset, so if I tap onto a second textfield with the keyboard still present, I can deduct the old offset. – Josh Kahane Feb 24 '13 at 18:41
0

I like to use UITableViewController subclass in situations like you have. Just build your view using UITableView headers footers and cells. When you'll need to make a text edit inside UITextView or Field UITableViewController will layout everything automatically.

It will work even you'll set scrollEnabled property to NO;

iiFreeman
  • 5,165
  • 2
  • 29
  • 42