Using and Centering UITextFields In Cocos2d

While working on Empous, I needed to include UITextFields so I could allow my users to create accounts and login to my application. This post will explain the difficulties I encountered along with my solutions.

The first part of this post explains how to add and remove UITextFields from a Cocos2d scene. This is provided as a background for those who have never used UITextFields in a cocos2d application before. The second half of this post will highlight the code I use in Empous to center a UITextField above the keyboard whenever a user enters text.

Adding and Removing UITextFields

You cannot add UITextFields directly to a Cocos2d object, but you can add them to the to the openGLView held by the CCDirector object. To get a basic UITextField onto the scene you could do the following (Please note that the code has only been tested in a landscape orientation. I am quite confident this will not work without modification in a portrait application):

CGSize winSize = [[CCDirector sharedDirector] winSize];
CGPoint center = ccp(winSize.width/2,winSize.height/2);
UITextField* textField = [[UITextField alloc] initWithFrame:CGRectMake((center.x - 150), 170, 300, 35)];

//The UITextField will not be visible unless you set the background color and border style. This sets the background color to a dark green. You can choose what you like. 
textField.backgroundColor = [UIColor colorWithRed:.27 green:.18 blue:.05 alpha:1.0];
textField.borderStyle = UITextBorderStyleRoundedRect;

//Add the text field to the openGlView
[[[CCDirector sharedDirector] openGLView] addSubview: textField];

Removal is handled using iOS semantics since we are using a UITextField. You will need to keep a pointer to the text field in order to remove it from the view. The code is one line:

[textField removeFromSuperView];

For more information on UITextViews and how to access the text from them see the UITextField documentation.

Center UITextFields Above the iOS Keyboard

When a user activates a UITextField, iOS will automatically bring up a keyboard. If the text field is too low on the screen, the keyboard may cover it preventing the user from seeing what they are typing. Even if the text field is not covered by the keyboard, it may look odd if the text field is not centered in the remaining screen space. The code below will move the scene to center any active text field. It will also move the screen back when the user is done entering text.

You must declare that your class obeys the UITextFieldDelegate in the .h file.

@interface TextFieldScene : CCScene <UITextFieldDelegate>

Implementing the protocol is not enough. You must also have every text field set your class as its delegate. Using the code from the first part of this post, you would need to add the following line:

textField.delegate = self;

Without the above line, none of the delegate methods will be called when a user interacts with a text field. To actually slide the screen, enter the following code into your class.

- (void)textFieldDidBeginEditing:(UITextField *)textField
{
    
    int idealViewingLocation = 3*320/4;
    
    //Need to slide the layer to center the text field at the desiredViewingLocation
    double centerYOfField = textField.frame.origin.y - (textField.frame.size.height/2);

    //Translate to cocos2d frame (0,0 is the top left of Apple coordinates).
    double centerYOfFieldCocos = 320 - centerYOfField;
    
    double moveDistance = idealViewingLocation - centerYOfFieldCocos;
    if(moveDistance > 0)
    {
        UIView* cocosView = [[CCDirector sharedDirector] openGLView];
        [UIView beginAnimations: @"anim" context: nil];
        [UIView setAnimationBeginsFromCurrentState: YES];
        [UIView setAnimationDuration: .2f];
        cocosView.frame = CGRectOffset(cocosView.frame, moveDistance, 0);
        [UIView commitAnimations];
    }
}

- (void)textFieldDidEndEditing:(UITextField *)textField
{
    UIView* cocosView = [[CCDirector sharedDirector] openGLView];
    
    //Get the current location of the UIView and see how far off it is from its proper location i.e. 0
    double moveDistance = 0 - cocosView.frame.origin.x;
    
    //Need to slide the layer back down
    [UIView beginAnimations: @"anim" context: nil];
    [UIView setAnimationBeginsFromCurrentState: YES];
    [UIView setAnimationDuration: .2f];
    cocosView.frame = CGRectOffset(cocosView.frame, moveDistance, 0);
    [UIView commitAnimations];    
}

- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
    [textField resignFirstResponder];
    return YES;
}

The above code should handle all the terrible sliding for you. I’ve only tested this with landscape oriented apps, but works well for me.