对UITableviewController进行子类化以为Core Data制作模板

时间:2012-04-16 11:20:33

标签: objective-c cocoa core-data nsfetchedresultscontroller

我正在重构已经在应用商店中的应用程序。

在这个应用程序中,我有一个带有两个选项卡的UITabBar。每个选项卡都包含一个tableview,其中包含Customers和Inventory列表。 tableview由核心数据支持,我有适当的方法来滑动表视图以更改数据的排序。

这两个控制器与我传递给NSFetchedResultsController的实体的名称相同。我的目标是创建一个超类,它将成为这两个视图的模板(因为我们在其他应用程序中重用这些视图),因此我们可以快速构建未来项目的原型。

然而,我的尝试失败了。我知道问题在于NSFetchedResultsController,但我只是不知道为什么。当我尝试运行应用程序时会触发错误

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSArrayM insertObject:atIndex:]: object cannot be nil'

这是我的超类和一个视图控制器的代码。如果有人有任何见解,我会非常感激。

FocusTableViewController.h

#import <UIKit/UIKit.h>

@interface FocusTableViewController : UITableViewController<NSFetchedResultsControllerDelegate, UISearchBarDelegate, UISearchDisplayDelegate, UITableViewDelegate, UITableViewDataSource>
{
@protected
    //Remembering where the tableview was when you click through on an item
    CGPoint savedScrollPosition;

    //Our Shit for sorting
    NSString *dEntity;
    NSString *dCacheName;

    NSString *dSectionKey0;
    NSString *dSectionKey1;

    NSString *dSortKey0;
    NSString *dSortKey1;

    //The Current sorting criteria
    //These have to be mutable, so they can change
    NSMutableString *currentKey;
    NSMutableString *currentSectionKey;

    //Filter Predicate For Searching
    NSString *dFilterPredicate;
}

@property (nonatomic) BOOL searchIsActive;

/** DAO **/
@property (nonatomic, strong) NSFetchedResultsController *fetchedResultsController;
@property (nonatomic, strong) NSManagedObjectContext *managedObjectContext;
@property (nonatomic, strong) UISearchDisplayController *searchDisplayController;

/** Custom Constructor **/
- (id)initWithTitle:(NSString *)title;

/** Search Bar **/
- (void)setupSearchBar;
- (void)makeSearchBarActive:(id)sender;

/** Sorting Methods **/
- (void)sortTableView;
- (void)changeFetchData;

@end

FocusTableViewController.m

#import "FocusTableViewController.h"
#import "AppDelegate.h"

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

@implementation FocusTableViewController
@synthesize fetchedResultsController = __fetchedResultsController;
@synthesize managedObjectContext = __managedObjectContext;
@synthesize searchDisplayController = __searchDisplayController;
@synthesize searchIsActive;

- (id)initWithTitle:(NSString *)title
{
    self = [super initWithStyle:UITableViewStylePlain];
    if (self) {
        self.title = title;
        [self setupSearchBar];
        self.tableView.dataSource = self;
        self.tableView.delegate = self;
    }
    return self;
}

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

- (void)viewDidLoad
{
    [super viewDidLoad];

    if (__managedObjectContext == nil) 
    { 
        __managedObjectContext = [(AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext]; 
    }

    self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemSearch target:self action:@selector(makeSearchBarActive:)];
}

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    if (savedScrollPosition.y == CGPointZero.y) 
    {
        [self.tableView setContentOffset:CGPointMake(0, 44.f) animated:NO];
    }
    else {
        [self.tableView setContentOffset:savedScrollPosition animated:NO];
    }
}

- (void)viewDidUnload
{
    [super viewDidUnload];
    // Release any retained subviews of the main view.
    // e.g. self.myOutlet = nil;
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    return 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];
    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:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
    }
    [self configureCell:cell atIndexPath:indexPath];

    return cell;
}

- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
    NSManagedObject *managedObject = [self.fetchedResultsController objectAtIndexPath:indexPath];

    if ([currentKey isEqualToString:dSortKey0])
    {
        cell.textLabel.text = [[managedObject valueForKey:@"title"] description];
    }
    else 
    {
        cell.textLabel.text = [NSString stringWithFormat:@"%@ - %@",[[managedObject valueForKey:@"accountno"] description],[[managedObject valueForKey:@"title"] description]];
    }
}

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


// Support conditional rearranging of the table view.
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
    return YES;
}

