Доступ к базе данных с использованием разделов в UICollection

Я разрабатываю небольшое приложение в iOS (я начинающий). Я сделал меню Core Data объекта, где у меня есть categoryName а также imageNamed сохранены. Я хочу мое UICollectionView в разделах. Но я застрял в определении различных indexPaths для разных разделов.

Я также чувствую, что мой способ доступа к данным очень плох и кажется неэффективным, так как я определяю два NSFetchedResultsController экземпляров. Как я могу получить доступ к хранилищу данных лучше?

Я получаю эту ошибку:

14-03-17 10: 02: 19.974 citiliving [1149: 70b] * Завершение работы приложения из-за необработанного исключения 'NSRangeException', причина: '* - [__ NSArrayM objectAtIndex:]: индекс 1 за пределами [0 .. 0]'

Кажется, ошибка вышла за пределы, поэтому раздел обращается к чему-то, что находится за пределами массива.

Код для доступа к данным в viewDidLoad:

- (void)viewDidLoad
{
    [super viewDidLoad];

    testAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
    NSManagedObjectContext *managedObjectContext = [appDelegate managedObjectContext];

    NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Menu"];
    NSString *cacheName = [@"Menu" stringByAppendingString:@"Cache"];

    NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"categoryName" ascending:YES];
    NSPredicate* pred = [NSPredicate predicateWithFormat:@"section = %@", @"dining"];

    fetchRequest.predicate = pred; // req is the NSFetchRequest we're configuring
    [fetchRequest setSortDescriptors:@[sortDescriptor]];

    self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:nil cacheName:cacheName ];

    NSError *error;
    if (![self.fetchedResultsController performFetch:&error])
    {
        NSLog(@"Fetch failed: %@", error);
    }
    else
    {
        NSLog(@"Records found: number %lu", (unsigned long)self.fetchedResultsController.fetchedObjects.count);     
    }

    NSFetchRequest *fetchRequestMenuTwo = [NSFetchRequest fetchRequestWithEntityName:@"Menu"];
    NSString *cacheNameTwo = [@"Menu" stringByAppendingString:@"Cache"];

    NSSortDescriptor *sortDescriptorTwo = [NSSortDescriptor sortDescriptorWithKey:@"categoryName" ascending:YES];
    NSPredicate* predTwo = [NSPredicate predicateWithFormat:@"section = %@", @"information"];

    fetchRequestMenuTwo.predicate = predTwo; // req is the NSFetchRequest we're configuring
    [fetchRequestMenuTwo setSortDescriptors:@[sortDescriptorTwo]];

    self.fetchedResultsControllerTwo = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequestMenuTwo managedObjectContext:managedObjectContext sectionNameKeyPath:nil cacheName:cacheNameTwo ];

    //NSError *error;
    if (![self.fetchedResultsControllerTwo performFetch:&error])
    {
        NSLog(@"Fetch failed: %@", error);
    }
    else
    {
        NSLog(@"Records found at 2nd fetch: number %lu", (unsigned long)self.fetchedResultsControllerTwo.fetchedObjects.count);
    }
}

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

Код, используемый для получения разделов:

-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
    switch (section) {
        case 0:
            return self.fetchedResultsController.fetchedObjects.count;
        case 1:
            return self.fetchedResultsControllerTwo.fetchedObjects.count;
        default:
            return 0;
    }
}

-(NSString *)nameForSection:(NSInteger)index
{
    switch(index)
    {
        case 0:
            return @"Section One";
        case 1:
            return @"Section Two";
        default:
            return @"Unknown";
    }
}

