iOS:如何在自定义单元格上正确使用prepareForReuse?将编辑切换到非编辑模式时,我的单元格被错误地重复使用

时间:2014-01-25 22:23:54

标签: ios objective-c uitableview

对于这个话题,我的最后一篇文章已经确定了......我尽可能地缩小了它的范围,但我肯定需要帮助才能解决这个问题。 由于我使用的是自定义单元格和嵌入式表视图,因此当键盘出现时我必须自己滚动并隐藏一些文本字段(每个单元格都有一个文本字段)。这很好用。但是,如果我的表视图向上移动,有些单元格隐藏在我的标题后面或导航控制器栏后面,一旦我结束编辑模式,一切都会搞砸。之前隐藏的单元格立即处于正常状态而不是缩进。这看起来很讨厌,我不知道如何解决它。如果没有隐藏细胞,当然一切都很好。

所以我查看了文档并找到了prepareForReuse,我想这可能会有所帮助。不知道怎么解决这个问题?如果有人能给我必要的暗示,我会非常感激...

我有一个只有这种方法的自定义UITableViewCell:

- (void) setEditing:(BOOL)editing animated:(BOOL)animated{
    [super setEditing:editing animated:animated];
    if (editing){
        self.title.hidden = NO;
        self.titleLabel.hidden = YES;

        self.iconButton.hidden = NO;
        self.icon.hidden = YES;
        self.costs.hidden = YES;
        self.disclosureIndicator.hidden = YES;
        self.subcategories.hidden = YES;
    }  else if (!editing){
        self.title.hidden = YES;
        self.titleLabel.text = self.title.text;
        self.titleLabel.hidden = NO;

        self.iconButton.hidden = YES;
        self.icon.hidden = NO;
        self.subcategories.hidden = NO;
        self.costs.hidden = NO;
        self.disclosureIndicator.hidden = NO;
    }
}

在此初始化并重复使用:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    //Data model and cell setup
    static NSString *CellIdentifier = @"MainCategoryCell";
    MainCategoryTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
    MainCategory *mainCategory = [self.fetchedResultsController objectAtIndexPath:indexPath];

    ...

    return cell;
}

由于我使用嵌入式表视图,因此我必须自己滚动隐藏的文本字段:

- (void)registerForKeyboardNotifications
{
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWasShown:)
                                                 name:UIKeyboardDidShowNotification object:nil];

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWillBeHidden:)
                                                 name:UIKeyboardWillHideNotification object:nil];

}

- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];

    //Has to be unregistered always, otherwise nav controllers down the line will call this method
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

- (void)keyboardWasShown:(NSNotification*)aNotification
{
    //All table views are embedded in the parent view
    //The parent view y is defined by the status and navigation bar, the height by the tab bar
    CGRect viewRect = self.view.frame;
    CGRect tableRect = self.tableView.frame;

    //The keyboard size will be adjusted that the height is really only the height overlapping the table
    NSDictionary* info = [aNotification userInfo];
    CGSize kbOriginalSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
    CGFloat effectiveKeyBoardHeight = kbOriginalSize.height - TABBARHEIGHT - (viewRect.size.height - tableRect.size.height - tableRect.origin.y); //the last origin property is important to find out if there is a header
    CGSize kbSize = CGSizeMake(kbOriginalSize.width, effectiveKeyBoardHeight);

    //Now the content insets will be adjusted for the calculated part of the keyboard overlapping the table
    UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0);
    self.tableView.contentInset = contentInsets;
    self.tableView.scrollIndicatorInsets = contentInsets;

    // If active text field is hidden by keyboard, scroll it so it's visible
    // I've changed the apple code here! They use viewRect..in my app this doesn't make any sense, tableRect is key
    tableRect.size.height -= kbSize.height;
    if (!CGRectContainsPoint(tableRect, self.activeField.frame.origin) ) {
        [self.tableView scrollRectToVisible:self.activeField.frame animated:YES];
    }
}

- (void)keyboardWillBeHidden:(NSNotification*)aNotification
{
    UIEdgeInsets contentInsets = UIEdgeInsetsZero;
    self.tableView.contentInset = contentInsets;
    self.tableView.scrollIndicatorInsets = contentInsets;
}

3 个答案:

答案 0 :(得分:1)

我认为问题可能是tableview的设置contentInset不会调用tableView的prepareForReuse方法或任何其他方法来调用cellForRow,这会导致隐藏的单元格出现奇怪状态。所以在键盘隐藏方法中,配置所有可能显示单元格状态,然后reloadData将有助于正确显示单元格。

答案 1 :(得分:0)

如果你只是做一个reloadData,我觉得应该清理一切。 (请记住,您的dataSource应保留每个单元格的所有状态。)

答案 2 :(得分:0)

解决了......我一周都在努力解决这个问题,解决方案就是移动一行代码......:

我的开始和结束编辑表:

- (IBAction) editTable:(id)sender
{
    if(self.editing)
    {
        //Change to editing no
        [super setEditing:NO animated:YES];
        //resigns first responder for all textfields
        [self.view endEditing:YES];
        [self setEditing:NO animated:YES];
        [self.tableView setEditing:NO animated:YES];

        //Remove Done button and exchange it with edit button
        [self.navigationItem.rightBarButtonItem setTitle:NSLocalizedString(@"Edit", nil)];
        [self.navigationItem.rightBarButtonItem setStyle:UIBarButtonItemStylePlain];
        self.navigationItem.leftBarButtonItem = nil;
        [self.navigationItem setHidesBackButton:NO animated:YES];
        [((AppDelegate *)[[UIApplication sharedApplication] delegate]) saveContext];
        self.suspendAutomaticTrackingOfChangesInManagedObjectContext = NO;
    } else {
        //Change to editing mode
        [super setEditing:YES animated:YES];
        [self.tableView setEditing:YES animated:YES];
        [self setEditing:YES animated:YES];

        //Exchange the edit button with done button
        [self.navigationItem.rightBarButtonItem setTitle:NSLocalizedString(@"Done", nil)];
        [self.navigationItem.rightBarButtonItem setStyle:UIBarButtonItemStyleDone];
        [self.navigationItem setHidesBackButton:YES animated:YES];

        //And insert instead of the back button an add button
        UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(addButtonAction:)];
        [self.navigationItem setLeftBarButtonItem:addButton];
    }
}

这里的关键是:

[self.tableView setEditing:NO animated:YES];

在结束编辑状态的最底部!之前我在所有细胞的辞职响应者面前,这是错误。因为结束所有单元格的第一响应者的结束编辑当然触发了我的keyboardWillBeHidden方法。然后表格识别它需要两个新单元格,但是如果表格已经结束编辑状态,则单元格处于非编辑状态。但是,如果我将这行代码切换到最后,那么一切都应该正常运行!希望这将在未来的某个时候帮助别人。