I'm wondering why my iPhone keyboard opened by a UITextField doesn't type anything except the delete key and the clear button within the text field itself. The text field is embedded in a UIActionSheet with this code sample:
// setup UITextField for the UIActionSheet
UITextField* textField = [[UITextField alloc] initWithFrame:CGRectMake(8.0, 34.0, 304.0, 30.0)];
NSString* startText = [[self.pathName lastPathComponent] stringByDeletingPathExtension];
textField.borderStyle = UITextBorderStyleRoundedRect;
textField.backgroundColor = [UIColor whiteColor];
textField.clearButtonMode = UITextFieldViewModeAlways;
textField.text = startText;
textField.delegate = self;
[textField setKeyboardType:UIKeyboardTypeAlphabet];
[textField setKeyboardAppearance:UIKeyboardAppearanceAlert];
// setup UIActionSheet
UIActionSheet* asheet = [[UIActionSheet alloc] initWithTitle: @"Please enter file name\n\n\n"
delegate: self
cancelButtonTitle: @"Cancel"
destructiveButtonTitle: nil
otherButtonTitles: @"OK", nil];
if ([currentView isKindOfClass:[UIToolbar class]])
[asheet showFromToolbar:(UIToolbar*)currentView];
else if ([currentView isKindOfClass:[UITabBar class]])
[asheet showFromTabBar:(UITabBar*)currentView];
else
[asheet showInView:currentView];
[asheet setFrame:CGRectMake(0.0, 60.0, 320.0, 380.0)];
[asheet insertSubview:textField atIndex:0];
[textField becomeFirstResponder];
// memory management
[textField release];
[asheet release];
I can press the shift key but the keys don't change to lowercase, they are always uppercase. Well, I have never used a text field with an action sheet ... what am I doing wrong here? The delegate methods for action sheet and text field seem to be present as needed.
Edit: I've changed the view association of the action sheet and the size of the action sheet as well:
UIWindow* appWindow = [UIApplication sharedApplication].keyWindow;
[asheet showInView:appWindow];
[asheet setFrame:CGRectMake(0.0, 60.0, 320.0, 204.0)];
while (CGRectEqualToRect(asheet.bounds, CGRectZero))
;
// Add the text field to action sheet
[asheet addSubview:textField];
[textField release];
Now the keyboard is not overlapped (or "underlapped") by the action sheet, the action sheet shows properly below status bar and navigation bar and the keyboard follows below the action sheet and comes from bottom up.
But only textFieldShouldBeginEditing and textFieldDidBeginEditing get called of the text field's delegate methods, nothing more. The shift key changes when I tap it, the erase key works, all other keys show themselves on tap - but don't insert into the text field. So, what is it all about?
Solution: Thanks to SVD pointing me to the fact that it doesn't seem to be possible to embed a UITextField into a UIActionSheet. Because I want to have a method that returns a file name string with an action sheet (just because I like the appearance more than a UIAlertView with a text field added) I've spent some more work on it and finally found a working solution. I share it here because I realized that quite a lot of you were looking for a similar thing.
There are two main aspects to consider to get this working:
the text field must not be a subview of the action sheet, at least at the moment when it is in editable state
the action sheet should not overlap the keyboard
Ok, here comes the code. The modal solution works with a second runloop, so the delegate methods for text field and action sheet can be handled w/out returning to the caller - I'm sure this can be broken down into the conventional approach where you handle all delegate stuff within your own class.
ModalAction.h:
#import <UIKit/UIKit.h>
@interface ModalAction : NSObject
+ (NSString*)ask: (NSString*)question
withTextPrompt: (NSString*)prompt
textPreset: (NSString*)preset
dockToView: (UIView*)toView;
@end
ModalAction.m:
#import "ModalAction.h"
#define TEXT_FIELD_TAG 9999
#define ACTION_SHEET_TAG 8888
@interface ModalActionDelegate : NSObject <UIActionSheetDelegate, UITextFieldDelegate>
{
CFRunLoopRef currentLoop;
NSString* text;
NSUInteger index;
}
@property (assign) NSUInteger index;
@property (retain) NSString* text;
@end
@implementation ModalActionDelegate
@synthesize index;
@synthesize text;
-(id)initWithRunLoop: (CFRunLoopRef)runLoop
{
if (self = [super init])
currentLoop = runLoop;
return self;
}
// Activate keyboard
- (void)acceptInput: (UIActionSheet*)actionSheet
{
UITextField* textField = (UITextField*)[actionSheet viewWithTag:TEXT_FIELD_TAG];
UIWindow* appWindow = [UIApplication sharedApplication].keyWindow;
CGRect frame = textField.frame;
[appWindow insertSubview:textField aboveSubview:actionSheet];
frame.origin.y += 60.0; // move text field to same position as on action sheet
textField.frame = frame;
[textField becomeFirstResponder];
}
- (void)dealloc
{
self.text = nil;
[super dealloc];
}
#pragma mark -
#pragma mark === UIActionSheetDelegate ===
#pragma mark -
// User pressed button. Retrieve results
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
{
NSLog(@"actionSheet clickedButtonAtIndex:%d", buttonIndex);
UIWindow* appWindow = [UIApplication sharedApplication].keyWindow;
UITextField* textField = (UITextField*)[appWindow viewWithTag:TEXT_FIELD_TAG];
if (textField != nil)
self.text = textField.text;
self.index = buttonIndex;
CFRunLoopStop(currentLoop);
}
#pragma mark -
#pragma mark === UITextFieldDelegate ===
#pragma mark -
- (BOOL)textFieldShouldBeginEditing:(UITextField*)textField
{
NSLog(@"textFieldShouldBeginEditing");
return YES;
}
- (void)textFieldDidBeginEditing:(UITextField*)textField
{
NSLog(@"textFieldDidBeginEditing");
[textField becomeFirstResponder];
}
- (BOOL)textFieldShouldEndEditing:(UITextField*)textField
{
NSLog(@"textFieldShouldEndEditing");
return YES;
}
- (void)textFieldDidEndEditing:(UITextField*)textField
{
NSLog(@"textFieldDidEndEditing");
[textField resignFirstResponder];
}
- (BOOL)textField:(UITextField*)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString*)string
{
return YES;
}
- (BOOL)textFieldShouldClear:(UITextField*)textField
{
NSLog(@"textFieldShouldClearEditing");
return YES;
}
- (BOOL)textFieldShouldReturn:(UITextField*)textField
{
NSLog(@"textFieldShouldReturn (%@)", textField.text);
BOOL hasSomeText = ![textField.text isEqualToString:@""];
if (hasSomeText)
{
// send an OK to the action sheet
UIWindow* appWindow = [UIApplication sharedApplication].keyWindow;
UIActionSheet* actionSheet = (UIActionSheet*)[appWindow viewWithTag:ACTION_SHEET_TAG];
if (actionSheet != nil)
{
[actionSheet dismissWithClickedButtonIndex:0 animated:YES];
self.text = textField.text;
self.index = 0;
CFRunLoopStop(currentLoop);
[textField resignFirstResponder];
}
}
return hasSomeText;
}
@end
@implementation ModalAction
+ (NSString*)textQueryWith: (NSString*)question
prompt: (NSString*)prompt
textPreset: (NSString*)preset
button1: (NSString*)button1
button2: (NSString*)button2
forView: (UIView*)toView
{
// Create action sheet
CFRunLoopRef currentLoop = CFRunLoopGetCurrent();
ModalActionDelegate* madelegate = [[ModalActionDelegate alloc] initWithRunLoop:currentLoop];
NSString* sheetTitle = [question stringByAppendingString:@"\n\n\n"];
UIActionSheet* actionSheet = [[UIActionSheet alloc] initWithTitle: sheetTitle
delegate: madelegate
cancelButtonTitle: button1
destructiveButtonTitle: nil
otherButtonTitles: button2, nil];
actionSheet.tag = ACTION_SHEET_TAG;
// Build text field
UITextField* textField = [[UITextField alloc] initWithFrame:CGRectMake(8.0, 34.0, 304.0, 30.0)];
textField.borderStyle = UITextBorderStyleRoundedRect;
textField.tag = TEXT_FIELD_TAG;
textField.placeholder = prompt;
textField.text = preset;
textField.delegate = madelegate;
textField.clearButtonMode = UITextFieldViewModeWhileEditing;
textField.keyboardType = UIKeyboardTypeAlphabet;
textField.keyboardAppearance = UIKeyboardAppearanceAlert;
textField.autocapitalizationType = UITextAutocapitalizationTypeWords;
textField.autocorrectionType = UITextAutocorrectionTypeNo;
textField.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
textField.returnKeyType = UIReturnKeyDone;
// Show action sheet and wait for it to finish displaying
UIWindow* appWindow = [UIApplication sharedApplication].keyWindow;
[actionSheet showInView:appWindow];
[actionSheet setFrame:CGRectMake(0.0, 60.0, 320.0, 204.0)];
while (CGRectEqualToRect(actionSheet.bounds, CGRectZero))
;
// Add the text field to action sheet
[actionSheet addSubview:textField];
[textField release];
// Set the field to first responder and move it into place
[madelegate performSelector: @selector(acceptInput:) withObject: actionSheet];
// Start the run loop
CFRunLoopRun();
// Retrieve the user choices
NSUInteger index = madelegate.index;
NSString* answer = [[madelegate.text copy] autorelease];
if (index == 1)
answer = nil; // assumes cancel in position 1
[textField resignFirstResponder];
[actionSheet release];
textField.delegate = nil;
[textField removeFromSuperview];
[madelegate release];
return answer;
}
+ (NSString*)ask: (NSString*)question
withTextPrompt: (NSString*)prompt
textPreset: (NSString*)preset
dockToView: (UIView*)toView
{
return [ModalAction textQueryWith: question
prompt: prompt
textPreset: preset
button1: NSLocalizedString(@"AlertBtnCancel", @"")
button2: @"OK"
forView: toView];
}
@end
It is running on Simulator iOS 4.2 and on real devices' iOS 4.0.2 - I hope it is also helpful for others.