Swipe-To-Delete вызывает вставку строки в моей таблице

В моем приложении пользователю предоставляется таблица данных. Когда они нажимают «изменить», вверху появляется строка редактирования с зеленым знаком «плюс» — они могут использовать это, чтобы добавить другой элемент в список.

Это отлично работает, когда пользователь просто нажимает кнопку редактирования, но если пользователи используют пролистывание для удаления, появляется строка редактирования (без зеленого плюса), и все идет странно (кнопка удаления появляется в неправильной строке и т. д.).

Может ли кто-нибудь сказать мне, что я делаю неправильно? Извините, что вывалил кучу кода, но я прошел столько кругов, что потерял всякую перспективу!

#import "ChecklistsViewController.h"
#import "Checklist.h"

@interface ChecklistsViewController (private)
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath;
- (void)addingView; 
@end


@implementation ChecklistsViewController
@synthesize category, managedObjectContext, fetchedResultsController;


- (id)initWithStyle:(UITableViewStyle)style
{
    self = [super initWithStyle:style];
    if (self) {
        editControlsDidShow = NO;
    }
    return self;
}

- (void)dealloc
{
    [category release];
    [managedObjectContext release];
    [fetchedResultsController release];
    [super dealloc];
}

#pragma mark - View lifecycle

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.navigationItem.rightBarButtonItem = self.editButtonItem;    
    self.tableView.allowsSelectionDuringEditing = YES;
}

#pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return [[self.fetchedResultsController sections] count];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section];    
    if (tableView.editing) return [sectionInfo numberOfObjects] +1; 
    return [sectionInfo numberOfObjects];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
    }

    // Configure the cell...
    cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
    [self configureCell:cell atIndexPath:indexPath];

    if (tableView.editing) {
        if (indexPath.row == 0) {
            cell.textLabel.text = @"Add New Checklist";
            cell.detailTextLabel.text = nil;
        }
        if (indexPath.row != 0) {
            [self configureCell:cell atIndexPath:indexPath];
        } 
    }
    return cell;
}

- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{    
    return YES;
}

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (editingStyle == UITableViewCellEditingStyleDelete)
    {
        NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
        [context deleteObject:[self.fetchedResultsController objectAtIndexPath:indexPath]];

        NSError *error = nil;
        if (![context save:&error])
        {
            // error code
            NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
            abort();
        }
    }    
    else if (editingStyle == UITableViewCellEditingStyleInsert) {

        [self addingView];
    }   
}

- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath {
    int row = indexPath.row;    
    if (self.editing && row == 0) {
        if (editControlsDidShow) return UITableViewCellEditingStyleInsert;
        return UITableViewCellEditingStyleDelete;
    }
    return UITableViewCellEditingStyleDelete;
}

- (void)setEditing:(BOOL)editing animated:(BOOL)animated
{
    editControlsDidShow = NO;
    [super setEditing:editing animated:animated];

    NSArray *addRow = [NSArray arrayWithObjects:[NSIndexPath indexPathForRow:0 inSection:0], nil];
    [self.tableView beginUpdates];
    if (editing) {
        editControlsDidShow = YES;
        [self.tableView insertRowsAtIndexPaths:addRow withRowAnimation:UITableViewRowAnimationLeft];
    }
    else {
        [self.tableView deleteRowsAtIndexPaths:addRow withRowAnimation:UITableViewRowAnimationLeft];
    }
    [self.tableView endUpdates];
}


#pragma mark - Table view delegate

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    // not done yet
}


#pragma mark - Data


- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
    Checklist *aChecklist = [self.fetchedResultsController objectAtIndexPath:indexPath];
    cell.textLabel.text = [aChecklist.name description];
    cell.detailTextLabel.text = [aChecklist.category.name description];
}


- (void) addingView// :(id)sender
{   
    //Create the root view controller for the navigation controller
    AddingViewController *viewController = [[AddingViewController alloc] initWithNibName:@"AddingViewController" bundle:nil];    
    viewController.delegate = self;
    viewController.title = @"Add Checklist";

    // Create the navigation controller and present it modally
    UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:viewController];
    [self presentModalViewController:navigationController animated:YES];

    viewController.textLabel.text = @"Enter new checklist name";

    [navigationController release];
    [viewController release];
}


#pragma mark - AddingViewDelegate


