14

Tôi đang cố gắng sử dụng FRC với dữ liệu ngôn ngữ hỗn hợp và muốn có chỉ mục phần.NSFetchedResultsController v.s. UILocalizedIndexedCollation

Nó có vẻ như từ các tài liệu bạn sẽ có thể ghi đè lên

- (NSString *)sectionIndexTitleForSectionName:(NSString *)sectionName 
- (NSArray *)sectionIndexTitles 

và của FRC sau đó sử dụng UILocalizedIndexedCollation để có một chỉ số và phần cục bộ. Nhưng thật đáng buồn điều này không hoạt động và không phải là những gì được dự định sẽ được sử dụng: (

Có ai có thể sử dụng FRC với UILocalizedIndexedCollation hoặc chúng tôi buộc phải sử dụng phương pháp sắp xếp thủ công được đề cập trong ví dụ UITableView + UILocalizedIndexedCollation ví dụ (mã ví dụ bao gồm nơi tôi đã làm việc này)

Sử dụng các thuộc tính sau

@property (nonatomic, assign) UILocalizedIndexedCollation *collation; 
@property (nonatomic, assign) NSMutableArray *collatedSections; 

và mã:.

- (UILocalizedIndexedCollation *)collation 
{ 
    if(collation == nil) 
    { 
     collation = [UILocalizedIndexedCollation currentCollation]; 
    } 

    return collation; 
} 

- (NSArray *)collatedSections 
{ 
    if(_collatedSections == nil) 
    { 
     int sectionTitlesCount = [[self.collation sectionTitles] count]; 

     NSMutableArray *newSectionsArray = [[NSMutableArray alloc] initWithCapacity:sectionTitlesCount]; 
     collatedSections = newSectionsArray; 
     NSMutableArray *sectionsCArray[sectionTitlesCount]; 

     // Set up the sections array: elements are mutable arrays that will contain the time zones for that section. 
     for(int index = 0; index < sectionTitlesCount; index++) 
     { 
      NSMutableArray *array = [[NSMutableArray alloc] init]; 
      [newSectionsArray addObject:array]; 
      sectionsCArray[index] = array; 
      [array release]; 
     } 


     for(NSManagedObject *call in self.fetchedResultsController.fetchedObjects) 
     { 
      int section = [collation sectionForObject:call collationStringSelector:NSSelectorFromString(name)]; 
      [sectionsCArray[section] addObject:call]; 
     } 

     NSArray *sortDescriptors = self.fetchedResultsController.fetchRequest.sortDescriptors; 
     for(int index = 0; index < sectionTitlesCount; index++) 
     { 
      [newSectionsArray replaceObjectAtIndex:index withObject:[sectionsCArray[index] sortedArrayUsingDescriptors:sortDescriptors]]; 
     } 
    } 
    return [[collatedSections retain] autorelease]; 
} 

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 
{ 
    // The number of sections is the same as the number of titles in the collation. 
    return [[self.collation sectionTitles] count]; 
} 


- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 
{ 
    // The number of time zones in the section is the count of the array associated with the section in the sections array. 
    return [[self.collatedSections objectAtIndex:section] count]; 
} 


- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section 
{ 
    if([[self.collatedSections objectAtIndex:section] count]) 
     return [[self.collation sectionTitles] objectAtIndex:section]; 
    return nil; 
} 


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


- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index { 
    return [self.collation sectionForSectionIndexTitleAtIndex:index]; 
} 

Tôi rất muốn vẫn có thể sử dụng giao thức FRCDelegate để được thông báo về các bản cập nhật. Nó có vẻ như không có cách nào tốt để làm cho hai đối tượng này làm việc cùng nhau độc đáo.

Trả lời

3

Brent, giải pháp của tôi dựa trên FRC và tôi nhận được phần từ tìm nạp chỉ định một thuộc tính thoáng qua trên đối tượng mô hình của tôi trả về tên phần cho đối tượng. Tôi sử dụng UIlocalizedIndexedCollation chỉ trong việc thực hiện getter thuộc tính sau đó tôi dựa vào việc thực hiện FRC trên bộ điều khiển xem bảng. Tất nhiên tôi sử dụng localizedCaseInsensitiveCompare làm bộ chọn sắp xếp trên tìm nạp.

- (NSString *)sectionInitial { 

    NSInteger idx = [[UILocalizedIndexedCollation currentCollation] sectionForObject:self  collationStringSelector:@selector(localeName)]; 
    NSString *collRet = [[[UILocalizedIndexedCollation currentCollation] sectionTitles]  objectAtIndex:idx]; 

    return collRet; 
} 

Hạn chế duy nhất tôi có là tôi không thể có phần # ở cuối vì tôi không thay đổi sắp xếp từ DB. Mọi thứ khác hoạt động tốt.

+1

Tôi sẽ phải thử điều này trong lần tiếp theo tôi trình bày vấn đề này (giải pháp hiện có hoạt động tốt). Chỉ cần lưu ý: bạn không bao giờ nên sử dụng localizedCaseInsensitiveCompare vì một số ngôn ngữ (như tiếng Pháp tôi tin) đưa trường hợp vào tài khoản khi sắp xếp; sử dụng localizedStandardCompare. Điều này đã được đề cập trên video địa phương hóa WWDC mới nhất –

+1

Trong tất cả các giải pháp tôi đã tìm thấy để phân loại và hiển thị dữ liệu được lập chỉ mục từ FRC, đây là cách thanh lịch nhất. Bạn đã tìm thấy một cách để di chuyển phần # đến cuối? Tôi cảm thấy giống như một trong các tùy chọn sắp xếp chuỗi sẽ làm điều này và kỳ lạ là phần của UILocalizedIndexedCollation'sIndexIndex được đặt hàng khác với thứ tự của bất kỳ bộ so sánh chuỗi nào sắp xếp theo. –

+0

Đây không phải là một viên đạn bạc, vì vậy hãy cẩn thận. Là một ghi chú câu trả lời khác, bạn không thể xây dựng một 'NSFetchRequest' với một sắp xếp trên thuộc tính tạm thời như thuộc tính bạn đã mô tả. (Ít nhất, không phải nếu bạn đang sử dụng kho lưu trữ dữ liệu lõi được SQLite sao lưu.) Vì vậy, nếu thứ tự sắp xếp của bạn khác với thứ tự đối chiếu, 'NSFetchedResultsController' sẽ lỗi và không trả về hàng nào. Ví dụ: nếu họ của người dùng bắt đầu bằng chữ thường, nó sẽ được sắp xếp * sau * tất cả các tên khác, nhưng collation sẽ cố gắng đặt nó với các tên khác bắt đầu bằng cùng một chữ cái (nhưng viết hoa). – jsadler

6

Vì bạn không thể sắp xếp trên một tài sản tạm thời, giải pháp tôi thực hiện là ...

  1. Tạo một thuộc tính chuỗi gọi là "sectionKey" cho mỗi thuộc tính có thể phân loại trong mỗi thực thể trong mô hình lõi dữ liệu của bạn. Thuộc tính sectionKey sẽ là giá trị được tính bắt nguồn từ thuộc tính cơ sở (ví dụ: tên hoặc thuộc tính tiêu đề). Nó phải được duy trì bởi vì (hiện tại) một thuộc tính tạm thời không thể được sử dụng trong một bộ mô tả sắp xếp cho một yêu cầu tìm nạp. Cho phép lập chỉ mục trên mỗi phần Thuộc tính khóa và cơ sở để phân loại sẽ được cung cấp. Để áp dụng bản cập nhật này cho một ứng dụng hiện có, bạn sẽ cần thực hiện di chuyển nhẹ và cũng bao gồm một thường trình để cập nhật cơ sở dữ liệu đã tồn tại từ trước.

  2. Nếu bạn đang tạo dữ liệu (ví dụ: để điền các cài đặt mới với bộ dữ liệu chuẩn hoặc tạo cơ sở dữ liệu SQLite được bản địa hóa cho từng ngôn ngữ đích, trong đó sẽ được sao chép khi khởi chạy ban đầu), trong mã đó , tính và cập nhật thuộc tính sectionKey của thực thể. Ý kiến ​​khác nhau về cách tiếp cận "tốt nhất" đối với dữ liệu giống, tuy nhiên cần lưu ý rằng một số tệp plist cho mỗi ngôn ngữ (thường sẽ dao động từ vài byte đến 20k, ngay cả đối với danh sách bao gồm vài trăm giá trị) một dấu chân tổng thể nhỏ hơn nhiều so với một cơ sở dữ liệu SQLite riêng lẻ cho mỗi ngôn ngữ (bắt đầu ở khoảng 20k mỗi ngôn ngữ). Trên một lưu ý phụ, Microsoft Excel cho Mac có thể được cấu hình để cung cấp phân loại địa phương của danh sách bằng cách cho phép các tính năng ngôn ngữ (3).

  3. Trong hàm tạo bộ điều khiển kết quả được tìm nạp, sắp xếp trên thuộc tính sectionKey và base và chuyển phầnKey cho đường dẫn khóa tên phần.

  4. Thêm logic tính toán để cập nhật thuộc tính sectionKey (s) trong tất cả các đầu vào thêm hoặc chỉnh sửa người dùng, ví dụ: trong textFieldDidEndEditing :.

Vậy đó! Không phân vùng thủ công các đối tượng đã tìm nạp vào một mảng các mảng. NSFetchedResultsController sẽ thực hiện đối chiếu cục bộ cho bạn. Ví dụ, trong trường hợp của tiếng Trung Quốc (giản thể), các đối tượng được tìm nạp sẽ được lập chỉ mục theo cách phát âm ngữ âm (4).

(1) Từ Thư viện nhà phát triển Apple iOS> Chủ đề lập trình quốc tế hóa>Internationalization and Localization. (2) 3_SimpleIndexedTableXem của TableViewSuite. (3) How to enable Chinese language features in Microsoft Office for Mac. (4) Ngôn ngữ Trung Quốc thường được sắp xếp theo số đếm đột quỵ hoặc cách phát âm ngữ âm.

+1

Bạn có thể xây dựng trên "tính toán" bạn đang làm để tạo ra giá trị sectionKey? –

2

Đối mặt với cùng một vấn đề gần đây làm cho tôi tìm kiếm web (stackoverflow trước hết) cho giải pháp thích hợp để làm cho NSFetchedResultsController (FRC) và UILocalizedIndexedCollation (LIC) làm việc cùng nhau. Hầu hết các giải pháp tìm thấy không đủ tốt để đáp ứng mọi yêu cầu. Điều quan trọng cần lưu ý là chúng ta không thể sử dụng LIC để sắp xếp các đối tượng đã tìm nạp theo cách cần thiết, chúng ta sẽ có hiệu năng rất lớn bị mất và FRC sẽ không tận dụng hết mọi lợi thế.

Vì vậy, đây là vấn đề nói chung:

1) Chúng tôi có DB với một số loại dữ liệu mà chúng tôi muốn lấy và hiển thị sử dụng FRC trong một danh sách (UITableView) với chỉ số (tương tự như Contacts.app). Chúng ta cần truyền chìa khóa giá trị đối tượng để FRC có thể đưa ra quyết định sắp xếp. Thậm chí nếu chúng ta sẽ thêm trường đặc biệt vào các mô hình CoreData của chúng tôi cho các phần sắp xếp và sử dụng tiêu đề chỉ mục của FRC, chúng tôi sẽ không đạt được kết quả mong muốn, khóa học FRC chỉ cung cấp các chỉ mục tìm thấy, nhưng không hoàn thành bảng chữ cái. Ngoài ra, chúng tôi sẽ gặp phải vấn đề với các chỉ mục không chính xác đang hiển thị (không thực sự chắc chắn tại sao lại như vậy, có thể một số lỗi trong FRC). Ví dụ: trong trường hợp bảng chữ cái tiếng Nga, sẽ có các ký hiệu trống hoặc "lạ" ($,?, ',…).

3) Nếu chúng tôi cố gắng sử dụng LIC để hiển thị các chỉ mục được bản địa hoá tốt, chúng tôi sẽ gặp vấn đề lập bản đồ các phần dựa trên dữ liệu trong FRC để hoàn thành bảng chữ cái "phần" được bản địa hóa trong LIC.

