2012-05-07 5 views
0

Я создаю простой контрольный список в UITableView. Я добавил возможности редактирования, разместив обычную кнопку редактирования на панели навигации. Кнопка включает режим редактирования. Режим редактирования отлично работает, пока я не добавлю специальные флажки (как кнопки) в аксессуар каждой ячейки. Я использую этот код, чтобы сделать это:Проблемы с использованием пользовательской кнопки в представлении Access UITableViewCell

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

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 

    if (!cell) { 
     cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]; 

     // put the tasks into the cell 
     [[cell textLabel] setText:[NSString stringWithFormat:@"%@", [[[CLTaskStore sharedStore] allTasks] objectAtIndex:[indexPath row]]]]; 

     // put the checkbox into the cell's accessory view 
     UIButton *checkBox = [UIButton buttonWithType:UIButtonTypeCustom]; 
     [checkBox setImage:[UIImage imageNamed:@"checkbox.png"] forState:UIControlStateNormal]; 
     [checkBox setImage:[UIImage imageNamed:@"checkbox-checked.png"] forState:UIControlStateSelected]; 
     checkBox.frame = CGRectMake(0, 0, 30, 30); 
     checkBox.userInteractionEnabled = YES; 
     [checkBox addTarget:self action:@selector(didCheckTask:) forControlEvents:UIControlEventTouchDown]; 
     cell.accessoryView = checkBox; 

     // put the index path in the button's tag 
     checkBox.tag = [indexPath row]; 
    } 
    return cell; 
} 

Как вы можете видеть, я использую тег баттона передать indexPath моему didCheckTask: метод:

- (void)didCheckTask:(UIButton *)button 
{ 
    task = [[[CLTaskStore sharedStore] allTasks] objectAtIndex:button.tag]; 
    task.didComplete = YES; 

    // toggle checkbox 
    button.selected = !button.selected; 

    [checkList reloadData]; 
} 

Флажки и редактирование все, кажется, работают нормально на поверхности. Однако возникает большая проблема, когда я вхожу в режим редактирования, удаляю элемент в таблицеView, а затем пытаюсь использовать этот флажок. Например, если удалить первый элемент в Tableview, а затем попытаться проверить флажок последнего пункта, программа падает с:

2012-05-06 21: 45: 40,645 CheckList [16022: F803] * Нагрузочный приложение из-за для неперехваченного исключения 'NSRangeException', причина: '* - [__ NSArrayM objectAtIndex]: индекс 4 за пределами [0 .. 3]

Я пытался выяснить, источник этой ошибки, но мне не повезло. Я мог бы действительно использовать некоторую помощь - я новичок в какао. Далее следует соответствующий код.

CLTaskFactory.h

#import <Foundation/Foundation.h> 

@interface CLTaskFactory : NSObject 
{ 
    NSString *taskName; 
    BOOL didComplete; 
} 

@property NSString *taskName; 

- (void)setDidComplete:(BOOL)dc; 
- (BOOL)didComplete; 

@end 

CLTaskFactory.m

#import "CLTaskFactory.h" 

@implementation CLTaskFactory 

@synthesize taskName; 

- (void)setDidComplete:(BOOL)dc 
{ 
    didComplete = dc; 
} 

- (BOOL)didComplete 
{ 
    return didComplete; 
} 

- (NSString *)description 
{ 
    // override the description 
    NSString *descriptionString = [[NSString alloc] initWithFormat:@"%@", taskName]; 
    return descriptionString; 
} 

@end 

CLTaskStore.h

#import <Foundation/Foundation.h> 

@class CLTaskFactory; 

@interface CLTaskStore : NSObject 
{ 
    NSMutableArray *allTasks; 
} 

+ (CLTaskStore *)sharedStore; 

- (NSMutableArray *)allTasks; 
- (void)addTask:(CLTaskFactory *)task; 
- (void)removeTask:(CLTaskFactory *)task; 
- (void)moveTaskAtIndex:(int)from toIndex:(int)to; 

@end 

CLTaskStore.m