#pragma mark - Table View Customisation
// Creates the scrubber on the right hand side of the list
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView
{
    if (searchIsActive)
    {
        return [__fetchedResultsController sectionIndexTitles];
    }
    else
    {
        return [[NSArray arrayWithObject:@"{search}"]arrayByAddingObjectsFromArray:[__fetchedResultsController sectionIndexTitles]];
    }
}

- (NSInteger) tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index
{
    if (index == 0) 
    {
        [tableView setContentOffset:CGPointZero animated:NO];
        return NSNotFound;
    }
    return index;
}

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
    if (!(section == 0 && [self.tableView numberOfSections] == 1)) 
    {
        id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
        return [sectionInfo name];  
    }
    return nil;
}

#pragma mark - Table view delegate

#pragma mark - Search Bar

- (void)setupSearchBar
{
    UISearchBar *mySearchBar = [[UISearchBar alloc] init];
    mySearchBar.delegate = self;
    [mySearchBar setAutocapitalizationType:UITextAutocapitalizationTypeNone];
    [mySearchBar sizeToFit];
    self.tableView.tableHeaderView = mySearchBar;

    __searchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar:mySearchBar contentsController:self];

    [self setSearchDisplayController:__searchDisplayController];
    [__searchDisplayController setDelegate:self];
    [__searchDisplayController setSearchResultsDataSource:self];
}

- (void)makeSearchBarActive:(id)sender
{
    [__searchDisplayController setActive:YES animated:YES];
}

#pragma mark -
#pragma mark Content Filtering

- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
    [NSFetchedResultsController deleteCacheWithName:dCacheName];  

    NSFetchRequest *aRequest = [[self fetchedResultsController] fetchRequest];

    NSPredicate *predicate = [NSPredicate predicateWithFormat:dFilterPredicate,  searchText];

    [aRequest setPredicate:predicate];

    NSError *error = nil;
    if (![[self fetchedResultsController] performFetch:&error]) 
    {
        //_ERROR_ Save Error
    }  

}

#pragma mark -
#pragma mark UISearchDisplayController Delegate Methods

- (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller
{
    [self setSearchIsActive:YES];
}

- (void)searchDisplayControllerDidEndSearch:(UISearchDisplayController *)controller 
{
    [NSFetchedResultsController deleteCacheWithName:dCacheName];

    self.fetchedResultsController = nil;

    //For some reason the iPad version can't calculate whether there's a navbar or not
    //see : http://stackoverflow.com/questions/6591674/uisearchdisplaycontroller-gray-overlay-not-fully-covering-table
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
        [self.tableView setContentOffset:CGPointMake(0, 44.f) animated:NO];   
    }
}

- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
    [self filterContentForSearchText:searchString scope:[[self.searchDisplayController.searchBar scopeButtonTitles] objectAtIndex:[self.searchDisplayController.searchBar selectedScopeButtonIndex]]];

    // Return YES to cause the search result table view to be reloaded.
    return YES;
}

- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchScope:(NSInteger)searchOption
{
    [self filterContentForSearchText:[self.searchDisplayController.searchBar text] scope:[[self.searchDisplayController.searchBar scopeButtonTitles] objectAtIndex:searchOption]];

    // Return YES to cause the search result table view to be reloaded.
    return YES;
}

- (void)searchDisplayControllerDidBeginSearch:(UISearchDisplayController *)controller
{
    /*
     Because the searchResultsTableView will be released and allocated 
     automatically, so each time we start to begin search, we set its 
     delegate here.
     */
    [self.searchDisplayController.searchResultsTableView setDelegate:self];
}

#pragma mark - Sort Table View

- (void)changeFetchData
{
    self.fetchedResultsController.delegate = nil;
    __fetchedResultsController = nil;

    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:dEntity inManagedObjectContext:__managedObjectContext];
    [fetchRequest setEntity:entity];

    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:currentKey ascending:YES selector:@selector(caseInsensitiveCompare:)];
    NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
    [fetchRequest setSortDescriptors:sortDescriptors];

    NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:__managedObjectContext sectionNameKeyPath:currentSectionKey cacheName:nil];
    self.fetchedResultsController = aFetchedResultsController;
    __fetchedResultsController.delegate = self;


    NSError *error;
    if (![[self fetchedResultsController] performFetch:&error]) 
    {
        //_ERROR_
        // Update to handle the error appropriately.
        NSLog(@"Fetch failed");
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        exit(-1);  // Fail
    }

    [self.tableView reloadData];
}


