表视图部分中搜索结果对象的预期行为

时间:2014-01-16 17:29:24

标签: ios uitableview core-data uisearchbar

我有UITableView部分。每个core data object都会显示在预期的部分下方。为了测试应用程序,我添加了3个对象。

1. item 2(overdue)-> OVERDUE section
2. item 1(today)-> TODAY section
3. item 3(upcoming)->UPCOMING section.

我还实现了search bar,它通过属性todoName过滤对象(只需要匹配3个字符)。 这是应用在其第一个视图中的屏幕截图: enter image description here

如果我在搜索栏控制器中引入搜索字符串,则会在所有部分下重复显示结果对象。搜索“天”后请查看下面的示例 enter image description here

这是我的问题:

1。我需要结果对象可能会显示在上一部分下面或没有任何部分,但不会出现在所有部分下面。

2。结果对象已失去正确的>标记,并且在点击它们时不提供任何操作。

欢迎任何建议或帮助。 到目前为止,这是我的代码:

#import "ToDoItemsTableViewController.h"
#import "AppDelegate.h"
#import "AddToDoItemViewController.h"
#import "ToDoSubItemsTableViewController.h"

@interface ToDoItemsTableViewController ()

@property (nonatomic, strong)NSManagedObjectContext *managedObjectContext;
@property (nonatomic, strong)NSFetchedResultsController *fetchedResultsController;

@end

@implementation ToDoItemsTableViewController
@synthesize searchResults;

- (id)initWithStyle:(UITableViewStyle)style
{
    self = [super initWithStyle:style];
    if (self) {
        // Custom initialization
    }
    return self;
}
-(NSManagedObjectContext *)managedObjectContext{
    return [(AppDelegate*)[[UIApplication sharedApplication]delegate]managedObjectContext];
}
- (void)viewDidLoad
{
    [super viewDidLoad];


    //navigation bar background image
    [self.navigationController.navigationBar

     setBackgroundImage:[UIImage imageNamed:@"navBar.png"]

     forBarMetrics:UIBarMetricsDefault];

    NSDictionary *textAttributes = [NSDictionary dictionaryWithObjectsAndKeys:
                                    [UIColor blackColor],NSForegroundColorAttributeName,
                                    [UIColor blackColor],NSBackgroundColorAttributeName,nil];
    self.navigationController.navigationBar.titleTextAttributes = textAttributes;



    NSError *error = nil;
    if (![[self fetchedResultsController]performFetch:&error]){
        NSLog(@"Error %@",error);
        abort();
    }
    self.searchResults = [NSMutableArray arrayWithCapacity:[[self.fetchedResultsController fetchedObjects] count]];
    [self.tableView reloadData];
}
-(void) viewWillAppear:(BOOL)animated{
    [self.tableView reloadData];
}
- (void)viewDidUnload
{
    self.searchResults = nil;
}
- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{

    if ([[segue identifier]isEqualToString:@"addToDoItem"]){
        UINavigationController *navigationController = segue.destinationViewController;

        AddToDoItemViewController *addToDoItemViewController = (AddToDoItemViewController*)navigationController.topViewController;
        ToDoItem *addToDoItem = [NSEntityDescription insertNewObjectForEntityForName:@"ToDoItem" inManagedObjectContext:self.managedObjectContext];
        addToDoItem.todoDueDate = [NSDate date];
        addToDoItemViewController.addToDoItem = addToDoItem;
    }
    if ([[segue identifier] isEqualToString:@"toToDoSubItems"]){

        ToDoSubItemsTableViewController *todoSubItemsTableViewController = [segue destinationViewController];
        NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
        ToDoItem *selectedToDoItem = (ToDoItem*)[self.fetchedResultsController objectAtIndexPath:indexPath];
        todoSubItemsTableViewController.selectedToDoItem = selectedToDoItem;



    }




}



#pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{

    return [[self.fetchedResultsController sections]count];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    if (tableView == self.searchDisplayController.searchResultsTableView)
    {
        return [self.searchResults count];
    }
    else {
    id<NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections]objectAtIndex:section];
    return [sectionInfo numberOfObjects];

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

    // Configure the cell...

    ToDoItem *toDoItem = nil;



    if (tableView == self.searchDisplayController.searchResultsTableView)
    {
        if (cell==nil) {
            cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];

        }
        NSLog(@"Configuring cell to show search results");
        toDoItem = [self.searchResults objectAtIndex:indexPath.row];
        cell.textLabel.text = toDoItem.todoName;



        NSDate *fechaToDO = toDoItem.todoDueDate;

        NSDateFormatter *dateFormatter = [[NSDateFormatter alloc]init];
        [dateFormatter setDateFormat:@"EEEE, dd MMMM YYYY"];
        NSString *fechaToDo = [dateFormatter stringFromDate:fechaToDO];



        cell.detailTextLabel.text = fechaToDo;

    }
    else
    {


    ToDoItem *todoItem = [self.fetchedResultsController objectAtIndexPath:indexPath];
    cell.textLabel.text = todoItem.todoName;



    NSDate *fechaToDO = todoItem.todoDueDate;

    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc]init];
    [dateFormatter setDateFormat:@"EEEE, dd MMMM YYYY"];
    NSString *fechaToDo = [dateFormatter stringFromDate:fechaToDO];



    cell.detailTextLabel.text = fechaToDo;
    }
    return cell;
}

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
    static NSString *header = @"customHeader";

    UITableViewHeaderFooterView *vHeader;

    vHeader = [tableView dequeueReusableHeaderFooterViewWithIdentifier:header];

    if (!vHeader) {
        vHeader = [[UITableViewHeaderFooterView alloc] initWithReuseIdentifier:header];
        vHeader.textLabel.backgroundColor = [UIColor redColor];
        vHeader.textLabel.textColor = [UIColor whiteColor];

        vHeader.contentView.backgroundColor = [UIColor redColor];
    }

    if (section == 0) {
        vHeader.textLabel.backgroundColor = [UIColor redColor];
        vHeader.textLabel.textColor = [UIColor whiteColor];

        vHeader.contentView.backgroundColor = [UIColor redColor];
    }

    else if (section == 1) {
        vHeader.textLabel.backgroundColor = [UIColor orangeColor];
        vHeader.textLabel.textColor = [UIColor blueColor];

        vHeader.contentView.backgroundColor = [UIColor orangeColor];
    }
    else if (section == 2) {
        vHeader.textLabel.backgroundColor = [UIColor greenColor];
        vHeader.textLabel.textColor = [UIColor whiteColor];

        vHeader.contentView.backgroundColor = [UIColor greenColor];
    }


    vHeader.textLabel.text = [self tableView:tableView titleForHeaderInSection:section];

    return vHeader;
}
-(NSString*)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{




    id <NSFetchedResultsSectionInfo> theSection = [[self.fetchedResultsController sections]objectAtIndex:section];
    NSString *sectionname = [theSection name];

    if ([sectionname isEqualToString:@"0"]){

        NSString *valor = [NSString stringWithFormat:@"O V E R D U E   (%d)", [self.tableView
                                                             numberOfRowsInSection:section]];
        return valor;
    }
    else if ([sectionname isEqualToString:@"1"]){

        NSString *valor = [NSString stringWithFormat:@"T O D A Y   (%d)", [self.tableView
                                           numberOfRowsInSection:section]];
        return valor;
    }
    else if ([sectionname isEqualToString:@"2"]){

        NSString *valor = [NSString stringWithFormat:@"U P C O M I N G   (%d)", [self.tableView
                                                             numberOfRowsInSection:section]];
        return valor;
    }




    if ([[self.fetchedResultsController sections]count]>0){
        id<NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections]objectAtIndex:section];
        return [sectionInfo name];
    }
    else{
        return nil;
    }

}


#pragma mark - Fetched Results Controller Section