#import "CLTaskStore.h" 

    @implementation CLTaskStore 

    + (id)allocWithZone:(NSZone *)zone 
    { 
     return [self sharedStore]; 
    } 

    + (CLTaskStore *)sharedStore 
    { 
     static CLTaskStore *sharedStore = nil; 
     if (!sharedStore) { 
      sharedStore = [[super allocWithZone:nil] init]; 
     } 
     return sharedStore; 
    } 

    - (id)init 
    { 
     self = [super init]; 
     if (self) { 
      allTasks = [[NSMutableArray alloc] init]; 
     } 
     return self; 
    } 

    - (NSMutableArray *)allTasks 
    { 
     return allTasks; 
    } 

    - (void)addTask:(CLTaskFactory *)task 
    { 
     [allTasks addObject:task]; 
    } 

    - (void)removeTask:(CLTaskFactory *)task 
    { 
     [allTasks removeObjectIdenticalTo:task]; 

     NSInteger taskCount = [allTasks count]; 
     NSLog(@"Removed: %@, there are now %d remaining tasks, they are:", task, taskCount); 
     for (int i = 0; i < taskCount; i++) { 
      NSLog(@"%@", [[[CLTaskStore sharedStore] allTasks] objectAtIndex:i]); 
     } 
    } 

    - (void)moveTaskAtIndex:(int)from toIndex:(int)to 
    { 
     if (from == to) { 
      return; 
     } 

     CLTaskFactory *task = [allTasks objectAtIndex:from]; 
     [allTasks removeObjectAtIndex:from]; 
     [allTasks insertObject:task atIndex:to]; 
    } 

    @end 


CLChecklistViewController.h 

    #import <Foundation/Foundation.h> 

    @class CLTaskFactory; 

    @interface CLCheckListViewController : UIViewController 
    { 
     CLTaskFactory *task; 
    } 

    - (void)didCheckTask:(UIButton *)button; 

    @end 

CLCheckListViewController.m 

#import "CLCheckListViewController.h" 
#import "CLTaskFactory.h" 
#import "CLTaskStore.h" 

@implementation CLCheckListViewController 
{ 
    __weak IBOutlet UITableView *checkList; 
} 

- (id)init 
{ 
    self = [super init]; 
    if (self) { 
     // add five sample tasks 
     CLTaskFactory *task1 = [[CLTaskFactory alloc] init]; 
     [task1 setTaskName:@"Task 1"]; 
     [task1 setDidComplete:NO]; 
     [[CLTaskStore sharedStore] addTask:task1]; 

     CLTaskFactory *task2 = [[CLTaskFactory alloc] init]; 
     [task2 setTaskName:@"Task 2"]; 
     [task2 setDidComplete:NO]; 
     [[CLTaskStore sharedStore] addTask:task2]; 

     CLTaskFactory *task3 = [[CLTaskFactory alloc] init]; 
     [task3 setTaskName:@"Task 3"]; 
     [task3 setDidComplete:NO]; 
     [[CLTaskStore sharedStore] addTask:task3]; 

     CLTaskFactory *task4 = [[CLTaskFactory alloc] init]; 
     [task4 setTaskName:@"Task 4"]; 
     [task4 setDidComplete:NO]; 
     [[CLTaskStore sharedStore] addTask:task4]; 

     CLTaskFactory *task5 = [[CLTaskFactory alloc] init]; 
     [task5 setTaskName:@"Task 5"]; 
     [task5 setDidComplete:NO]; 
     [[CLTaskStore sharedStore] addTask:task5]; 
    } 
    return self; 
} 

- (void)viewDidLoad 
{ 
    [[self navigationItem] setTitle:@"Checklist"]; 

    // create edit button 
    [[self navigationItem] setLeftBarButtonItem:[self editButtonItem]]; 
} 

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 
{ 
    return [[[CLTaskStore sharedStore] allTasks] count]; 
} 

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

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 

    if (!cell) { 
     cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]; 

     // put the tasks into the cell 
     [[cell textLabel] setText:[NSString stringWithFormat:@"%@", [[[CLTaskStore sharedStore] allTasks] objectAtIndex:[indexPath row]]]]; 

     // put the checkbox into the cell's accessory view 
     UIButton *checkBox = [UIButton buttonWithType:UIButtonTypeCustom]; 
     [checkBox setImage:[UIImage imageNamed:@"checkbox.png"] forState:UIControlStateNormal]; 
     [checkBox setImage:[UIImage imageNamed:@"checkbox-checked.png"] forState:UIControlStateSelected]; 
     checkBox.frame = CGRectMake(0, 0, 30, 30); 
     checkBox.userInteractionEnabled = YES; 
     [checkBox addTarget:self action:@selector(didCheckTask:) forControlEvents:UIControlEventTouchDown]; 
     cell.accessoryView = checkBox; 

     // put the index path in the button's tag 
     checkBox.tag = [indexPath row]; 
    } 
    return cell; 
} 

