代码之家  ›  专栏  ›  技术社区  ›  Greg Combs

NSFetchedResultsController,其节由字符串的第一个字母创建

  •  70
  • Greg Combs  · 技术社区  · 16 年前

    在iPhone上学习核心数据。关于核心数据用节填充表视图的例子似乎很少。这个 CoreDataBooks 示例使用节,但它们是从模型中的完整字符串生成的。我想把核心数据表按姓氏的第一个字母(地址簿的第一个字母)组织成几个部分。

    我可以进去为每个人创建另一个属性,即一个字母,以充当分区,但这似乎很困难。

    这是我的开始。。。这个把戏似乎在愚弄观众 sectionNameKeyPath

    - (NSFetchedResultsController *)fetchedResultsController {
    //.........SOME STUFF DELETED
        // Edit the sort key as appropriate.
        NSSortDescriptor *orderDescriptor = [[NSSortDescriptor alloc] initWithKey:@"personName" ascending:YES];
        NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:orderDescriptor, nil];
    
        [fetchRequest setSortDescriptors:sortDescriptors];
        // Edit the section name key path and cache name if appropriate.
        // nil for section name key path means "no sections".
        NSFetchedResultsController *aFetchedResultsController = 
                [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest 
                managedObjectContext:managedObjectContext 
                sectionNameKeyPath:@"personName" cacheName:@"Root"];
    //....
    }
    
    7 回复  |  直到 13 年前
        1
  •  62
  •   Stuart    16 年前

    戴夫·德隆的方法是好的,至少在我的情况下,只要你忽略了几件事。以下是它对我的作用:

    • 到名为 “lastnamepinitial”(或其他类似的词)

      使此属性为瞬态。这 将其保存到数据文件中。这 当你需要的时候。

      实体

      不要为这个担心二传手 所有物创建此getter(这是 一半的魔法,伊姆霍)


    // THIS ATTRIBUTE GETTER GOES IN YOUR OBJECT MODEL
    - (NSString *) committeeNameInitial {
        [self willAccessValueForKey:@"committeeNameInitial"];
        NSString * initial = [[self committeeName] substringToIndex:1];
        [self didAccessValueForKey:@"committeeNameInitial"];
        return initial;
    }
    
    
    // THIS GOES IN YOUR fetchedResultsController: METHOD
    // Edit the sort key as appropriate.
    NSSortDescriptor *nameInitialSortOrder = [[NSSortDescriptor alloc] 
            initWithKey:@"committeeName" ascending:YES];
    
    [fetchRequest setSortDescriptors:[NSArray arrayWithObject:nameInitialSortOrder]];
    
    NSFetchedResultsController *aFetchedResultsController = 
            [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest 
            managedObjectContext:managedObjectContext 
            sectionNameKeyPath:@"committeeNameInitial" cacheName:@"Root"];
    

    以前:按照Dave的初始步骤,在setPropertiesToFetch出现无效参数异常时,该字母会因setPropertiesToFetch而消失。我已将代码和调试信息记录在下面:

    NSDictionary * entityProperties = [entity propertiesByName];
    NSPropertyDescription * nameInitialProperty = [entityProperties objectForKey:@"committeeNameInitial"];
    NSArray * tempPropertyArray = [NSArray arrayWithObject:nameInitialProperty];
    
    //  NSARRAY * tempPropertyArray RETURNS:
    //    <CFArray 0xf54090 [0x30307a00]>{type = immutable, count = 1, values = (
    //    0 : (<NSAttributeDescription: 0xf2df80>), 
    //    name committeeNameInitial, isOptional 1, isTransient 1,
    //    entity CommitteeObj, renamingIdentifier committeeNameInitial, 
    //    validation predicates (), warnings (), versionHashModifier (null), 
    //    attributeType 700 , attributeValueClassName NSString, defaultValue (null)
    //    )}
    
    //  NSInvalidArgumentException AT THIS LINE vvvv
    [fetchRequest setPropertiesToFetch:tempPropertyArray];
    
    //  *** Terminating app due to uncaught exception 'NSInvalidArgumentException',
    //    reason: 'Invalid property (<NSAttributeDescription: 0xf2dfb0>), 
    //    name committeeNameInitial, isOptional 1, isTransient 1, entity CommitteeObj, 
    //    renamingIdentifier committeeNameInitial, 
    //    validation predicates (), warnings (), 
    //    versionHashModifier (null), 
    //    attributeType 700 , attributeValueClassName NSString, 
    //    defaultValue (null) passed to setPropertiesToFetch: (property is transient)'
    
    [fetchRequest setReturnsDistinctResults:YES];
    
    NSSortDescriptor * nameInitialSortOrder = [[[NSSortDescriptor alloc]
        initWithKey:@"committeeNameInitial" ascending:YES] autorelease];
    
    [fetchRequest setSortDescriptors:[NSArray arrayWithObject:nameInitialSortOrder]];
    
    NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] 
        initWithFetchRequest:fetchRequest 
        managedObjectContext:managedObjectContext 
        sectionNameKeyPath:@"committeeNameInitial" cacheName:@"Root"];
    
        2
  •  54
  •   Greg Combs    13 年前

    我想我还有另一个选择,这个选择使用了NSString上的一个类别。。。

    @implementation NSString (FetchedGroupByString)
    - (NSString *)stringGroupByFirstInitial {
        if (!self.length || self.length == 1)
            return self;
        return [self substringToIndex:1];
    }
    @end
    

    稍后,在构建FRC时:

    - (NSFetchedResultsController *)newFRC {
        NSFetchedResultsController *frc = [[NSFetchedResultsController alloc] initWithFetchRequest:awesomeRequest
                managedObjectContext:coolManagedObjectContext
                sectionNameKeyPath:@"lastName.stringGroupByFirstInitial"
                cacheName:@"CoolCat"];
        return frc;
    }
    

        3
  •  15
  •   Dave DeLong    16 年前

    这就是你的想法 可以 让它工作起来:

    • 使此属性为瞬态。这意味着核心数据不会麻烦地保存到您的数据文件中。此属性仅在需要时存在于内存中。
    • 生成此实体的类文件。
    • - (NSString *) lastNameInitial {
      [self willAccessValueForKey:@"lastNameInitial"];
      NSString * initial = [[self lastName] substringToIndex:1];
      [self didAccessValueForKey:@"lastNameInitial"];
      return initial;
      }
    • 在fetch请求中,仅请求此PropertyDescription,如下所示(这是另一个神奇之处):

      NSDictionary * entityProperties = [myEntityDescription propertiesByName];
      NSPropertyDescription * lastNameInitialProperty = [entityProperties objectForKey:@"lastNameInitial"];
      [fetchRequest setPropertiesToFetch:[NSArray arrayWithObject:lastNameInitialProperty]];
    • 确保获取请求只返回不同的结果(这是魔术的最后一个季度):

      [fetchRequest setReturnsDistinctResults:YES];
    • 通过此信函订购您的结果:

      NSSortDescriptor * lastNameInitialSortOrder = [[[NSSortDescriptor alloc] initWithKey:@"lastNameInitial" ascending:YES] autorelease];
      [fetchRequest setSortDescriptors:[NSArray arrayWithObject:lastNameInitialSortOrder]];

    如果我理解这是如何工作的,那么我猜它将返回一个NSManagedObject数组,每个NSManagedObject只将lastNameInitial属性加载到内存中,它们是一组不同的姓氏首字母。

    祝你好运,并报告这是如何工作的。我只是想知道这是否有效

        4
  •  8
  •   fawsha1    13 年前

    - (NSString *)stringGroupByFirstInitial {
        NSString *temp = [self uppercaseString];
        if (!temp.length || temp.length == 1)
            return self;
        return [temp substringToIndex:1];
    }
    
        5
  •  6
  •   Mike Baldus    15 年前

    我经常遇到这个问题。我经常回到的最好的解决方案是给实体一个真正的初始属性。由于可以将字段设置为索引,因此作为实字段可以提供更高效的搜索和排序。在首次导入/创建数据时,拉出第一个首字母并用它填充第二个字段似乎并不需要太多工作。您必须以任何一种方式编写初始解析代码,但您可以为每个实体编写一次,以后不再编写。缺点似乎是每个实体存储一个额外字符(以及索引),实际上,这可能无关紧要。

    再加一张。我避免修改生成的实体代码。也许我遗漏了一些东西,但是用于生成CoreData实体的工具不尊重我可能在其中输入的任何代码。生成代码时选择的任一选项都会删除我可能进行的任何自定义。如果我用聪明的小函数填充我的实体,那么我需要向那个实体添加一堆属性,我不能轻易地重新生成它。

        6
  •  2
  •   Benny Davidovitz    9 年前

    斯威夫特3

    extension NSString{
        func firstChar() -> String{
            if self.length == 0{
                return ""
            }
            return self.substring(to: 1)
        }
    }
    

    然后使用firstChar键路径进行排序,在我的例子中是lastname.firstChar

    request.sortDescriptors = [
                NSSortDescriptor(key: "lastname.firstChar", ascending: true),
                NSSortDescriptor(key: "lastname", ascending: true),
                NSSortDescriptor(key: "firstname", ascending: true)
            ]
    

    最后 对sectionNameKeyPath使用firstChar键路径

    let controller = NSFetchedResultsController(fetchRequest: request, managedObjectContext: context, sectionNameKeyPath: "lastname.firstChar", cacheName: "your_cache_name")
    
        7
  •  -1
  •   user698333    14 年前

    我想我有更好的办法。将显示“视图中”而不是“瞬态”属性。重新计算NSManagedObject的派生属性并保存上下文。更改后,您可以重新加载表视图。

    - (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
    {
        [self.tableView endUpdates];
        [self.tableView reloadData];
    }
    
    - (void)viewWillAppear:(BOOL)animated
    {
        [super viewWillAppear:animated];
        NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Capsid"];
        NSError *error = nil;
        NSArray *results = [self.managedObjectContext executeFetchRequest:request error:&error];
        if (error) {
            NSLog(@"refresh error");
            abort();
        }
        for (Capsid *capsid in results) {
            unsigned long long sum = 0;
            for (Touch *touch in capsid.vs) {
                sum += touch.count.unsignedLongLongValue;
            }
            for (Touch *touch in capsid.us) {
                sum += touch.count.unsignedLongLongValue;
            }
            capsid.sum = [NSNumber numberWithUnsignedLongLong:sum];
        }
        if (![self.managedObjectContext save:&error]) {
            NSLog(@"save error");
            abort();
        }
    }
    
    - (NSFetchedResultsController *)fetchedResultsController
    {
        if (__fetchedResultsController != nil) {
            return __fetchedResultsController;
        }
    
        // Set up the fetched results controller.
        // Create the fetch request for the entity.
        NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
        // Edit the entity name as appropriate.
        NSEntityDescription *entity = [NSEntityDescription entityForName:@"Capsid" inManagedObjectContext:self.managedObjectContext];
        [fetchRequest setEntity:entity];
    
        // Set the batch size to a suitable number.
        [fetchRequest setFetchBatchSize:20];
    
        // Edit the sort key as appropriate.
        //    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"timeStamp" ascending:NO];
    //    NSSortDescriptor *sumSortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"sum" ascending:NO];
    //    NSArray *sortDescriptors = [NSArray arrayWithObjects:sumSortDescriptor, nil];
        [fetchRequest setReturnsDistinctResults:YES];
        NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"sum" ascending:NO];
        NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
        [fetchRequest setSortDescriptors:sortDescriptors];
    
    
        // Edit the section name key path and cache name if appropriate.
        // nil for section name key path means "no sections".
        NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:nil];
        aFetchedResultsController.delegate = self;
        self.fetchedResultsController = aFetchedResultsController;
    
        NSError *error = nil;
        if (![self.fetchedResultsController performFetch:&error]) {
            /*
             Replace this implementation with code to handle the error appropriately.
    
             abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. 
             */
            NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
            abort();
        }
    
        return __fetchedResultsController;
    }