2009-11-16 50 views
38

我正在使用核心數據作爲表視圖,並且我想將每個結果的第一個字母用作部分標題(這樣我可以得到部分指數在旁邊)。有沒有辦法做到這一點的關鍵路徑?像下面這樣,我使用name.firstLetter作爲sectionNameKeyPath(不幸的是,這不起作用)。如何使用第一個字符作爲部分名稱

我必須手動抓住每個結果的第一個字母並創建我的部分嗎?把一個新的房產放在第一個字母上並用作sectionNameKeyPath是否更好?

NSFetchedResultsController *aFetchedResultsController = 
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest 
      managedObjectContext:managedObjectContext 
      sectionNameKeyPath:@"name.firstLetter" 
      cacheName:@"Root"]; 

謝謝。

**編輯:**我不知道它是否有所作爲,但我的結果是日語,按片假名排序。我想用這些片假名作爲部分索引。

回答

78

您應該只傳遞「name」作爲sectionNameKeyPath。查看此answer的問題「帶有索引的核心數據支持的UITableView」。

UPDATE
也就是說,如果你只關心具有快速索引標題滾動解決方案僅適用。在這種情況下,你不會顯示節標題。請參閱下面的示例代碼。

否則,我同意refulgentis一個瞬態屬性是最好的解決方案。此外,在創建NSFetchedResultsController時,該sectionNameKeyPath有此限制:

如果這個關鍵路徑是不一樣的,通過在fetchRequest第一個排序 描述符中指定 ,他們必須 產生相同的相對排序。例如,fetchRequest中的第一個排序描述符 可能爲持久性屬性指定密鑰 ; sectionNameKeyPath可能會爲從 派生的瞬態屬性指定密鑰 持久性屬性。

樣板UITableViewDataSource實現使用NSFetchedResultsController:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 
    return [[fetchedResultsController sections] count]; 
} 

- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView { 
    return [fetchedResultsController sectionIndexTitles]; 
} 

- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index { 
    return [fetchedResultsController sectionForSectionIndexTitle:title atIndex:index]; 
} 

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 
    id <NSFetchedResultsSectionInfo> sectionInfo = [[fetchedResultsController sections] objectAtIndex:section]; 
    return [sectionInfo numberOfObjects]; 
} 

// Don't implement this since each "name" is its own section: 
//- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { 
// id <NSFetchedResultsSectionInfo> sectionInfo = [[fetchedResultsController sections] objectAtIndex:section]; 
// return [sectionInfo name]; 
//} 

更新2

對於新的 'uppercaseFirstLetterOfName' 瞬態特性,在模型中,並檢查一個新字符串屬性添加到相關實體「瞬態」框。

有幾種實現getter的方法。如果您正在生成/創建子類,那麼您可以將其添加到子類的實現(.m)文件中。

否則,您可以創建NSManagedObject類別(我把這個權利在我的視圖控制器的實現文件的頂部,但你可以在適當的頭部和自身的實現文件之間的分裂吧):

@interface NSManagedObject (FirstLetter) 
- (NSString *)uppercaseFirstLetterOfName; 
@end 

@implementation NSManagedObject (FirstLetter) 
- (NSString *)uppercaseFirstLetterOfName { 
    [self willAccessValueForKey:@"uppercaseFirstLetterOfName"]; 
    NSString *aString = [[self valueForKey:@"name"] uppercaseString]; 

    // support UTF-16: 
    NSString *stringToReturn = [aString substringWithRange:[aString rangeOfComposedCharacterSequenceAtIndex:0]]; 

    // OR no UTF-16 support: 
    //NSString *stringToReturn = [aString substringToIndex:1]; 

    [self didAccessValueForKey:@"uppercaseFirstLetterOfName"]; 
    return stringToReturn; 
} 
@end 

此外,在這個版本中,不要忘記通過 'uppercaseFirstLetterOfName' 作爲sectionNameKeyPath:

NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext 
sectionNameKeyPath:@"uppercaseFirstLetterOfName" // this key defines the sections 
cacheName:@"Root"]; 

而且,在UITableViewDataSource實施以取消tableView:titleForHeaderInSection:

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { 
    id <NSFetchedResultsSectionInfo> sectionInfo = [[fetchedResultsController sections] objectAtIndex:section]; 
    return [sectionInfo name]; 
} 
+0

我試着把名字放在'sectionNameKeyPath'中,但是每個返回的名字都有一段。我獲得了與名稱一樣多的部分,每部分只有一個條目(名稱)。 – 2009-11-16 10:19:23

+0

您可能沒有正確安裝。再讀一個答案。它像廣告一樣工作。 – 2009-11-16 10:33:34

+0