4) Sau khi quyết định sử dụng LIC và bằng cách nào đó giải quyết vấn đề 3), chúng tôi sẽ chú ý rằng LIC sẽ đặt phần "#" xuống dưới (tức là chỉ mục phần cao nhất) nhưng FRC sẽ đặt "#" - như đối tượng lên trên tức là chỉ số phần thấp nhất - 0). Vì vậy, sẽ có phần chuyển hoàn toàn. Sử dụng tất cả những điều đó, tôi quyết định "lừa" FRC mà không có bất kỳ "hack" lớn nào nhưng làm cho nó sắp xếp dữ liệu theo cách tôi cần (di chuyển tất cả các đối tượng từ "#" - như phần dưới cùng của danh sách).

Dưới đây là giải pháp mà tôi đã đến:

tôi thêm phương pháp khuyến nông để dụ NSManagedObject của tôi để chuẩn bị loại tên mà chúng ta sẽ sử dụng trong mô tả sắp xếp và phần đường dẫn quan trọng cho các thiết lập FRC. Không cần phải di chuyển đặc biệt trừ những động thái sẽ được mô tả bên dưới.

Bài toán 4) xảy ra do các thuật toán sắp xếp của FRC (SQL cấp thấp) có thể được sửa đổi một chút: chỉ bằng cách áp dụng các bộ mô tả sắp xếp. vấn đề.