- (void)didCheckTask:(UIButton *)button 
{ 
    task = [[[CLTaskStore sharedStore] allTasks] objectAtIndex:button.tag]; 
    task.didComplete = YES; 

    // toggle checkbox 
    button.selected = !button.selected; 

    [checkList reloadData]; 
} 

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

    // set editing mode 
    if (editing) { 
     self.navigationItem.title = @"Edit Checklist"; 
     [checkList setEditing:YES]; 
    } else { 
     self.navigationItem.title = @"Checklist"; 
     [checkList setEditing:NO]; 
    } 
} 

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle 
              forRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    // remove guest from store 
    if (editingStyle == UITableViewCellEditingStyleDelete) { 

     task = [[[CLTaskStore sharedStore] allTasks] objectAtIndex:[indexPath row]]; 
     [[CLTaskStore sharedStore] removeTask:task]; 

     // remove guest from table view 
     [checkList deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; 
    } 
} 

- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath 
{ 
    [[CLTaskStore sharedStore] moveTaskAtIndex:[sourceIndexPath row] toIndex:[destinationIndexPath row]]; 
} 

@end 

Большое вам спасибо за вашу помощь и знания!

отредактирован:

я изменил два метода с зацикливание NSLogs, чтобы получить некоторое представление. Во-первых, CLTaskStore:

- (void)removeTask:(CLTaskFactory *)task 
{ 
    [allTasks removeObjectIdenticalTo:task]; 

    NSInteger taskCount = [allTasks count]; 
    NSLog(@"Removed: %@, there are now %d remaining tasks, they are:", task, taskCount); 
    for (int i = 0; i < taskCount; i++) { 
     NSLog(@"%@, status: %@", [[[CLTaskStore sharedStore] allTasks] objectAtIndex:i], [[[[CLTaskStore sharedStore] allTasks] objectAtIndex:i] didComplete][email protected]"YES":@"NO"); 
    } 
} 

Во-вторых, CLTaskListViewController:

- (void)didCheckTask:(UIButton *)button 
{ 
    task = [[[CLTaskStore sharedStore] allTasks] objectAtIndex:button.tag]; 
    task.didComplete = YES; 

    NSInteger taskCount = [[[CLTaskStore sharedStore] allTasks] count]; 
    for (int i = 0; i < taskCount; i++) { 
     NSLog(@"%@, status: %@", [[[CLTaskStore sharedStore] allTasks] objectAtIndex:i], [[[[CLTaskStore sharedStore] allTasks] objectAtIndex:i] didComplete][email protected]"YES":@"NO"); 
    } 

    // toggle checkbox 
    button.selected = !button.selected; 

    [checkList reloadData]; 
} 

я заметил две вещи. Если я удалю вверх, снизу вверх, проблем нет. Я могу проверить все - все работает. Однако, если я удалю первую строку, а затем проверит последнюю строку, программа выйдет из строя. NSLog от удаления является чистым, его работа прекрасна.

Если я удаляю первую строку и проверяю четвертую строку, проверяется NSLog из отчета CLTaskStore, строка 5.

В этом проблема. После удаления они находятся в определенном порядке.

+0

В методе didCheckTask вы можете поместить NSLog в начале метода, а другой - непосредственно перед [checkList reloadData]. И попробуйте снова создать ошибку. Посмотрите, сможете ли вы видеть, какой журнал показывался перед сбоем приложения? – user523234

+0

Благодарим за помощь. Я попробовал это. Первый печатный журнал, второй журнал (прямо перед reloadData) не печатался. Похоже, индексирование искажено, но я не могу, чтобы жизнь меня видна там, где это происходит. Является ли использование тега кнопки надежным в этом типе ситуации? (то есть в сочетании с редактированием)? – mySilmaril