我不相信答案本身就是「有效的」,我不知道爲什麼迴應者將第一個名字作爲關鍵路徑的行爲是一個奇蹟。 – refulgentis 2009-11-16 10:36:28

11

有可能是一個更優雅的方式來做到這一點,但我最近有同樣的問題,並提出了這個解決方案。

首先,我在被稱爲firstLetterOfName的索引對象上定義了一個transient屬性,並將該getter寫入該對象的.m文件。 e.x.

- (NSString *)uppercaseFirstLetterOfName { 
    [self willAccessValueForKey:@"uppercaseFirstLetterOfName"]; 
    NSString *stringToReturn = [[self.name uppercaseString] substringToIndex:1]; 
    [self didAccessValueForKey:@"uppercaseFirstLetterOfName"]; 
    return stringToReturn; 
} 

接下來,我設置了我的獲取請求/實體以使用此屬性。

NSFetchRequest *request = [[NSFetchRequest alloc] init]; 
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Object" inManagedObjectContext:dataContext]; 
[request setEntity:entity]; 
[NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES selector:@selector(caseInsensitiveCompare:)]; 
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor]; 
[request setSortDescriptors:sortDescriptors]; 

側面說明,沒什麼特別的理由:謹慎使用NSFetchedResultsController - 這不正是完全成熟但恕我直言,超越文檔中列出的簡單案件的任何情況下,你可能會更好做它的「舊時尚「的方式。

+0

+1瞬態屬性是顯示節標題時的最佳解決方案。 – gerry3 2009-11-16 20:07:35

+1

請注意'substringToIndex:'!如果字符串是一個UTF-16字符串,那隻會抓取第一個字符的前半部分。正確的方法是'[aString subStringWithRange:[aString rangeOfComposedCharacterSequenceAtIndex:1]] – 2009-11-16 20:32:51

+0

感謝您的答案。我不明白你在哪裏調用'uppercaseFirstLetterOfName'方法。你應該調用這個而不是'caseInsensitiveCompare',還是有另一種方式被調用? – 2009-11-17 03:33:38

4

我解決了這個使用UILocalizedIndexCollat​​ion作爲NSFetchedResultsController v.s. UILocalizedIndexedCollation問題

+1

這真的是解決它的唯一方法。UILocalizedIndexCollat​​ion有一大堆內置行爲,其他答案中建議的uppercaseFirstLetterOfName代碼甚至沒有接近。作爲一個例子,UILocalizedIndexCollat​​ion可以正確摺疊日文半片和全片片假名到正確的部分,部分名稱用正確的平假名字符表示 - 它對世界上幾乎所有其他語言都是正確的。 – JosephH 2012-08-27 16:59:58

1

優雅的辦法是做使「firstLetter」短暫屬性中提到的,在實踐中是緩慢的但是。

這很慢,因爲要計算瞬態屬性,需要將整個對象故障存入內存。如果你有很多記錄,它會非常非常慢。

快速但不雅的方法是創建一個非暫時性的「firstLetter」屬性,每次設置「name」屬性時都會更新它。有幾種方法可以做到這一點:覆蓋「setName:」評估者,覆蓋「willSave」,KVO。

+0

你能確認嗎?我無法發現我的項目中瞬態和永久性屬性之間的差異。 – 2012-05-16 04:53:48

+0

我在評論@ gerry3答案時質疑「優雅」的方式。而「不雅」則顯得更「不雅」。索引應該來自'UILocalizedIndexedCollat​​ion'。因此,您應該重置更改區域設置猜疑的每個條目,以便將當前整理與您的第一個字母匹配。 – user500 2013-12-25 20:25:23

1

查看我對類似問題的回答here,其中我介紹如何創建持久化的本地化sectionKey索引(因爲您無法對NSFetchedResultsController中的瞬態屬性進行排序)。

0

下面是obj-c,幾年和一種語言的簡單解決方案。它在我的一個項目中工作並且很快運行。

首先,我在NSString上創建了一個類別,命名文件NSString + Indexing。我寫返回字符串的第一個字母

@implementation NSString (Indexing) 

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

然後我用在所取得的結果控制器的定義,方法與兩個stringIndex如下

_fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:@"name.stringGroupByFirstInitial" 
                       cacheName:nil]; 

上面的代碼的組合,方法方法將工作,不需要搞亂瞬態屬性或存儲一個單獨的屬性,只保留核心數據中的第一個字母

但是,當我試圖做與Swift一樣的它會拋出一個異常,因爲它不喜歡有一個函數作爲部分關鍵路徑(或計算出的字符串屬性 - 我也試過)如果有人知道如何在Swift中實現同樣的事情,我會非常想知道如何。

相關問題