Tôi nhận thấy rằng FRC quyết định rằng ký hiệu "#" thấp hơn bất kỳ ký hiệu bảng chữ cái nào đối diện với LIC ở vị trí "#" cao nhất.

Logic của FRC khá đơn giản vì biểu tượng "#" trong UTF-8 là U + 0023. Và vốn Latinh "A" là U + 0041, do đó, 23 < 41. Để làm cho FRC sẽ đặt "#" - như đối tượng cho phần chỉ mục cao nhất, chúng ta cần phải vượt qua biểu tượng UTF-8 cao nhất. Để xem nguồn này http://www.utf8-chartable.de/unicode-utf8-table.pl biểu tượng UTF-8 là U + 1000FF(). Tất nhiên có hầu như không có cách nào mà biểu tượng này sẽ xảy ra trong cuộc sống thực. Cho phép sử dụng U + 100000 cho độ sắc nét.

Sắp xếp phương pháp tên update trông giống như sau:

#define UT8_MAX @"\U00100000" 

- (void)updateSortName 
{ 
    NSMutableString *prSortName = [NSMutableString stringWithString:[self dataDependantSortName]]; // for sort descriptors 

    NSString *prSectionIdentifier = [[prSortName substringToIndex:1] uppercaseString]; // section keypath 

    UILocalizedIndexedCollation *collation = [UILocalizedIndexedCollation currentCollation]; 

    NSUInteger sectionIndex = [collation sectionForObject:prSectionIdentifier collationStringSelector:@selector(stringValue)]; // stringValue is NSString category method that returns [NSString stringWithString:self] 

    if(sectionIndex == [[collation sectionTitles] count] - 1) // last section tile '#' 
    { 
     prSectionIdentifier = UT8_MAX; 
    } 
    else 
    { 
     prSectionIdentifier = [collation sectionTitles][sectionIndex]; 
    } 

    [prSortName replaceCharactersInRange:NSMakeRange(0, 1) withString:prSectionIdentifier]; 

// sortName, sectionIdentifier - non-transient string attributes in CoreData model 

    [self willChangeValueForKey:@"sortName"]; 
    [self setPrimitiveValue:prSortName forKey:@"sortName"]; 
    [self didChangeValueForKey:@"sortName"]; 

    [self willChangeValueForKey:@"sectionIdentifier"]; 
    [self setPrimitiveValue:prSectionIdentifier forKey:@"sectionIdentifier"]; 
    [self didChangeValueForKey:@"sectionIdentifier"]; 
} 