+0

Похоже, что массив allTasks имеет меньше элементов, чем ваше табличное представление после удаления. Вы должны поместить некоторые NSLogs для проверки после удаления, что ваш массив allTasks содержит правильные элементы и согласован с вашим текущим представлением таблицы. – user523234

ответ

2

Вся ваша проблема связана с плохой идеей использования тегов, чтобы указать, в какой строке находится кнопка. Это достаточно плохо, если вы не удаляете строки из источника данных, но когда вы находитесь, это проблема вы можете столкнуться.

Использование местоположения постученного элемента в представлении таблицы и получение указательного пути местоположения из представления таблицы является гораздо более надежным и работает с редактируемыми таблицами и таблицами с несколькими разделами. См. Пример кода в моем ответе here.

Если вы делаете это так, переиндексации не требуется.

+0

Спасибо! Я думал, что это, вероятно, было мертвым два дня назад, но мне сказали, что это можно сделать. Часть быть нобом. Я буду перекодировать, используя ваше предложение. Я дам вам знать, как это происходит. – mySilmaril

+0

A +, очень приятно. Огромное спасибо. – mySilmaril

0

Когда кнопка удаления нажата после ввода режима редактирования для вашего TableView, вы должны удалить соответствующий элемент данных из источника данных.Ваш код показывает, что у вас есть метод removeTask:, но я не вижу, где вы на самом деле вызываете этот метод для удаления соответствующей записи задачи из вашего источника данных. Хорошее место для этого было бы в представлении таблицы: commitEditingStyle: forRowAtIndexPath: метод в вашем контроллере просмотра.

Поскольку вы удаляете соответствующий элемент в источнике данных, дальнейшее изучение кода показывает, что ваши значения тега флажка по-прежнему имеют свои исходные значения. Если вы удалите элемент tableView перед последним, попробуйте проверить последний, ваш метод didCheckTask попытается получить доступ к исходному значению строки indexPath, которое в настоящее время не существует и вызывает исключение границ. Если вы удалите первые две ячейки, то последние два элемента tableView будут вызывать исключения и т. Д.

Он не будет работать в методе didCheckTask, но в методе removeTask: после удаления объекта из вашего источника данных проведите цикл по оставшимся объектам и установите каждый тег равным его соответствующему индексу массива. В методе moveTaskAtIndex: toIndex: после того, как вы перемещаете записи массива из-за пользовательских параметров переупорядочения, выполните одно и то же - пропустите массив и установите каждый тег равным его индексу в массиве.

+0

Благодарим за помощь. Однако я не уверен, что следую. Мой CLChecklistViewController - это мой делегат и источник данных. Когда введен режим редактирования, сообщение tableView: commitEditingStyle: forRowAtIndexPath: отправляется в источник данных. В этом методе я делаю две вещи: (1) удаляет задачу из одноэлементного объекта CLTaskStore и (2) удаляет задачу из представления таблицы. Метод вызывается из кнопки удаления * в режиме * редактирования. Обратите внимание, что удаление работает как ожидалось МОСТ времени. Его близко, но не совсем правильно. Опять же, когда я удаляю первый элемент, а затем пытаюсь проверить последний элемент ... проблемы. – mySilmaril

+0

Извините, не видел кода для этого, поэтому не знал, удаляете ли вы запись из источника данных. – Steve

+0

Я думаю, что ваша проблема проистекает из того, что ваши теги checkbox все еще имеют свои исходные значения. Поэтому, когда вы пытаетесь проверить последний флажок, он был пятой ячейкой в ​​вашем представлении таблицы и, следовательно, имеет значение строки indexPath, равное 4. Когда ячейка создана, значение тэга флажка установлено на это. Удалите элемент, предшествующий этой ячейке, и если вы не перенумеруете свои теги флажка, они будут содержать исходные значения строки indexPath. Проблема возникает только в последнем элементе таблицы, потому что это единственный, индекс которого приведет к нарушению границ. См. Отредактированный ответ. – Steve

 Смежные вопросы

  • Нет связанных вопросов^_^