iPhone UITableView. Як увімкнути однобуквенний алфавітний список, як-от Music App?


82

У програмі для музики iPhone, вибравши пункт Виконавець, Пісні або Альбоми, ви побачите таблицю ViewView зі списком окремих букв по вертикалі праворуч від інтерфейсу користувача, що забезпечує швидке прокручування. Як увімкнути цю функцію у своєму додатку?

Ура, Даг

Відповіді:


133

Введіть власні символи індексу:

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

і потім:

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

Вам знадобляться розділи.


21
Чи є спосіб мати індекси без розділів (для плоского списку)?
УСЕФУЛ

12
@ThEuSeFuL Ви можете мати розділи, не показуючи заголовків розділів. Для кінцевого користувача це може виглядати як плоский список.
Джон Халл,

10
що означає "yourSectionIndexForTheSectionForSectionIndexTitle"?
ravoorinandan

2
@ravoorinandan він, по суті, означає повернути індекс змінної заголовка у вашому масиві заголовків. Отже, якщо заголовок @ "a", він повинен повертати 0. Якщо заголовок @ "i", він повинен повертати 2, і так далі. Ви можете зробити це, зробивши масив доступним для обох методів, показаних тут, а потім викликаючи [array indexOfObject: title] у другому методі. Хоча я б сказав, що відповідь нижче (пов'язана з UILocalizedIndexedCollation), мабуть, краща.
Брайан Сачетта

На всякий випадок хтось зробив ту ж помилку, що і моя, виберіть на таблиці розкадрування tableView та змініть колір тексту на потрібний. У моєму випадку колір тексту був автоматично обраний як чіткий, а тому індекс не відображався в списку. Я витратив досить багато часу, щоб з’ясувати, в чому проблема.
Вінсент Джой

35

Щось інше, що вам слід врахувати, це локалізація розділів для кожної мови. Покопавшись трохи, я виявив UILocalizedIndexedCollationсебе цілком корисним:

- (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

Я придумав альтернативний підхід до обробки списку алфавітів з однієї літери без використання розділів. Це схоже на відповідь Zaph, але замість отримання будь-якого значення від повернення нового індексу (оскільки ми завжди матимемо 1 розділ), ми обчислюємо індекс для розташування першого елемента в масиві, який починається з певного символу, а потім прокручуємо до нього.

Недоліком є ​​те, що кожен раз потрібно шукати масив (це абсолютно жахливо?), Однак я не помітив жодної затримки чи повільної поведінки в симуляторі iOS або на моєму iPhone 4S.

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

додавання властивості для зберігання останнього вибраного індексу, наприклад

 @property (assign, nonatomic) NSInteger previousSearchIndex;

і зберігати цю властивість щоразу, як:

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

та оновлення scrollToRowкоду, наприклад:

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

Зробіть цей метод ще кращим та з приємною анімацією.


14
проголосував лише тому, що це означає, що мені не потрібно було вводити алфавітний масив!
Пол Сезанн

1
хе. це також не погане рішення ... apple прийняв мене до какао-табору, використовуючи це в моєму зразку коду. чи означає це, що вони офіційно схвалюють це чи ні ... я не знаю;).
Kyle Clegg

@PaulCezanne Насправді, щоб зробити його чистішим, я пропоную вам прокрутити ваш масив рядків і захопити перші літери upperCase всіх рядків, щоб ваш список був динамічним, оскільки у вас не буде запитань, якщо у вас немає елементів, які Почніть з літери Q. Повідомте мене, якщо ви хочете, щоб я оновив свій код, щоб показати це.
Kyle Clegg

Дякую. Я насправді робив це в іншому додатку, над яким працював. На практиці це виглядало досить дивно. Масив AZ збоку досить пояснювальний. Коли у вас є лише кілька букв, які ви матимете, якщо у вас є лише невелика кількість пісень або якщо ви багато відфільтрували, призначення літер вздовж боку виглядає дивним.
Paul Cezanne

1
@Mingebag Спробуйте налагодити його, перевіривши, яке значення повертається indexForFirstChar кожного разу, коли воно викликається. Це має бути від 0 до 25 кожного разу. Якщо це> 25, можливо, у вас є не алфавітні символи або щось інше, що призводить до повернення занадто високого значення.
Kyle Clegg

21

Купа людей запитувала, чи можна це робити без секцій. Я хотів те саме, і я знайшов рішення, яке може бути трохи тіньовим і не повертає значення sectionForSectionIndexTitle, але якщо ви знаходитесь у кутку і не хочете робити розділ для кожної літери алфавіту, це є вірним виправленням. Заздалегідь вибачте будь-який код нацистів. : 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++;
    }
}

Це чудово працює, якщо ви отримуєте велику кількість даних, а секціонування займе купу роботи. :) Намагався використовувати загальні змінні, щоб ви знали, що я роблю. d_itemsInTable - це масив NSDictionaries, який я перелічую до UITableView.


1
Веселий факс: якщо ви повернете -1, він проігнорує попередження компілятора, а не намагатиметься перейти до цього розділу з методу
sectionForSectionIndexTitle

Дійсно приголомшливий зразок завдяки відведенню, можливо, додавши гальмо після фонду = ТАК; також не було б так погано ^^
Mingebag

3

Ось модифікована версія функції Кайла, яка обробляє випадок клацання індексу, для якого у вас немає рядка:

- (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

Якщо ви використовуєте a NSFetchedResultsController, ви можете просто зробити:

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

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


2

Ось просте рішення в Swift, якщо ви маєте заголовки заголовків у масиві. Якщо заголовок не вдалося знайти, він поверне попередній індекс у масиві.

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
}

Я вважаю, що бажано змінити другий рядок повернення на, return self.headerTitles.count - self.headerTitles.filter{$0 > title}.countщоб повернути індекс першого, а не останнього відповідного розділу для даного заголовка індексу.
Кріс Сі

Залежить від того, що клієнт очікує, насправді. Швидше за все, яку б функціональність ви не вибрали, буде протилежною від того, що вони хочуть. :)
Самах,

0

Якщо ви використовуєте MonoTouch, перевизначте метод SectionIndexTitles (UITableView) у класі UITableViewDataSource. Просто поверніть масив рядків, а підклас подбає про все інше.

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

* лише підказка для тих, хто використовує C # та Mono (.NET) для написання програм для iPhone. :)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.