FRC thiết lập:

- (void)setupFRC 
{ 
    NSEntityDescription *entityDescription = 
    [NSEntityDescription entityForName:@"entity" 
       inManagedObjectContext:self.moc]; 

    NSSortDescriptor *sortNameDescriptor = [[NSSortDescriptor alloc] initWithKey:@"sortName" ascending:YES selector:@selector(localizedCaseInsensitiveCompare:)]; // or any selector you need 
    NSArray *sortDescriptors = [NSArray arrayWithObjects:sortNameDescriptor, nil]; 

    NSFetchRequest *fetchRequest = [NSFetchRequest new]; 
    [fetchRequest setEntity:entityDescription]; 
    [fetchRequest setFetchBatchSize:BATCH_SIZE]; 
    [fetchRequest setSortDescriptors:sortDescriptors]; 

    NSFetchedResultsController *fetchedResultsController = 
    [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest 
             managedObjectContext:self.moc 
              sectionNameKeyPath:@"sectionIdentifier" 
                cacheName:nil]; 
    self.fetchedResultsController = fetchedResultsController; 
} 

phương pháp FRC đại biểu là mặc định. Phương thức nguồn của truyền hình và nguồn dữ liệu:

- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView 
{ 
    return [[self localizedIndexedCollation] sectionTitles]; 
} 

- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index 
{ 
    NSString *indexTitle = [title isEqualToString:@"#"] ? UT8_MAX : title; 
    NSInteger fetchTitleIndex = NSNotFound; 

    NSArray *sections = [self.fetchedResultsController sections]; 
    for (id <NSFetchedResultsSectionInfo> sectionInfo in sections) 
    { 
     if([[sectionInfo name] isEqualToString:indexTitle]) 
     { 
      fetchTitleIndex = [sections indexOfObject:sectionInfo]; 
      break; 
     } 
    } 

    return fetchTitleIndex; 
} 

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section 
{ 
    id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section]; 
    NSString *fetchTitle = [sectionInfo name]; 

    NSInteger collationTitleIndex = [[self localizedIndexedCollation] sectionForObject:fetchTitle 
                   collationStringSelector:@selector(stringValue)]; 
    return [[[self localizedIndexedCollation] sectionTitles] objectAtIndex:collationTitleIndex]; 
} 

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

Thats it. Cho đến nay hoạt động tốt. Có lẽ nó sẽ làm việc cho bạn.

+1

khắc phục sự cố này sẽ khắc phục tiêu đề và chỉ mục phần. Nội dung phần vẫn sai. Ví dụ: ký tự Trung Quốc sẽ có tiêu đề phần tiếng Anh chính xác nhưng chúng nằm trong phần sai. Tôi sẽ kết thúc với 2 phần với cùng tên: S –

+0

@ JoãoNunes bạn đã tìm ra cách để đối phó với các ký tự Trung Quốc? – user754905

+0

@ user754905 không. Nhưng cũng không nhìn vào điều này nữa. –

2

Tôi đã tìm thấy một cách dễ dàng để giải quyết vấn đề này!

Chỉ cần thay thế "#" thành "^" trong dữ liệu cốt lõi của bạn để các phần cho chế độ xem bảng của bạn sẽ là "A-Z ^". Trong khi unicode của '#' nhỏ hơn 'A', '^' 's là ngược lại. Vì vậy, nó không khó cho bạn để dự đoán '^' sẽ làm theo Z trong phần của bạn.

Sau đó, bạn nên thay thế các phần của bộ điều khiển kết quả đã tìm nạp của mình. chỉ bằng một vài dòng mã sau:

- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView 
{ 

    NSMutableArray *array = [[NSMutableArray alloc] initWithArray:[self.frc sectionIndexTitles]]; 

    // If "^" is in the section, replace it to "#" 
    if ([[array lastObject] isEqualToString:@"^"]) 
    { 
     [array setObject:@"#" atIndexedSubscript:[array count]-1]; 
     return array; 
    } 
    // If "#" is not in the section 
    return [self.frc sectionIndexTitles]; 
} 

- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title 
       atIndex:(NSInteger)index 
{ 
    if ([title isEqualToString:@"#"]) { 
     return [self.frc sectionForSectionIndexTitle:@"^" atIndex:index]; 
    } 
    return [self.frc sectionForSectionIndexTitle:title atIndex:index]; 
} 

-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section 
{ 
    if ([[[self.frc sectionIndexTitles] objectAtIndex:section] isEqualToString:@"^"]) { 
     return @"#"; 
    } 
    return [[self.frc sectionIndexTitles] objectAtIndex:section]; 
} 
Các vấn đề liên quan