-(NSFetchedResultsController*)fetchedResultsController{

    if (_fetchedResultsController != nil){
        return _fetchedResultsController;
    }
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc]init];
    NSManagedObjectContext *context = self.managedObjectContext;
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"ToDoItem" inManagedObjectContext:context];
    [fetchRequest setEntity:entity];
    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc]initWithKey:@"todoDueDate" ascending:YES];
    NSSortDescriptor *sortDescriptor1 = [[NSSortDescriptor alloc]initWithKey:@"todoName" ascending:YES];

    NSArray *sortDescriptors = [[NSArray alloc]initWithObjects:sortDescriptor,sortDescriptor1, nil];
    fetchRequest.sortDescriptors = sortDescriptors;
    _fetchedResultsController = [[NSFetchedResultsController alloc]initWithFetchRequest:fetchRequest managedObjectContext:context sectionNameKeyPath:@"sectionIdentifier" cacheName:nil];
    _fetchedResultsController.delegate = self;
    return _fetchedResultsController;
}


#pragma mark - Fetched Results Controller Delegates

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

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

}

-(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:{

            ToDoItem *changeToDoItem = [self.fetchedResultsController objectAtIndexPath:indexPath];
            UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
            cell.textLabel.text = changeToDoItem.todoName;
            NSDate *fechaToDO = changeToDoItem.todoDueDate;

            NSDateFormatter *dateFormatter = [[NSDateFormatter alloc]init];
            [dateFormatter setDateFormat:@"EEEE, dd MMMM YYYY"];
            NSString *fechaToDo = [dateFormatter stringFromDate:fechaToDO];
            cell.detailTextLabel.text = fechaToDo;
        }
            break;
        case NSFetchedResultsChangeMove:
            [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
            [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;
    }


}

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

}





/*
// Override to support conditional editing of the table view.
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Return NO if you do not want the specified item to be editable.
    return YES;
}
*/


// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (editingStyle == UITableViewCellEditingStyleDelete) {
        // Delete the row from the data source
        NSManagedObjectContext *context = [self managedObjectContext];
        ToDoItem *ToDoItemToDelete = [self.fetchedResultsController objectAtIndexPath:indexPath];
        [context deleteObject:ToDoItemToDelete];


        NSError *error = nil;
        if (![context save:&error]){
            NSLog(@"Error: %@",error);
        }

    }
}


/*
// Override to support rearranging the table view.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
}
*/

/*
// Override to support conditional rearranging of the table view.
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Return NO if you do not want the item to be re-orderable.
    return YES;
}
*/

/*
#pragma mark - Navigation

// In a story board-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    // Get the new view controller using [segue destinationViewController].
    // Pass the selected object to the new view controller.
}

 */
#pragma mark -
#pragma mark Content Filtering

-(void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope {
    self.searchResults = [[self.fetchedResultsController fetchedObjects] filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
        ToDoItem * item = evaluatedObject;
        NSString* name = item.todoName;

        //searchText having length < 3 should not be considered
        if (!!searchText && [searchText length] < 3) {
            return YES;
        }

        if ([scope isEqualToString:@"All"] || [name isEqualToString:scope])  {
            return ([name rangeOfString:searchText].location != NSNotFound);
        }
        return NO; //if nothing matches
    }]];
}
#pragma mark -
#pragma mark UISearchDisplayController Delegate Methods

- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
    [self filterContentForSearchText:searchString scope:@"All"];
    return YES;
}

- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchScope:(NSInteger)searchOption
{
    [self filterContentForSearchText:[self.searchDisplayController.searchBar text] scope:@"All"];
    return YES;
}

@end

