views:

1785

answers:

3

I need help trying to configure in a UIViewController, the keyboard : (a) not hovering over both UITextfields, so the scroller should be positioned correctly; and (b) When the user touches the background the keyboard disappears.

I havent tried (b) yet but im trying (a) and the code I got from googling around didn't give me the desired effects. In-fact, it makes my textboxes dissapear when I touch one of them. Im sure my implementation of activeField is also wrong. I got this example from Apple Development's Keyboard section.

Any help would be appreciated. Thanks

The code I have so far is below:

@interface FirstViewController : UIViewController {
    IBOutlet UITextField *emailAddress;
    IBOutlet UITextField *password;
    IBOutlet UIButton *loginButton;
    IBOutlet UIScrollView *scroller;
    BOOL keyboardShown;
    UITextField *activeField;

    ASIHTTPRequest *requestRequiringAuthentication;
    ASINetworkQueue *networkQueue;
}

- (IBAction) LoginUser:(id)sender;

@property (nonatomic,retain) IBOutlet UITextField *emailAddress;
@property (nonatomic,retain) IBOutlet UITextField *password;
@property (nonatomic, retain) IBOutlet UIScrollView *scroller;
@property (nonatomic, retain) UITextField *activeField;
@property (retain) ASINetworkQueue *networkQueue;
@property (retain) ASIHTTPRequest *requestRequiringAuthentication;

@end


@implementation FirstViewController

@synthesize requestRequiringAuthentication;
@synthesize networkQueue;
@synthesize password;
@synthesize emailAddress;
@synthesize scroller;
@synthesize activeField;

#pragma mark -
#pragma mark LifeCycle

- (void)awakeFromNib
{
    NSLog(@"awaking from nib");
    [self setNetworkQueue:[[[ASINetworkQueue alloc] init] autorelease]];    
}    

- (void)viewDidLoad {
    NSLog(@"viewdidload");
    [super viewDidLoad];
}

- (void)viewDidUnload {
    // Release any retained subviews of the main view.
    // e.g. self.myOutlet = nil;
}

- (void)viewWillAppear:(BOOL)animated {
    keyboardShown = NO;
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWasShown:) name:UIKeyboardDidShowNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWasHidden:) name:UIKeyboardDidHideNotification object:nil];
[super viewWillAppear:animated];
}

- (void)viewWillDisappear:(BOOL)animated {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    [super viewWillDisappear:animated];
}


// Called when the UIKeyboardDidShowNotification is sent.
- (void)keyboardWasShown:(NSNotification*)aNotification
{
    if (keyboardShown)
        return;

    NSDictionary* info = [aNotification userInfo];

    // Get the size of the keyboard.
    NSValue* aValue = [info objectForKey:UIKeyboardBoundsUserInfoKey];
    CGSize keyboardSize = [aValue CGRectValue].size;

    // Resize the scroll view (which is the root view of the window)
    CGRect viewFrame = [scroller frame];
    viewFrame.size.height -= keyboardSize.height;
    scroller.frame = viewFrame;

    // Scroll the active text field into view.
    CGRect textFieldRect = [activeField frame];
    [scroller scrollRectToVisible:textFieldRect animated:YES];

    keyboardShown = YES;
}

// Called when the UIKeyboardDidHideNotification is sent
- (void)keyboardWasHidden:(NSNotification*)aNotification
{
    NSDictionary* info = [aNotification userInfo];

    // Get the size of the keyboard.
    NSValue* aValue = [info objectForKey:UIKeyboardBoundsUserInfoKey];
    CGSize keyboardSize = [aValue CGRectValue].size;

    // Reset the height of the scroll view to its original value
    CGRect viewFrame = [scroller frame];
    viewFrame.size.height += keyboardSize.height;
    scroller.frame = viewFrame;

    keyboardShown = NO;
}

- (void)textFieldDidBeginEditing:(UITextField *)textField
{
    activeField = textField;
}

- (void)textFieldDidEndEditing:(UITextField *)textField
{
    activeField = nil;
}
+2  A: 