Наконец-то все получилось indexPath (здесь я получаю сообщение об ошибке, как только я помещаю кодировку второго раздела, иначе первый раздел работает нормально:

-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *identifier = @"Cell";

    UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];

    switch (indexPath.section) {
        case 0:
        {
            Menu *menu = (Menu *)[self.fetchedResultsController objectAtIndexPath:indexPath];
            UIImageView *cityLivingImageView = (UIImageView *)[cell viewWithTag:100];
            UILabel *cityLivingLabel = (UILabel *)[cell viewWithTag:200];
            cityLivingLabel.text = menu.categoryName;
            cityLivingImageView.image    = [UIImage imageNamed:menu.imageName];

            NSLog(@"Record found: %@", menu.categoryName );

            return cell;
        }
        case 1:
        {
            Menu *menu = (Menu *)[self.fetchedResultsControllerTwo objectAtIndexPath:indexPath];
            UIImageView *cityLivingImageView = (UIImageView *)[cell viewWithTag:100];
            UILabel *cityLivingLabel = (UILabel *)[cell viewWithTag:200];
            cityLivingLabel.text = menu.categoryName;
            cityLivingImageView.image    = [UIImage imageNamed:menu.imageName];

            NSLog(@"Record found: %@", menu.categoryName );

            return cell;        
        }
        default:
        break;
    }

    return cell;    
}

У меня есть только два раздела, один из которых имеет 5 рядов, а другой имеет 6 рядов. Я хотел сделать этот контроллер для доступа к данным и поместить все в переменные, чтобы позже я мог использовать переменные. Моя база данных - CategoryName | CategoryImage | раздел

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

2 ответа

Решение

Первый вопрос: сколько разных ценностей Menu объекты имеют для section ключ? Как правило, вы должны один контроллер результатов выборки (FRC) и использовать section как sectionNameKeyPath,

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

Ваш сбой происходит из-за того, что вы используете 2 FRC, но ко второму вы относитесь так, как будто у вас только 1. Вы создаете представление коллекции с 2 разделами, но каждый FRC понимает только один раздел. Ваш первый раздел работает, потому что номер раздела (ноль) соответствует тому, что ожидает первый FRC. Но второй раздел имеет номер раздела один, который не совпадает, поэтому вы получаете исключение диапазона.

Вы должны изменить свой код, чтобы использовать только один FRC, если вы можете (если вы хотите один раздел для каждого другого значения section в хранилище данных). Если нет, вы можете исправить ошибку, изменив код на:

NSIndexPath *fabricatedIndexPath = [NSIndexPath indexPathForItem:indexPath.item inSection:0];
Menu *menu = (Menu *)[self.fetchedResultsControllerTwo objectAtIndexPath:fabricatedIndexPath];

(немного грязно на самом деле)

Я наконец все исправил, хотя желание может сделать мой код чище, но я не знаю, как сделать из двух FRC один и использовать sectionNameKeyPath, но так как я должен подать заявку в управление, чтобы я сделал следующие изменения.

  1. Я сделал два разных кеша, как предложил мистер Уэйн, и это решило проблему с ошибками.

Но после получения двух разделов у меня возникла другая проблема выбора индекса. Поскольку я щелкал по одному разделу, но получал один и тот же indexPath, я использовал цикл if, чтобы увидеть, какой именно раздел, и использовал два преобразованных FRC для объекта, а затем назначил точный путь к разделу. Моя попытка выбрать раздел следующая. Я знаю, что это грязно, но нехватка времени заставила меня сделать это.

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:@"showCategoriesMenu"])
{
    NSIndexPath *selectedIndexPath = [[self.collectionView indexPathsForSelectedItems] objectAtIndex:0];
    SubMenuTableViewController *subMenuViewController = [segue destinationViewController];

    NSString *clickedCell;

    if (selectedIndexPath.section == 1)
    {
    Menu *menu = (Menu *)[itemsMenuTwo  objectAtIndex:selectedIndexPath.row];
        clickedCell = menu.categoryName;
    }
    else
    {
    Menu *menu = (Menu *)[itemsMenuOne  objectAtIndex:selectedIndexPath.row];
        clickedCell = menu.categoryName;
    }
    subMenuViewController.title = clickedCell;
    subMenuViewController.categoryName = clickedCell;
}
}

Спасибо за поддержку, и я действительно ценю это.

Другие вопросы по тегам