Crash in resignFirstResponder in uitableview

In my UITableView , I have a set of user cells that contain a UITextField . I found out (the hard way), which, apparently, leaves the current view (either by clicking on the new view controller or disabling the active view (“return”), my application will crash when the keyboard is still visible.

To hide the keyboard when the user is still editing the UITextField , but the view has been changed, I added [self.view endEditing:YES]; just before you click on the new view controller, as well as in the viewWillDisappear method.

Now my application only crashes 1 out of 5 attempts to hide the keyboard. Here, I probably learned about the reason for this: When a cell moves behind the scenes, it is destroyed / recycled, so it can be deleted again if necessary. This means that after my cell and the contained text fields go off the screen, send a message to resignFirstResponder (either manually or via [self.view endEditing:YES]; application will [self.view endEditing:YES]; . This is the reverse trace:

 #0 0x012e309b in objc_msgSend () #1 0x05956888 in dyld_stub_usleep () #2 0x003ff056 in -[UIResponder resignFirstResponder] () #3 0x003c697f in -[UITextField resignFirstResponder] () #4 0x003c6ab1 in -[UIView(UITextField) endEditing:] () #5 0x00012c0a in -[MyController viewWillDisappear:] (self=0x7419e90, _cmd=0x52aa27e, animated=1 '\001') at MyController:121 #6 0x003ee9a2 in -[UINavigationController _startTransition:fromViewController:toViewController:] () #7 0x003e932a in -[UINavigationController _startDeferredTransitionIfNeeded] () #8 0x003e8fb6 in -[UINavigationController _popViewControllerWithTransition:allowPoppingLast:] () #9 0x003e9142 in -[UINavigationController popViewControllerAnimated:] () #10 0x003e857a in -[UINavigationController navigationBar:shouldPopItem:] () #11 0x00389260 in -[UINavigationBar _popNavigationItemWithTransition:] () #12 0x0039261b in -[UINavigationBar _handleMouseUpAtPoint:] () #13 0x00354ded in -[UIWindow _sendTouchesForEvent:] () #14 0x00335c37 in -[UIApplication sendEvent:] () #15 0x0033af2e in _UIApplicationHandleEvent () #16 0x01ad5992 in PurpleEventCallback () #17 0x0115e944 in __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ () #18 0x010becf7 in __CFRunLoopDoSource1 () #19 0x010bbf83 in __CFRunLoopRun () #20 0x010bb840 in CFRunLoopRunSpecific () #21 0x010bb761 in CFRunLoopRunInMode () #22 0x01ad41c4 in GSEventRunModal () #23 0x01ad4289 in GSEventRun () #24 0x0033ec93 in UIApplicationMain () #25 0x00001e18 in main (argc=1, argv=0xbffff0a0) at main.m:24 

Now my question is: how do I properly hide the UITextField keyboard inside my UITableViewCell in all situations? (the table view disappears, the new view controller moves, the cell / text field moves off the screen, etc.)

Any help is much appreciated, I just can't get rid of the crashes!

Ill includes one more code:

1) Custom cell class:

 #import <UIKit/UIKit.h> enum Type { tpText = 0, tpInteger, tpDecimal, tpNumber }; @protocol TextInputCellDelegate <NSObject> @required - (void)setNewText:(NSString*)newText forIndex:(NSIndexPath*)index; @end @interface TextInputCell : UITableViewCell <UITextFieldDelegate> { IBOutlet UILabel* mainText; IBOutlet UITextField* textField; NSNumberFormatter* numberFormatter; int type; id <TextInputCellDelegate> delegate; } - (void) initDelegateWithType:(int)aType; - (void) save:(NSString*)text; - (void) startEditing; @property (nonatomic, retain) UILabel* mainText; @property (nonatomic, retain) UITextField* textField; @property (nonatomic, retain) NSNumberFormatter* numberFormatter; @property (nonatomic, assign) int type; @property (nonatomic, assign) id <TextInputCellDelegate> delegate; @end 

Custom cell implementation:

 #import "TextInputCell.h" @implementation TextInputCell @synthesize mainText; @synthesize textField; @synthesize numberFormatter; @synthesize type; @synthesize delegate; - (void) initDelegateWithType:(int)aType { self.type = aType; textField.delegate = self; } - (BOOL)textField:(UITextField *)aTextField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string; { if (![string length] || type == tpText || (isnumber([string characterAtIndex:0]) && type == tpNumber)) { [self save:[textField.text stringByReplacingCharactersInRange:range withString:string]]; return YES; } char c = [string characterAtIndex:0]; BOOL isSep = [string isEqualToString:[numberFormatter decimalSeparator]]; BOOL isMinus = [string isEqualToString:@"-"]; NSRange sep; sep.location = NSNotFound; sep.length = 0; if ([aTextField.text length]) sep = [aTextField.text rangeOfString:[numberFormatter decimalSeparator]]; if (isMinus) { // allow '-' only if type is tpNumber and field is empty if (type != tpNumber) return NO; if ([aTextField.text length]) return NO; [self save:[textField.text stringByReplacingCharactersInRange:range withString:string]]; return YES; } if (isnumber(c) || ((type == tpDecimal || type == tpNumber) && isSep && sep.location == NSNotFound)) { // allow separator for decimal and number, but only if not in text already if (!isSep && sep.location != NSNotFound && type == tpDecimal) { // round after , (only for decimal type) NSString* text = [NSString stringWithFormat:@"%@%@", aTextField.text, string]; double num = [[numberFormatter numberFromString:text] doubleValue]; double res = ((int)(num / 0.5)) * 0.5; aTextField.text = [numberFormatter stringFromNumber:[NSNumber numberWithDouble:res]]; [self save:aTextField.text]; return NO; } [self save:[NSString stringWithFormat:@"%@%@", aTextField.text, string]]; return YES; } [self save:[NSString stringWithFormat:@"%@%@", aTextField.text, string]]; return NO; } - (void) save:(NSString*)text { UITableView* view = (UITableView*)[self superview]; NSIndexPath* index = [view indexPathForCell:self]; if (delegate) [delegate setNewText:text forIndex:index]; } - (BOOL)textFieldShouldEndEditing:(UITextField *)field { NSLog(@"should end"); // if ([field becomeFirstResponder]) // [field resignFirstResponder]; return YES; } - (BOOL)textFieldShouldReturn:(UITextField *)field { if (type != tpText && [field.text length] == 0) field.text = @"0"; NSLog(@"should return"); // if ([field becomeFirstResponder]) // [field resignFirstResponder]; return YES; } - (void) startEditing { NSLog(@"should return"); [self.textField becomeFirstResponder]; } @end 

View a controller that contains 4 of these cells:

 @implementation MailPrefController @synthesize menuItems; @synthesize mailTo; @synthesize mailCc; @synthesize mailBcc; @synthesize mailSubject; @synthesize mailBody; #pragma mark - #pragma mark Initialization - (id)initWithStyle:(UITableViewStyle)style { // Override initWithStyle: if you create the controller programmatically and want to perform customization that is not appropriate for viewDidLoad. if ((self = [super initWithStyle:UITableViewStyleGrouped])) { } return self; } #pragma mark - #pragma mark View lifecycle - (void)viewDidLoad { [super viewDidLoad]; // save button UIBarButtonItem* saveButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemSave target:self action:@selector(saveMailPrefs)]; self.navigationItem.rightBarButtonItem = saveButton; [saveButton release]; self.navigationItem.title = NSLocalizedString(@"Mail template", nil); // menu items self.menuItems = [[NSArray alloc] initWithObjects:NSLocalizedString(@"To:", nil), NSLocalizedString(@"Cc:", nil), NSLocalizedString(@"Bcc:", nil), NSLocalizedString(@"Subject:", nil), NSLocalizedString(@"Body:", nil), nil]; } - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; } #pragma mark - #pragma mark Table view data source - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { // Return the number of sections. return 1; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { // Return the number of rows in the section. return [menuItems count]; } // Customize the appearance of table view cells. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { if (indexPath.row < 4) { // title TextInputCell* textCell = (TextInputCell*)[tableView dequeueReusableCellWithIdentifier:@"TextInputCell"]; if (textCell == nil) { NSArray* nibContents = [[NSBundle mainBundle] loadNibNamed:@"TextInputCell" owner:self options:nil]; textCell = [nibContents lastObject]; textCell.selectionStyle = UITableViewCellSelectionStyleNone; textCell.textField.textColor = [Service uiColor]; textCell.delegate = self; [textCell initDelegateWithType:tpText]; } textCell.mainText.text = [menuItems objectAtIndex:indexPath.row]; switch (indexPath.row) { case 0: textCell.textField.text = self.mailTo; break; case 1: textCell.textField.text = self.mailCc; break; case 2: textCell.textField.text = self.mailBcc; break; case 3: textCell.textField.text = self.mailSubject; break; default: break; } return textCell; } // body text multiline TextInputMultilineCell* textMultilineCell = (TextInputMultilineCell*)[tableView dequeueReusableCellWithIdentifier:@"TextInputMultilineCell"]; if (textMultilineCell == nil) { NSArray* nibContents = [[NSBundle mainBundle] loadNibNamed:@"TextInputMultilineCell" owner:self options:nil]; textMultilineCell = [nibContents lastObject]; textMultilineCell.selectionStyle = UITableViewCellSelectionStyleNone; textMultilineCell.textView.font = [UIFont systemFontOfSize:12]; textMultilineCell.textView.textColor = [Service uiColor]; textMultilineCell.delegate = self; [textMultilineCell initDelegate]; CGRect rect = textMultilineCell.textView.frame; rect.size.height *= 2; textMultilineCell.textView.frame = rect; } textMultilineCell.mainText.text = [menuItems objectAtIndex:indexPath.row]; textMultilineCell.textView.text = self.mailBody; return textMultilineCell; } - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { switch (indexPath.section) { case 0: { // general section if (indexPath.row < 4) { TextInputCell* cell = (TextInputCell*)[[self tableView] cellForRowAtIndexPath:indexPath]; [cell startEditing]; } } default: break; } } 

An accident can be played by clicking on one of the cells with a UITextField , slide the cell off the screen (without hiding the keyboard), and then simply reject the table view (for example, return via the navigation controller).

Could this be caused by manually opening the keyboard when clicking on a cell? ( startEditing ). I do this, so the user does not need to hit the text box, but editing also starts when he, for example. selects the text label of the cell.

+8
objective-c iphone uitextfield uitableview
source share
5 answers

Maybe a little late, but I had a similar problem. I could see that with a zombie, my UITextView was trying to forward messages (using textViewDidEndEditing) after being freed by scrolling from the view. In dealloc of my equivalent of your TextInputCell, I just set the UITextField delegate to nil. worked with pleasure.

+5
source share

I had a similar problem today, with regular crashes caused by pressing the UINavigation feedback button while the keyboard was visible. My UITextField was contained in a UIScrollView to enable scrolling when the keyboard was visible.

I am compiling my application under iOS 5.1 using ARC, and the solution for me was to set the UIScrollView delegate to zero in the dealloc call.

+2
source share

Just send resignFirstResponder to the UITextField that is currently being edited before you reject the current view (but I think this should not be a problem). If you do not have access to it, remember which textField is edited in the variable:

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

PS. Can you post what error Xcode shows?

0
source share

As I said, I suspect that your problem is not directly related to calling resignFirstResponder, but rather something else. Most likely, no one "owns" the gaze in question (because, perhaps, due to too many unlock calls or inability to save at some point), and therefore, when a deferred call is made, the object does not exist.

You can try to print the number of stored objects in the application (in particular, a text representation) and see if the values ​​correspond to your expectations.

0
source share

I see this in my application built against iOS 5.0. Two thoughts come to mind:

  • Prevent scrolling when the keyboard is displayed so that the text field is not reworked.

  • Change the code around resignFirstResponder to make sure it is still valid for the call, for example: (Note, I am adding this to my code, but it has not yet been verified to resolve the error. I will post the update as soon as I can verify it)

     -(BOOL) textFieldShouldReturn:(UITextField *) textField { if (textfield && [textField isKindOfClass:[UITextField class]] && [textField retainCount] > 0 && [textField isFirstResponder]) { [textField resignFirstResponder]; } else { // how to hide the keyboard? // since uitextfield is bad the containing cell (self) may be bad, so cant [self.view endEditing:YES]; // any suggestions? } return YES; } 
0
source share

All Articles