I found the following code in a blog entry (I don't know which one because I think I had it saved in NNW before the switch to Google Reader syncing). It all lives in the implementation file, just make your class the delegate for your UITextFields, and declare it as following the UITextFieldDelegate protocol in your header. I pulled this straight out of the code for one of my shipping apps, and it works great for moving the text fields out of the way of the keyboard. I haven't setup your (B) yet, but I believe it's just another UITextFieldDelegate method that needs to be implemented.

Setup the following constants for your class:

static const CGFloat KEYBOARD_ANIMATION_DURATION = 0.3;
static const CGFloat MINIMUM_SCROLL_FRACTION = 0.2;
static const CGFloat MAXIMUM_SCROLL_FRACTION = 0.8;
static const CGFloat PORTRAIT_KEYBOARD_HEIGHT = 216;
static const CGFloat LANDSCAPE_KEYBOARD_HEIGHT = 140;

And then add these two methods:

- (void)textFieldDidBeginEditing:(UITextField *)textField {
   CGRect textFieldRect = [self.view.window convertRect:textField.bounds fromView:textField];
    CGRect viewRect = [self.view.window convertRect:self.view.bounds fromView:self.view];

    CGFloat midline = textFieldRect.origin.y + 0.5 * textFieldRect.size.height;
    CGFloat numerator =
            midline - viewRect.origin.y - MINIMUM_SCROLL_FRACTION * viewRect.size.height;
    CGFloat denominator =
            (MAXIMUM_SCROLL_FRACTION - MINIMUM_SCROLL_FRACTION) * viewRect.size.height;
    CGFloat heightFraction = numerator / denominator;

   if (heightFraction < 0.0)
    {
        heightFraction = 0.0;
    }
    else if (heightFraction > 1.0)
    {
        heightFraction = 1.0;
    }

   UIInterfaceOrientation orientation =
   [[UIApplication sharedApplication] statusBarOrientation];
    if (orientation == UIInterfaceOrientationPortrait ||
        orientation == UIInterfaceOrientationPortraitUpsideDown)
    {
        animatedDistance = floor(PORTRAIT_KEYBOARD_HEIGHT * heightFraction);
    }
    else
    {
        animatedDistance = floor(LANDSCAPE_KEYBOARD_HEIGHT * heightFraction);
    }
    CGFloat heightFraction = numerator / denominator;

   if (heightFraction < 0.0)
    {
        heightFraction = 0.0;
    }
    else if (heightFraction > 1.0)
    {
        heightFraction = 1.0;
    }

   UIInterfaceOrientation orientation =
   [[UIApplication sharedApplication] statusBarOrientation];
    if (orientation == UIInterfaceOrientationPortrait ||
        orientation == UIInterfaceOrientationPortraitUpsideDown)
    {
        animatedDistance = floor(PORTRAIT_KEYBOARD_HEIGHT * heightFraction);
    }
    else
    {
        animatedDistance = floor(LANDSCAPE_KEYBOARD_HEIGHT * heightFraction);
    }

   CGRect viewFrame = self.view.frame;
    viewFrame.origin.y -= animatedDistance;

    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationBeginsFromCurrentState:YES];
    [UIView setAnimationDuration:KEYBOARD_ANIMATION_DURATION];

    [self.view setFrame:viewFrame];

    [UIView commitAnimations];
}

- (void)textFieldDidEndEditing:(UITextField *)textField {
    CGRect viewFrame = self.view.frame;
    viewFrame.origin.y += animatedDistance;

    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationBeginsFromCurrentState:YES];
    [UIView setAnimationDuration:KEYBOARD_ANIMATION_DURATION];

    [self.view setFrame:viewFrame];

    [UIView commitAnimations];
}
Shawn Craver
awesome thanks for your time Shawn Ill give it a go tonight and let everyone know how it goes
Doron Katz
A: 

Hi there, I think the easiest way to solve this issue is to shift the view up and down, corresponding to the keyboard

- (void)textFieldDidBeginEditing:(UITextField *)textField
{
    CGRect rect = self.view.frame;
    rect = CGRectMake(rect.origin.x, rect.origin.y,
                  rect.size.width, rect.size.height + 80);//keyboard height
    [UIView beginAnimations: @"anim" context: nil];
    [UIView setAnimationBeginsFromCurrentState: YES];
    [UIView setAnimationDuration: movementDuration];
    self.view.frame = rect;
    [UIView commitAnimations];
}

- (void)textFieldDidEndEditing:(UITextField *)textField
{
    //same as above but you shift it down
}
Nguyen Khang
For either examples, i dont think the delegates are being registered properly. I did an NSLog in both delegates and it doesnt show up in the console. I did set protocol in header for UITextDelegate. What els e can i do? Anything in XIB? Ta
Doron Katz
In Interface Builder, you'll need to set your class that implements these methods as the delegate for each of your UITextFields. Or if you build your UI in code, set the delegates there.
Shawn Craver
A: 

I have been looking around for something similar in my case in which the password field was hidden by the keyboard. I wanted to resolve my problem with minimal lines of code.

This solution could also work with more text fields, but just detect each field and apply the offset needed.

In my case

1) I first placed the username, password and sign in button inside a scrollView.

2) Made the viewController a UITextFieldDelegate

3) Wrote the following lines of code (make sure your scrollView is an IBOutlet and it's connected from Interface Builder)

- (void)textFieldDidBeginEditing:(UITextField *)textField 
{
    if ([textField isEqual:textAccountPassword] == NO)
        return;

    CGPoint bottomOffset = CGPointMake(0, 20);
    [scrollview setContentOffset: bottomOffset animated: YES];
}


- (void)textFieldDidEndEditing:(UITextField *)textField 
{
    CGPoint bottomOffset = CGPointMake(0, 0); // return it back to no offset
    [scrollview setContentOffset: bottomOffset animated: YES];
}
ruralcoder