- (void)sortTableView
{
    if ([currentKey isEqualToString:dSortKey0]) 
    {
        [currentKey setString:dSortKey1];
        [currentSectionKey setString:dSectionKey1];
    }


    else if ([currentKey isEqualToString:dSortKey1]) 
    {
        [currentKey setString:dSortKey0];
        [currentSectionKey setString:dSectionKey0];

    }

    [NSFetchedResultsController deleteCacheWithName:dCacheName];  
    [self.tableView reloadData];

    [self changeFetchData];
}

#pragma mark - Fetched results controller
- (NSFetchedResultsController *)fetchedResultsController
{
    if (__fetchedResultsController != nil) {
        return __fetchedResultsController;
    }

    // Create the fetch request for the entity.
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    // Configure the Entity
    NSEntityDescription *entity = [NSEntityDescription entityForName:dEntity inManagedObjectContext:__managedObjectContext];
    [fetchRequest setEntity:entity];

    [fetchRequest setFetchBatchSize:20];

    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"deleted == 0"];
    [fetchRequest setPredicate:predicate];

    //Configure Sort Descriptors
    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:currentKey ascending:YES selector:@selector(caseInsensitiveCompare:)];
    NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
    [fetchRequest setSortDescriptors:sortDescriptors];

    [fetchRequest setReturnsObjectsAsFaults:NO];

    // nil for section name key path means "no sections".
    NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:__managedObjectContext sectionNameKeyPath:currentSectionKey cacheName:dCacheName];
    aFetchedResultsController.delegate = self;

    self.fetchedResultsController = aFetchedResultsController;

    NSError *error = nil;
    if (![self.fetchedResultsController performFetch:&error])
    {
        //_ERROR_
        NSLog(@"The error is %@",error);
    }

    return __fetchedResultsController;
}

#pragma mark - Fetched results controller delegate

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller 
{
    if ([self searchIsActive]) {
        [[[self searchDisplayController] searchResultsTableView] beginUpdates];
    }
    else  {
        [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 
{
    if ([self searchIsActive]) {
        [[[self searchDisplayController] searchResultsTableView] endUpdates];
    }
    else  {
        [self.tableView reloadData];
        [self.tableView endUpdates];
    }
}

@end

CustomerViewController_iPhone.h为空白,这是.m

#import "CustomerViewController_iPhone.h"
#import "CustomerDetailViewController_iPhone.h"

@interface CustomerViewController_iPhone ()
@end

@implementation CustomerViewController_iPhone

- (id)initWithTitle:(NSString *)title
{
    self = [super initWithTitle:title];
    if (self) {
        dEntity             = @"Customer";
        dCacheName          = @"Customer_Cache";

        dSectionKey0        = @"nameFirstLetter";
        dSectionKey1        = @"accountNoFirstNumber";

        dSortKey0           = @"title";
        dSortKey1           = @"accountno";

        dFilterPredicate    = @"(title contains[cd] %@ OR accountno contains[cd] %@) AND deleted == 0";
    }
    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    currentKey = [dSortKey0 mutableCopy];
    currentSectionKey = [dSectionKey0 mutableCopy];
}

- (void)viewDidUnload
{
    [super viewDidUnload];
    // Release any retained subviews of the main view.
}

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    savedScrollPosition = [tableView contentOffset];
    CustomerDetailViewController_iPhone *detail = [[CustomerDetailViewController_iPhone alloc] initWithNibName:@"CustomerDetailViewController_iPhone" bundle:nil];
    [self.navigationController pushViewController:detail animated:YES];
}

@end

修改

我再过3个小时无法回答我自己的问题,因为我没有足够的声誉。这是我写的:

我发现了问题。当我实例化视图控制器时,第一次调用NSFetchedResultsController时,变量require为null。愚蠢我知道。也许,如果我之前发布了这段代码,有人可能已经接受了答案。

/** Set up the Customers TableView **/
    NSString *customerTitle = NSLocalizedString(@"Customers", @"The user's list of customers");
    CustomerViewController_iPhone *customers = [[CustomerViewController_iPhone alloc] initWithTitle:customerTitle];
    UINavigationController *customers_nav = [[UINavigationController alloc] initWithRootViewController:customers];

感谢Nikita和Phillip的帮助。

0 个答案:

没有答案