- (void)addingViewController:(AddingViewController *)addingViewController didAdd:(NSString *)itemAdded
{
    if (itemAdded != nil) {
        // Turn off editing mode.
        if (self.editing) [self.navigationController setEditing:NO animated:NO];        
        // Create a new instance of the entity managed by the fetched results controller.
        NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
        NSEntityDescription *entity = [[self.fetchedResultsController fetchRequest] entity];
        Checklist *newChecklist = [NSEntityDescription insertNewObjectForEntityForName:[entity name] inManagedObjectContext:context];
        [category addChecklistsObject:newChecklist];
        newChecklist.name = itemAdded;        

        NSError *error = nil;
        if (![context save:&error])
        {
            // error code
            NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
            abort();
        }
    }
    [self dismissModalViewControllerAnimated:YES];
}


#pragma mark - Fetched results controller

- (NSFetchedResultsController *)fetchedResultsController
{
    if (fetchedResultsController != nil)
    {
        return fetchedResultsController;
    }     
    // Create the fetch request for the entity.
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    // Edit the entity name as appropriate.
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Checklist" inManagedObjectContext:self.managedObjectContext];
    [fetchRequest setEntity:entity];
    // Set 4* the predicate so we only see checklists for this category.
    NSPredicate *requestPredicate = [NSPredicate predicateWithFormat:@"category.name = %@", self.category.name];
    [fetchRequest setPredicate:requestPredicate];    
    // Set the batch size to a suitable number.
    [fetchRequest setFetchBatchSize:20];    
    // Edit the sort key as appropriate.
    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES];
    NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
    [fetchRequest setSortDescriptors:sortDescriptors];    
    // Edit the section name key path and cache name if appropriate.
    NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest 
                                                                                                managedObjectContext:self.managedObjectContext 
                                                                                                  sectionNameKeyPath:nil 
                                                                                                           cacheName:nil];
    aFetchedResultsController.delegate = self;
    self.fetchedResultsController = aFetchedResultsController;

    [aFetchedResultsController release];
    [fetchRequest release];
    [sortDescriptor release];
    [sortDescriptors release];

    NSError *error = nil;
    if (![self.fetchedResultsController performFetch:&error])
    {
       // handle the error properly!
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();
    }

    return fetchedResultsController;
} 


#pragma mark - Fetched results controller delegate


- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
    [self.tableView beginUpdates];
}


- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
           atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type
{
    switch(type)
    {
        case NSFetchedResultsChangeInsert:
            [self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
            break;

        case NSFetchedResultsChangeDelete:
            [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
            break;
    }
}


- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
       atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
      newIndexPath:(NSIndexPath *)newIndexPath
{
    UITableView *tableView = self.tableView;

    switch(type)
    {

        case NSFetchedResultsChangeInsert:
            [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;

        case NSFetchedResultsChangeDelete:
            [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;

        case NSFetchedResultsChangeUpdate:
            [self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
            break;

        case NSFetchedResultsChangeMove:
            [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
            [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]withRowAnimation:UITableViewRowAnimationFade];
            break;
    }
}


- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
    [self.tableView endUpdates];
}

@end

person Ric Levy    schedule 22.03.2011    source источник


Ответы (2)


Я нашел одну вещь, которую вы, вероятно, захотите изменить:

- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
    return YES;
}

Я не думаю, что вы хотите, чтобы строка редактирования была редактируемой. Вот код, чтобы исправить это:

- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
    return (indexPath.row != 0);
}

Возможно, вам понадобится if else, чтобы сделать его более читабельным, но если вы ленивы, этого будет достаточно.

Также не могли бы вы указать, где вы добавляете зеленый знак плюса? Я обновлю свой ответ, когда вы это сделаете.

РЕДАКТИРОВАНИЕ: проблема заключается в том, что когда вы используете смахивание для удаления, только та строка, которую вы пролистываете, переходит в режим редактирования. Строка редактирования не переходит в режим редактирования, поэтому зеленый плюс не отображается. Если кнопка удаления появляется в неправильной строке, я бы предположил, что проблема заключается в том, что когда табличное представление входит в режим редактирования, добавляется строка редактирования, которая обновляет indexPaths всех последовательных строк.

На данный момент я не могу предоставить какой-либо код для решения проблемы, но я могу дать вам идею. Вы не должны добавлять строку редактирования, когда пользователь использует смахивание для удаления. Если вы можете определить, когда используется смахивание для удаления, и не добавлять строку редактирования на основе этого, ваша проблема должна быть решена. У меня никогда не было подобной проблемы, поэтому мне никогда не приходилось этого делать. Поэтому я не могу предоставить подробную информацию о том, как это сделать.

Если вам нужно больше/лучший совет или помощь с реализацией, дайте мне знать.

person Erik B    schedule 22.03.2011
comment
Посмотрите на возвращаемый тип -tableView:canEditRowAtIndexPath:. Это BOOL. Возвращение индекса строки бессмысленно. ;п - person Jonathan Grynspan; 22.03.2011
comment
@Jonathan Grynspan indexPath.row - это целое число, и если это целое число равно 0, возвращаемое значение ложно, иначе оно истинно. Это лучше, чем писать оператор if else без автоматического завершения в stackoverflow. В реальной жизни вы хотите не сбивать с толку своих коллег. - person Erik B; 22.03.2011
comment
@Jonathan Grynspan Я отредактировал ответ, чтобы он был менее запутанным. - person Erik B; 22.03.2011
comment
@Erik B Я знаю, что такое BOOL. Я говорю, что в том виде, в котором это было написано (вы это исправили), это могло быть ошибочно принято за ошибку — и это так, поскольку, если индекс › 127, он может быть усечен до 0! - person Jonathan Grynspan; 22.03.2011
comment
@Erik К сожалению, это изменение усугубляет ситуацию - теперь оно не работает и при нажатии кнопки редактирования! Строка вставки не имеет отступа от других строк, и зеленый плюс не виден. Зеленый плюс создается в tableView:editingStyleForRowAtIndexPath (при редактировании он добавляет вверху строку UITableViewCellEditingStyleInsert). Спасибо за вашу постоянную помощь! - person Ric Levy; 22.03.2011
comment
@Ric Levy: Может быть, тебе стоит сказать @Erik B, так как это его ответ? - person Jonathan Grynspan; 22.03.2011
comment
@ Джонатан, ой, извини! Я исправил имя. - person Ric Levy; 22.03.2011
comment
@Ric Levy Я рад, что помог отследить проблему. Теперь я понимаю, почему это не работает. Я обновлю ответ, чтобы объяснить, почему он не работает, и, надеюсь, я также найду решение для вас. Я сейчас немного занят, но надеюсь, вам не придется долго ждать. - person Erik B; 22.03.2011
comment
@Erik - я пытался реализовать ваше предложение как можно лучше (будучи новичком), и это лучше, но я снова застрял. Теперь все работает, кроме удаления в режиме редактирования после нажатия кнопки «Редактировать». Я был бы очень признателен за любые дальнейшие советы, которые вы могли бы мне дать! Чтобы избежать путаницы, я задал новый вопрос который здесь. Спасибо! - person Ric Levy; 23.03.2011
comment
@Ric Levy - я полагаю, тогда вы должны принять этот ответ. Также я рассмотрел ваш новый вопрос и думаю, что у меня может быть решение для вас. Я поделюсь им с вами, как только смогу. - person Erik B; 24.03.2011

У меня была такая же проблема, и я просто исправил ее с помощью этого трюка:

Вы можете проверить, как режим редактирования был установлен на YES следующим образом:

- (void)tableView:(UITableView *)tableView willBeginEditingRowAtIndexPath:(NSIndexPath     *)indexPath
{
    editingFromSwipe = YES;
    [super tableView:tableView willBeginEditingRowAtIndexPath:indexPath];
}

- (void)tableView:(UITableView *)tableView didEndEditingRowAtIndexPath:(NSIndexPath     *)indexPath
{
    [super tableView:tableView didEndEditingRowAtIndexPath:indexPath];
    editingFromSwipe = NO;
}

И я предполагаю, что вы уже знаете, как вы поступаете с этого момента, вы создаете if-функцию, вставляющую строку-добавить только тогда, когда editFromSwipe равен нулю.

Я думаю, что мы все согласны с тем, что Apple здесь не слишком сотрудничает. Я надеюсь, что это помогает!

person Erik    schedule 04.09.2011
comment
Спасибо, это именно тот ответ, который я ищу. И OMG, еще один уровень операторов if в моем коде UITableView. - person William Entriken; 02.08.2012