即使UIResponder对象退出其第一响应者状态,键盘也不会closures

我设法自定义UIAlertView把两个UITextFields作为子视图里面,但在这样做的键盘坏了。

当我试图驳回它辞职的第一响应者,它没有做任何事情只是呆在那里。

这是我的代码,只有相关的部分(这是我放在UIAlertView子类):

#pragma mark - #pragma mark UIView - (void)layoutSubviews { [super layoutSubviews]; NSArray *subviews = [self subviews]; UIView *backgroundView = [subviews objectAtIndex:0]; CGRect firstFrame = [[subviews objectAtIndex:2] frame]; CGRect backgroundFrame = [backgroundView frame]; CGFloat displacement = kTextHeight * 2 + kPadding * 3; CGFloat pivot = firstFrame.origin.y + firstFrame.size.height; CGRect nameFrame = CGRectMake(firstFrame.origin.x, pivot + kPadding, kTextWidth, kTextHeight); CGRect descriptionFrame = CGRectMake(firstFrame.origin.x, pivot + kTextHeight + kPadding * 2, kTextWidth, kTextHeight); if (_nameField == nil && _descriptionField == nil) { // first UITextField _nameField = [[UITextField alloc] initWithFrame:CGRectZero]; [_nameField setPlaceholder:kNamePlaceholder]; [_nameField setBorderStyle:UITextBorderStyleRoundedRect]; [_nameField setDelegate:self]; // second UITextField _descriptionField = [[UITextField alloc] initWithFrame:CGRectZero]; [_descriptionField setPlaceholder:kDescriptionPlaceholder]; [_descriptionField setBorderStyle:UITextBorderStyleRoundedRect]; [_descriptionField setDelegate:self]; [self addSubview:_nameField]; [self addSubview:_descriptionField]; } // set the new frames to each text field [_nameField setFrame:nameFrame]; [_descriptionField setFrame:descriptionFrame]; // increment background image-view height by "displacement" backgroundFrame.size.height += displacement; [backgroundView setFrame:backgroundFrame]; // displace by "diplacement" every subview positioned after "pivot" for (UIView *view in subviews) { CGRect viewRect = [view frame]; if (viewRect.origin.y > pivot) [view setFrame:CGRectOffset(viewRect, 0., displacement)]; } } - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event { return YES; } #pragma mark - #pragma mark <UITextFieldDelegate> - (BOOL)textFieldShouldReturn:(UITextField *)textField { [textField resignFirstResponder]; return NO; } 

这是我的代码在一个大块:

 #import <Foundation/Foundation.h> #import <UIKit/UIKit.h> static CGFloat kTextWidth = 260.; static CGFloat kTextHeight = 25.; static CGFloat kPadding = 10.; static NSString *kNamePlaceHolder = @"Name"; static NSString *kDescriptionPlaceHolder = @"Description"; // interfaces @interface AppDelegate: NSObject <UIApplicationDelegate> { UIWindow *_window; } - (void)showAlertView; @end @interface AlertView: UIAlertView <UITextFieldDelegate> { UITextField *_nameField; UITextField *_descriptionField; } @end // implementations @implementation AppDelegate #pragma mark - #pragma mark NSObject - (void)dealloc { [_window release]; [super dealloc]; } #pragma mark - #pragma mark AppDelegate - (void)showAlertView { AlertView *alertView = [[[AlertView alloc] initWithTitle:@"Title" message:@"Body" delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil] autorelease]; [alertView show]; } #pragma mark - #pragma mark <UIApplicationDelegate> - (BOOL) application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)options { _window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; [_window setBackgroundColor:[UIColor whiteColor]]; [_window makeKeyAndVisible]; [NSTimer scheduledTimerWithTimeInterval:3. target:self selector:@selector(showAlertView) userInfo:nil repeats:NO]; return YES; } @end @implementation AlertView #pragma mark - #pragma mark NSObject - (void)dealloc { [_nameField setDelegate:nil]; [_descriptionField setDelegate:nil]; [_nameField release]; [_descriptionField release]; [super dealloc]; } #pragma mark - #pragma mark UIView - (void)layoutSubviews { [super layoutSubviews]; NSArray *subviews = [self subviews]; UIView *backgroundView = [subviews objectAtIndex:0]; CGRect firstFrame = [[subviews objectAtIndex:2] frame]; CGRect backgroundFrame = [backgroundView frame]; CGFloat displacement = kTextHeight * 2 + kPadding * 3; CGFloat pivot = firstFrame.origin.y + firstFrame.size.height; CGRect nameFrame = CGRectMake(firstFrame.origin.x, pivot + kPadding, kTextWidth, kTextHeight); CGRect descriptionFrame = CGRectMake(firstFrame.origin.x, pivot + kTextHeight + kPadding * 2, kTextWidth, kTextHeight); if (_nameField == nil && _descriptionField == nil) { // first UITextField _nameField = [[UITextField alloc] initWithFrame:CGRectZero]; [_nameField setPlaceholder:kNamePlaceholder]; [_nameField setBorderStyle:UITextBorderStyleRoundedRect]; [_nameField setDelegate:self]; // second UITextField _descriptionField = [[UITextField alloc] initWithFrame:CGRectZero]; [_descriptionField setPlaceholder:kDescriptionPlaceholder]; [_descriptionField setBorderStyle:UITextBorderStyleRoundedRect]; [_descriptionField setDelegate:self]; [self addSubview:_nameField]; [self addSubview:_descriptionField]; } // set the new frames to each text field [_nameField setFrame:nameFrame]; [_descriptionField setFrame:descriptionFrame]; // increment background image-view height by "displacement" backgroundFrame.size.height += displacement; [backgroundView setFrame:backgroundFrame]; // displace by "diplacement" every subview positioned after "pivot" for (UIView *view in subviews) { CGRect viewRect = [view frame]; if (viewRect.origin.y > pivot) [view setFrame:CGRectOffset(viewRect, 0., displacement)]; } } - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event { return YES; } #pragma mark - #pragma mark <UITextFieldDelegate> - (BOOL)textFieldShouldReturn:(UITextField *)textField { [textField resignFirstResponder]; return NO; } @end int main(int argc, char **argv) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; int retVal = UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); [pool drain]; return retVal; } 

