【问题标题】:crash in resignFirstResponder in uitableviewuitableview 中的 resignFirstResponder 崩溃
【发布时间】:2011-09-27 15:48:43
【问题描述】:

在我的UITableView 中,我有一组包含UITextField 的自定义单元格。我发现(很难),当键盘仍然可见时,显然离开当前视图(通过推送新的视图控制器或关闭活动视图('返回'))会使我的应用程序崩溃。

为了在用户仍在编辑UITextField,但视图已更改时隐藏键盘,我在推送新视图控制器之前添加了[self.view endEditing:YES];,也在viewWillDisappear 方法中添加。

现在,我的应用程序在 5 次尝试隐藏键盘时只有 1 次崩溃。 Here我大概知道了原因: 当单元格移出屏幕时,它会被销毁/回收,因此可以在需要时再次出列。这意味着,一旦我的单元格和包含的文本字段移出屏幕,向其发送resignFirstResponder 消息(手动或通过[self.view endEditing:YES];,应用程序将崩溃。这是回溯:

#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

我现在的问题是,如何在所有情况下正确UITextField 的键盘隐藏在我的UITableViewCell 中? (表格视图消失,新的视图控制器被推送,单元格/文本字段被移出屏幕等)

非常感谢任何帮助,我只是无法摆脱崩溃!

我将包含更多代码:

1) 自定义单元格类:

#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

自定义单元格的实现:

#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

包含其中 4 个单元的视图控制器:

@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;
   }
}

可以通过单击带有UITextField 的单元格之一,将单元格滑出屏幕(不隐藏键盘),然后关闭表格视图(例如通过导航控制器返回)来重现崩溃。

这可能是由于单击单元格时手动打开键盘造成的吗? (startEditing 方法)我这样做是为了让用户不必点击文本字段,但编辑也会在他例如点击单元格文本标签。

【问题讨论】:

  • 你试过自己做resignFirstResponder吗?这通常是处理键盘的方法。但我怀疑您还有另一个问题,主要与键盘无关,导致崩溃。
  • 我尝试将当前正在编辑的单元格保存在一个变量中,并在推送新的视图控制器后立即在单元格文本字段上调用 ​​resignFirstResponder。导致相同的行为 - 当单元格离开屏幕时,它会崩溃。此外,当我推送新的视图控制器时会触发文本字段 textFieldShouldReturn-message,但在那里调用 resignFirstResponder 也无济于事。

标签: iphone objective-c uitableview uitextfield


【解决方案1】:

在您关闭当前视图之前,只需将 resignFirstResponder 发送到当前正在编辑的 UITextField (但我认为这应该不是问题)。如果您无权访问它 - 请记住在变量中编辑了哪个 textField:

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

附言。你能发布什么错误显示 Xcode?​​p>

【讨论】:

  • 在上面添加了很多示例代码,也许这可以澄清一点。我怎样才能看到 xcode 错误是什么?
  • 检查插座是否连接,尤其是textField,我猜错误是“程序接收信号:”EXC_BAD_ACCESS“。”所以你将 resignFirstResponder 发送到释放的变量
  • 是的,错误是 EXC_BAD_ACCESS。这是连接:i52.tinypic.com/25j88xh.jpg
【解决方案2】:

就像我说的,我怀疑您的问题与 resignFirstResponder 调用没有直接关系,而是与其他问题有关。很可能没有人真正“拥有”所涉及的视图(可能是因为发布调用过多,或者在某些时候未能保留),因此当发出辞职调用时,该对象不存在。

您可以尝试打印应用中各种对象(尤其是文本视图)的保留计数,看看这些值是否符合您的预期。

【讨论】:

  • 上面发布的视图每个运行时只创建一次,并且只在应用关闭时销毁。因此,我认为我不能并保留此视图或其子对象的计数。但是,我将检查文本字段的保留计数。预期的结果是什么?据我了解,这些单元格在屏幕外被回收,所以文本字段很可能会/应该被销毁?此外,崩溃独立于视图控制器类。我总共有 4 个独立的视图控制器 (UITableViewController),它们使用上面发布的 TextInputCell 类,并且都崩溃了。
  • 您可能没有正确处理表格视图单元格。这相当棘手。
  • 我对单元格所做的一切都在上面的代码中。我做错了什么?
【解决方案3】:

我在针对 iOS 5.0 构建的应用中看到了这一点。想到两个想法:

  1. 在显示键盘时防止滚动,保证文本字段不被回收。

  2. 修改resignFirstResponder周围的代码,保证调用仍然有效,比如这样: (注意,我正在将其添加到我的代码中,但尚未验证是否可以解决该错误。我会在测试后尽快发布更新)

    -(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; 
     }
    

【讨论】:

  • 增强的错误检查未能防止崩溃。我猜原因正是因为封闭的单元格已被释放,所以只是尝试访问该方法是错误的。因此,“更好”的方法并不重要。
  • 我将通过调用 tableViewObj.scrollEnabled = NO; 来阻止用户滚动表格,直到他们完成对单元格的编辑。在“didBeginEditing”和“didEndEditing”调用中的 tableViewObj.scrollEnabled = YES 中。这变得更容易了,因为我已经在我的自定义单元格中定义了一个“parentController”属性
  • 如前所述防止表格滚动可防止该错误。耶!
【解决方案4】:

可能有点晚了,但我遇到了类似的问题。我可以看到启用僵尸的情况下,我的 UITextView 在释放后试图通过滚动出视图来发送消息(使用 textViewDidEndEditing)。 在我等价的 TextInputCell 的 dealloc 中,我只是将 UITextField 委托设置为 nil。辛苦了。

【讨论】:

    【解决方案5】:

    我今天遇到了类似的问题,在键盘可见时按 UINavigation Back 按钮经常导致崩溃。我的 UITextField 包含在 UIScrollView 中,以便在键盘可见时向上滚动。

    我正在使用 ARC 在 iOS 5.1 下编译我的应用程序,我的解决方案是在 dealloc 调用中将我的 UIScrollView 委托设置为 nil。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-05-04
      • 2023-03-14
      • 2018-07-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多