iPhone UITableView. Làm cách nào để bật danh sách bảng chữ cái đơn lẻ như Ứng dụng âm nhạc?


82

Trong ứng dụng âm nhạc trên iPhone, việc chọn Nghệ sĩ, Bài hát hoặc Album sẽ trình bày một Chế độ xem bảng với danh sách dọc gồm các chữ cái đơn lẻ ở phía bên tay phải của giao diện người dùng cho phép cuộn nhanh. Làm cách nào để bật chức năng này trong ứng dụng của tôi?

Chúc mừng, Doug

Câu trả lời:


133

Cung cấp các ký tự chỉ mục của riêng bạn:

- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
    return[NSArray arrayWithObjects:@"a", @"e", @"i", @"m", @"p", nil];
}

và sau đó:

- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString
    *)title atIndex:(NSInteger)index {
        return <yourSectionIndexForTheSectionForSectionIndexTitle >;
}

Bạn sẽ cần các phần.


21
Có cách nào để có chỉ mục mà không có phần (đối với danh sách phẳng)?
ThE uSeFuL

12
@ThEuSeFuL Bạn có thể có các phần mà không cần hiển thị tiêu đề phần. Điều này sẽ giống như một danh sách phẳng cho người dùng cuối.
Jon Hull

10
"yourSectionIndexForTheSectionForSectionIndexTitle" nghĩa là gì?
ravoorinandan

2
@ravoorinandan về cơ bản có nghĩa là trả về chỉ mục của biến tiêu đề trong mảng tiêu đề của bạn. Vì vậy, nếu tiêu đề là @ "a", nó sẽ trả về 0. Nếu tiêu đề là @ "i", nó sẽ trả về 2, v.v. Bạn có thể làm điều này bằng cách cung cấp mảng cho cả hai phương thức được hiển thị ở đây và sau đó gọi [array indexOfObject: title] trong phương thức thứ hai. Mặc dù tôi sẽ nói rằng câu trả lời bên dưới này (liên quan đến UILocalizedIndexedCollation) có lẽ tốt hơn.
Brian Sachetta

Để đề phòng, bất kỳ ai cũng mắc lỗi giống như tôi, hãy chọn tableView trong bảng phân cảnh và thay đổi màu văn bản thành màu bạn muốn. Trong trường hợp của tôi, màu văn bản được tự động chọn là màu rõ ràng và do đó chỉ mục không được liệt kê. Tôi đã lãng phí khá nhiều thời gian để tìm ra vấn đề là gì.
Vincent Joy,

35

Một điều khác mà bạn phải xem xét là bản địa hóa các phần cho từng ngôn ngữ. Sau khi tìm hiểu kỹ một chút, tôi thấy UILocalizedIndexedCollationnó khá hữu ích:

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
    return [[[UILocalizedIndexedCollation currentCollation] sectionTitles] objectAtIndex:section];
}

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

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

https://developer.apple.com/documentation/uikit/uilocalizedindexedcollation


34

Tôi đã nghĩ ra một cách tiếp cận thay thế để xử lý một danh sách bảng chữ cái đơn lẻ mà không sử dụng các phần. Nó tương tự như câu trả lời của Zaph nhưng thay vì nhận bất kỳ giá trị nào từ việc trả về một chỉ mục mới (vì chúng tôi sẽ luôn có 1 phần), chúng tôi tính toán chỉ số cho vị trí của mục đầu tiên trong mảng bắt đầu bằng một ký tự nhất định, sau đó cuộn với nó.

Nhược điểm là điều này đòi hỏi phải tìm kiếm mảng mỗi lần (điều này có thực sự khủng khiếp không?), Tuy nhiên tôi không nhận thấy bất kỳ hành vi chậm hoặc chậm nào trong trình mô phỏng iOS hoặc trên iPhone 4S của mình.

- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
  return[NSArray arrayWithObjects:@"A", @"B", @"C", @"D", @"E", @"F", @"G", @"H", @"I", @"J", @"K", @"L", @"M", @"N", @"O", @"P", @"Q", @"R", @"S", @"T", @"U", @"V", @"W", @"X", @"Y", @"Z", nil];
}

- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index {

  NSInteger newRow = [self indexForFirstChar:title inArray:self.yourStringArray];
  NSIndexPath *newIndexPath = [NSIndexPath indexPathForRow:newRow inSection:0];
  [tableView scrollToRowAtIndexPath:newIndexPath atScrollPosition:UITableViewScrollPositionTop animated:NO];

  return index;
}

// Return the index for the location of the first item in an array that begins with a certain character
- (NSInteger)indexForFirstChar:(NSString *)character inArray:(NSArray *)array
{
  NSUInteger count = 0;
  for (NSString *str in array) {
    if ([str hasPrefix:character]) {
      return count;
    }
    count++;
  }
  return 0;
}

thêm thuộc tính để lưu trữ chỉ mục được chọn cuối cùng như

 @property (assign, nonatomic) NSInteger previousSearchIndex;

và lưu trữ thuộc tính này mọi lúc như:

- (NSInteger)indexForFirstChar:(NSString *)character inArray:(NSArray *)array
{
    NSUInteger count = 0;
    for (NSString *str in array) {
        if ([str hasPrefix:character]) {
            self.previousSearchIndex = count;
            return count;
        }
        count++;
    }
    return self.previousSearchIndex;
}

và cập nhật scrollToRowmã như:

 [tableView scrollToRowAtIndexPath:newIndexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];

Làm phương pháp này thậm chí còn tốt hơn và với hoạt ảnh đẹp.


14
ủng hộ chỉ vì nó có nghĩa là tôi không phải nhập vào mảng bảng chữ cái!
Paul Cezanne

1
heh. nó cũng không phải là một giải pháp tồi ... apple đã chấp nhận cho tôi tham gia trại ca cao bằng cách sử dụng điều này trong mẫu mã của tôi. cho dù điều đó có nghĩa là họ chính thức xác nhận nó hay không ... Tôi không biết;).
Kyle Clegg

@PaulCezanne Trên thực tế, để làm cho nó gọn gàng hơn, tôi khuyên bạn nên lặp lại mảng chuỗi của mình và lấy các chữ cái đầu tiên upperCase của tất cả các chuỗi để danh sách của bạn động mà bạn sẽ không có bất kỳ chữ Q nào nếu bạn không có bất kỳ mục nào bắt đầu bằng chữ Q. Hãy cho tôi biết nếu bạn muốn tôi cập nhật mã của mình để hiển thị điều này.
Kyle Clegg

Cảm ơn. Tôi thực sự đã làm điều đó trong một ứng dụng khác mà tôi đang làm việc. Nó trông khá kỳ lạ trong thực tế. Mảng AZ ở bên cạnh khá dễ hiểu. Khi bạn chỉ có một vài chữ cái, bạn sẽ có nếu bạn chỉ có một số lượng bài hát nhỏ hoặc nếu bạn đã lọc rất nhiều, mục đích của các chữ cái dọc theo bên trông sẽ kỳ quặc.
Paul Cezanne

1
@Mingebag Hãy thử gỡ lỗi nó bằng cách kiểm tra giá trị trả về của indexForFirstChar mỗi khi nó được gọi. Nó phải nằm trong khoảng từ 0 đến 25 mỗi lần. Nếu nó> 25 thì có lẽ bạn có các ký tự không phải bảng chữ cái hoặc thứ gì khác khiến nó trả về giá trị quá cao.
Kyle Clegg,

21

Một loạt người đã hỏi liệu có thể thực hiện điều này mà không có phần. Tôi muốn điều tương tự và tôi đã tìm thấy một giải pháp có thể hơi mờ ám và không trả về giá trị cho sectionForSectionIndexTitle nhưng nếu bạn đang ở trong góc và không muốn phải tạo một phần cho mỗi chữ cái trong bảng chữ cái này là một sửa chữa chắc chắn. Xin lỗi trước bất kỳ mã nào của Nazis. : P

- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
    if (thisTableDataIsShowing)
    {
        NSMutableArray *charactersForSort = [[NSMutableArray alloc] init];
        for (NSDictionary *item in d_itemsInTable)
        {
            if (![charactersForSort containsObject:[[item valueForKey:@"character_field_to_sort_by"] substringToIndex:1]])
            {
                [charactersForSort addObject:[[item valueForKey:@"character_field_to_sort_by"] substringToIndex:1]];
            }
        }
        return charactersForSort;
    }
    return nil;
}

- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index {
    BOOL found = NO;
    NSInteger b = 0;
    for (NSDictionary *item in d_itemsInTable)
    {
        if ([[[item valueForKey:@"character_field_to_sort_by"] substringToIndex:1] isEqualToString:title])
            if (!found)
            {
                [d_yourTableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:b inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:NO];
                found = YES;
            }
        b++;
    }
}

Nó hoạt động tốt nếu bạn đang nhận được một lượng lớn dữ liệu và việc phân loại nó sẽ mất rất nhiều công việc. :) Đã cố gắng sử dụng các biến chung chung để bạn biết tôi đang làm gì. d_itemsInTable là một NSArray của NSDictionaries mà tôi đang liệt kê cho UITableView.


1
Fun fax: Nếu bạn quay trở lại -1, nó sẽ bỏ qua các cảnh báo trình biên dịch, và không cố gắng để di chuyển đến phần đó từ phương pháp sectionForSectionIndexTitle
elimirks

Mẫu thực sự tuyệt vời cảm ơn phân bổ có thể thêm một phanh sau quỹ = YES; sẽ cũng không phải là xấu ^^
Mingebag

3

Đây là phiên bản sửa đổi của hàm Kyle để xử lý trường hợp nhấp vào chỉ mục mà bạn không có chuỗi:

- (NSInteger)indexForFirstChar:(NSString *)character inArray:(NSArray *)array
{
    char testChar = [character characterAtIndex:0];
    __block int retIdx = 0;
    __block int lastIdx = 0;

    [array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        char firstChar = [obj characterAtIndex:0];

        if (testChar == firstChar) {
            retIdx = idx;
            *stop = YES;
        }

        //if we overshot the target, just use whatever previous one was
        if (testChar < firstChar) {
            retIdx = lastIdx;
            *stop = YES;
        }

        lastIdx = idx;
    }];
    return retIdx;
}

3

Nếu bạn đang sử dụng NSFetchedResultsController, bạn chỉ có thể làm:

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

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

2

Thực hiện các phương pháp ủy quyền -sectionIndexTitlesForTableView:-tableView:sectionForSectionIndexTitle:atIndex:

Xem UITableViewDataSourcetài liệu để biết thêm thông tin.


2

Đây là một giải pháp đơn giản trong Swift, giả sử bạn có tiêu đề tiêu đề của mình trong một mảng. Nếu không tìm thấy tiêu đề, nó sẽ trả về chỉ mục trước đó trong mảng.

func sectionIndexTitlesForTableView(tableView: UITableView) -> [String]? {
    return "ABCDEFGHIJKLMNOPQRSTUVWXYZ".characters.flatMap{String($0)}
}

func tableView(tableView: UITableView, sectionForSectionIndexTitle title: String, atIndex index: Int) -> Int {
    return self.headerTitles.filter{$0 <= title}.count - 1
}

Tôi nghĩ tốt hơn là thay đổi dòng trả về thứ hai return self.headerTitles.count - self.headerTitles.filter{$0 > title}.countđể trả về chỉ mục của đầu tiên hơn là phần đối sánh cuối cùng cho tiêu đề chỉ mục nhất định.
Chris C

Thực sự phụ thuộc vào những gì khách hàng mong đợi. Rất có thể, bất kỳ chức năng nào bạn chọn sẽ ngược lại với những gì họ muốn. :)
Samah

0

Nếu bạn đang sử dụng MonoTouch, hãy ghi đè phương thức SectionIndexTitles (UITableView) trong lớp UITableViewDataSource. Chỉ cần trả về một mảng chuỗi và lớp con sẽ lo phần còn lại.

class TableViewDataSource : UITableViewDataSource
{
  public override string[] SectionIndexTitles(UITableView tableView) 
  { 
    return new string[] { /*your string values */};
  }
}

* chỉ là một gợi ý cho những người trong chúng ta sử dụng C # và Mono (.NET) để viết ứng dụng iPhone. :)

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.