我的解决scheme是覆盖UIAlertView子类中的becomeFirstResponder

 - (BOOL)becomeFirstResponder { // For some unkown reason UIAlertView can be first responder. Disabling // this completely fixes this bug http://stackoverflow.com/q/6866932/434423 // BTW, overriding "canBecomeFirstResponder" to return "NO" doesn't work, // this is why we override this instead of "canBecomeFirstResponder". return NO; } 

我会发布我的所有代码,以防有人需要它。 正确处理UIAlertView自定义。


CustomizableAlertView.h

 @class UIAlertView; @interface CustomizableAlertView: UIAlertView { UIView *_customSubview; } @property (nonatomic, retain) UIView *customSubview; @end 

CustomizableAlertView.m

 #import <Foundation/Foundation.h> #import <UIKit/UIKit.h> #import <QuartzCore/QuartzCore.h> #import "CustomizableAlertView.h" static CGFloat kVerticalPadding = 10.; static CGFloat kMinHorizontalPadding = 12.; static NSString *kTransformKeyPath = @"transform"; @interface CustomizableAlertView (Private) - (void)centerViewOnKeyboardDismissal:(UIControl *)control; @end @implementation CustomizableAlertView #pragma mark - #pragma mark NSObject - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; [_customSubview release]; [super dealloc]; } #pragma mark - #pragma mark NSObject (NSKeyValueObserving) - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { // Make sure we are observing kTransformKeyPath from self if (_customSubview != nil && self == object && [keyPath isEqualToString:kTransformKeyPath]) { // We should remove the observing object to avoid a recursion [self removeObserver:self forKeyPath:kTransformKeyPath]; CGFloat displacement = [_customSubview frame].size.height / 2. + kVerticalPadding; [self setTransform: CGAffineTransformMakeTranslation(.0, -displacement)]; // Resume the observation [self addObserver:self forKeyPath:kTransformKeyPath options:0 context:NULL]; } } #pragma mark - #pragma mark UIResponder - (BOOL)becomeFirstResponder { // For some unkown reason UIAlertView can be first responder. Disabling // this completely fixes this bug http://stackoverflow.com/q/6866932/434423 // BTW, overriding "canBecomeFirstResponder" to return "NO" doesn't work, // this is why we override this instead of "canBecomeFirstResponder". return NO; } #pragma mark - #pragma mark UIView - (void)layoutSubviews { [super layoutSubviews]; NSArray *subviews = [self subviews]; UIView *messageView = nil; UIView *backgroundView = nil; // Perform a pattern search instead of depending on UIAlertView internal // structure. // This fetches the background view. for (UIView *view in subviews) if ([view isKindOfClass:[UIImageView class]]) { backgroundView = view; break; } // Perform a pattern search instead of depending on UIAlertView internal // structure // This fetches the view that we'll use as an anchor. for (UIView *view in subviews) if ([view isKindOfClass:[UILabel class]] && [[(UILabel *)view text] isEqualToString:[self message]]) { messageView = view; break; } if (backgroundView == nil || messageView == nil) // Abort if there were no matches return; CGRect bounds = [self bounds]; CGRect messageFrame = [messageView frame]; CGRect backgroundFrame = [backgroundView frame]; CGRect customFrame = [_customSubview frame]; CGFloat displacement = customFrame.size.height + kVerticalPadding * 2.; CGFloat pivot = messageFrame.origin.y + messageFrame.size.height; // increment background image-view height by "displacement" backgroundFrame.size.height += displacement; [backgroundView setFrame:backgroundFrame]; // displace by "diplacement" every subview positioned after "pivot" for (UIView *view in subviews) { CGRect viewRect = [view frame]; if (viewRect.origin.y > pivot) [view setFrame:CGRectOffset(viewRect, .0, displacement)]; } CGFloat maxWidth = bounds.size.width - kMinHorizontalPadding * 2.; if (customFrame.size.width > maxWidth) customFrame.size.width = maxWidth; [_customSubview setFrame:CGRectOffset(customFrame, (bounds.size.width - customFrame.size.width) / 2., pivot + kVerticalPadding)]; if ([_customSubview superview] == nil) [self addSubview:_customSubview]; } - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event { // perform hit-test for every subview for (UIView *view in [self subviews]) if ([view pointInside:[self convertPoint:point toView:view] withEvent:event]) return YES; return NO; } #pragma mark - #pragma mark UIAlertView - (void)show { NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter]; [defaultCenter addObserver:self selector:@selector(centerViewOnKeyboardDismissal:) name:UITextFieldTextDidEndEditingNotification object:nil]; [defaultCenter addObserver:self selector:@selector(centerViewOnKeyboardDismissal:) name:UITextViewTextDidEndEditingNotification object:nil]; // Dammit! we need to observe value-changes for the property "transform" to // handle properly the UIAlertView relocation. // We need to hang this onto "show" because UIAlertView's designated // initializer is imposible to override due to its variadic parameter (...). [self addObserver:self forKeyPath:kTransformKeyPath options:0 context:NULL]; [super show]; } - (void)dismissWithClickedButtonIndex:(NSInteger)buttonIndex animated:(BOOL)animated { // Cease to observe kTransformKeyPath upon dismissal [self removeObserver:self forKeyPath:kTransformKeyPath]; [super dismissWithClickedButtonIndex:buttonIndex animated:animated]; } #pragma mark - #pragma mark CustomizableAlertView @synthesize customSubview = _customSubview; - (void)centerViewOnKeyboardDismissal:(UIControl *)control { CGRect bounds = [[self superview] bounds]; [self setCenter:CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds))]; } @end 

这是一个用法示例:


InputView.h

 #import "CustomizableAlertView.h" @class UITextField; @protocol UITextFieldDelegate; @interface InputView: CustomizableAlertView <UITextFieldDelegate> { NSString *_placeholder; } @property (nonatomic, readonly) UITextField *textField; @property (nonatomic, copy) NSString *placeholder; @end 

InputView.m

 #import <Foundation/Foundation.h> #import <UIKit/UIKit.h> #import "InputView.h" static CGFloat kTextFieldHeight = 25.; static CGFloat kHorizontalPadding = 12.; static NSString *kDefaultPlaceholder = @"Enter text here"; // NSLocalizedString(@"Enter text here", nil) static NSString *kPlaceholder; static NSCharacterSet *kWhitespaceCharSet; @interface InputView (Private) - (void)initializeTextField; @end @implementation InputView #pragma mark - #pragma mark NSObject + (void)initialize { if (self == [InputView class]) { kPlaceholder = NSLocalizedString(kDefaultPlaceholder, nil); kWhitespaceCharSet = [[NSCharacterSet whitespaceAndNewlineCharacterSet] retain]; } } #pragma mark - #pragma mark UIView - (void)layoutSubviews { if ([self customSubview] == nil) [self initializeTextField]; [super layoutSubviews]; } #pragma mark - #pragma mark CustomizableAlertView - (void)setCustomSubview:(UIView *)view { // NO-OP, should be readonly our control } - (UITextField *)textField { return (UITextField *)[self customSubview]; } #pragma mark - #pragma mark InputView @synthesize placeholder = _placeholder; - (void)initializeTextField { UITextField *textField = [[[UITextField alloc] initWithFrame:CGRectMake(0., 0., [self bounds].size.width - kHorizontalPadding * 2, kTextFieldHeight)] autorelease]; [textField setBorderStyle:UITextBorderStyleRoundedRect]; [textField setPlaceholder: _placeholder == nil ? kPlaceholder : _placeholder]; [textField setKeyboardAppearance:UIKeyboardAppearanceAlert]; [textField setReturnKeyType:UIReturnKeyDone]; [textField setClearButtonMode:UITextFieldViewModeWhileEditing]; [textField setDelegate:self]; // call the setter from the superclass becaused we overrode it to do // nothing [super setCustomSubview:textField]; } #pragma mark - #pragma mark <UITextFieldDelegate> - (BOOL)textFieldShouldReturn:(UITextField *)textField { if([@"" isEqualToString:[[textField text] stringByTrimmingCharactersInSet:kWhitespaceCharSet]]) return NO; // The user pressed "Done" button, so dismiss the keyboard and return [textField resignFirstResponder]; return YES; } @end