我更改了commitEditingStyle,如下所示:

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
    {
    if (tableView == self.searchDisplayController.searchResultsTableView)
        {
            [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
        }
     else   if (editingStyle == UITableViewCellEditingStyleDelete) {
            // Delete the row from the data source
            NSManagedObjectContext *context = [self managedObjectContext];
            ToDoItem *ToDoItemToDelete = [self.fetchedResultsController objectAtIndexPath:indexPath];
            [context deleteObject:ToDoItemToDelete];


            NSError *error = nil;
            if (![context save:&error]){
                NSLog(@"Error: %@",error);
            }





    }
    }

但是在尝试删除搜索结果对象时,该应用会显示以下异常:

Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0.  The number of rows contained in an existing section after the update (1) must be equal to the number of rows contained in that section before the update (1), plus or minus the number of rows inserted or deleted from that section (0 inserted, 1 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).'
*** First throw call stack:

请检查我更改的方法以检测问题的位置,谢谢...


这是完整的例外,也许它可以帮助您检测问题:

[__NSArrayI removeObjectAtIndex:]: unrecognized selector sent to instance 0x89be750
2014-01-16 22:54:08.530 To-Do Pro Light[2319:a0b] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSArrayI removeObjectAtIndex:]: unrecognized selector sent to instance 0x89be750'
*** First throw call stack:
(
    0   CoreFoundation                      0x01aab5e4 __exceptionPreprocess + 180
    1   libobjc.A.dylib                     0x0182e8b6 objc_exception_throw + 44
    2   CoreFoundation                      0x01b48903 -[NSObject(NSObject) doesNotRecognizeSelector:] + 275
    3   CoreFoundation                      0x01a9b90b ___forwarding___ + 1019
    4   CoreFoundation                      0x01a9b4ee _CF_forwarding_prep_0 + 14
    5   To-Do Pro Light                     0x00004eda -[ToDoItemsTableViewController tableView:commitEditingStyle:forRowAtIndexPath:] + 298
    6   UIKit                               0x0068cba3 -[UITableView animateDeletionOfRowWithCell:] + 107
    7   UIKit                               0x0080c695 -[UITableViewCell _swipeDeleteButtonPushed] + 70
    8   libobjc.A.dylib                     0x01840874 -[NSObject performSelector:withObject:withObject:] + 77
    9   UIKit                               0x0059e0c2 -[UIApplication sendAction:to:from:forEvent:] + 108
    10  UIKit                               0x0059e04e -[UIApplication sendAction:toTarget:fromSender:forEvent:] + 61
    11  UIKit                               0x006960c1 -[UIControl sendAction:to:forEvent:] + 66
    12  UIKit                               0x00696484 -[UIControl _sendActionsForEvents:withEvent:] + 577
    13  UIKit                               0x00695733 -[UIControl touchesEnded:withEvent:] + 641
    14  UIKit                               0x00910c7f _UIGestureRecognizerUpdate + 7166
    15  UIKit                               0x005db19a -[UIWindow _sendGesturesForEvent:] + 1291
    16  UIKit                               0x005dc0ba -[UIWindow sendEvent:] + 1030
    17  UIKit                               0x005afe86 -[UIApplication sendEvent:] + 242
    18  UIKit                               0x0059a18f _UIApplicationHandleEventQueue + 11421
    19  CoreFoundation                      0x01a3483f __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 15
    20  CoreFoundation                      0x01a341cb __CFRunLoopDoSources0 + 235
    21  CoreFoundation                      0x01a5129e __CFRunLoopRun + 910
    22  CoreFoundation                      0x01a50ac3 CFRunLoopRunSpecific + 467
    23  CoreFoundation                      0x01a508db CFRunLoopRunInMode + 123
    24  GraphicsServices                    0x038bc9e2 GSEventRunModal + 192
    25  GraphicsServices                    0x038bc809 GSEventRun + 104
    26  UIKit                               0x0059cd3b UIApplicationMain + 1225
    27  To-Do Pro Light                     0x00007d2d main + 141
    28  libdyld.dylib                       0x020e7725 start + 0
)
libc++abi.dylib: terminating with uncaught exception of type NSException

1 个答案:

答案 0 :(得分:1)

您需要更改numberOfSectionsInTableView:之类的

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    if (tableView == self.searchDisplayController.searchResultsTableView)
    {
       return 1;
    }
    else
    { 
       return [[self.fetchedResultsController sections]count];
    }
}

编辑关于崩溃的第二个问题。

更改commitEditingStyle:forRowAtIndexPath:之类的内容:

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
     if (tableView == self.searchDisplayController.searchResultsTableView)
     {
         [self.searchResults removeObjectAtIndex:indexPath.row];
         [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
     }
     else
     {
         if (editingStyle == UITableViewCellEditingStyleDelete)
         {
             // Delete the row from the data source
             NSManagedObjectContext *context = [self managedObjectContext];
             ToDoItem *ToDoItemToDelete = [self.fetchedResultsController objectAtIndexPath:indexPath];
             [context deleteObject:ToDoItemToDelete];


              NSError *error = nil;
              if (![context save:&error])
              {
                 NSLog(@"Error: %@",error);
              }
          }
     }
}