keyboard in UIActionSheet does not type anything

2019-02-07 14:45发布

问题:

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.

回答1:

One weird thing about action sheets is that, for example, if there's a tab bar present, the tab bar may "steal" some of the action sheets taps, even though visually the action sheet is on top.

Your action sheet is 380 pixels high - so when the keyboard slides out it overlaps with the action sheet. Perhaps in this case it is the action sheet stealing the keyboard's taps? Try making the action sheet smaller and closer to the top - e.g. to test the idea I'd just make it originate at 0,0, and 240 pixels high.

Edit:

Apparently it is a problem - it doesn't work for me either in a simple test application, without tab bar or tool bar. I've found these links though:

Doing this with an alert view and it seems to work (although it seems like it only works for alert views): http://discussions.info.apple.com/thread.jspa?threadID=1674641&start=0&tstart=0

And here someone making their own "action sheet" instead: how to enable text input in UITextField which is in UIActionSheet?