iPhone UITableView. Jak włączyć jednoliterowe listy alfabetyczne, takie jak aplikacja Music?

82

W aplikacji muzycznej na iPhone'a wybranie Artist, Songs lub Albums przedstawia tableView z pionową listą pojedynczych liter po prawej stronie interfejsu użytkownika, która umożliwia szybkie przewijanie. Jak włączyć tę funkcję w mojej aplikacji?

Pozdrawiam, Doug

dugla
źródło

Odpowiedzi:

133

Podaj własne znaki indeksu:

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

i wtedy:

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

Będziesz potrzebował sekcji.

uff
źródło
21
Czy istnieje sposób na posiadanie indeksów bez sekcji (w przypadku listy płaskiej)?
UŻYTKOWNIK
12
@ThEuSeFuL Możesz mieć sekcje bez pokazywania nagłówków sekcji. Dla użytkownika końcowego wyglądałoby to jak płaska lista.
Jon Hull
10
co oznacza „yourSectionIndexForTheSectionForSectionIndexTitle”?
ravoorinandan
2
@ravoorinandan zasadniczo oznacza zwrócenie indeksu zmiennej title w tablicy tytułów. Więc jeśli tytuł to @ "a", powinien zwrócić 0. Jeśli tytuł to @ "i", powinien zwrócić 2 i tak dalej. Możesz to zrobić, udostępniając tablicę obu metodom pokazanym tutaj, a następnie wywołując [array indexOfObject: title] w drugiej metodzie. Chociaż powiedziałbym, że odpowiedź poniżej tego (związana z UILocalizedIndexedCollation) jest prawdopodobnie lepsza.
Brian Sachetta
Na wszelki wypadek każdy popełnił ten sam błąd co mój, wybierz tableView w serii ujęć i zmień kolor tekstu na żądany kolor. W moim przypadku kolor tekstu został automatycznie wybrany jako czysty kolor i dlatego indeks nie był wyświetlany. Zmarnowałem sporo czasu, zastanawiając się, o co chodzi.
Vincent Joy
35

Coś jeszcze, co musisz wziąć pod uwagę, to zlokalizowanie sekcji dla każdego języka. Po trochę poszperania okazało się, UILocalizedIndexedCollationże jest całkiem przydatny:

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

rwyland
źródło
34

Wymyśliłem alternatywne podejście do obsługi listy składającej się z pojedynczych liter bez użycia sekcji. Jest to podobne do odpowiedzi Zapha, ale zamiast pobierać jakąkolwiek wartość ze zwracania nowego indeksu (ponieważ zawsze będziemy mieć 1 sekcję), obliczamy indeks dla lokalizacji pierwszego elementu w tablicy, która zaczyna się od określonego znaku, a następnie przewijamy do tego.

Minusem jest to, że wymaga to przeszukiwania tablicy za każdym razem (czy to absolutnie okropne?), Jednak nie zauważyłem żadnego opóźnienia ani powolnego zachowania w symulatorze iOS ani na moim iPhonie 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;
}

dodanie właściwości do przechowywania ostatnio wybranego indeksu, np

 @property (assign, nonatomic) NSInteger previousSearchIndex;

i przechowywanie tej własności za każdym razem jak:

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

i aktualizowanie scrollToRowkodu, takiego jak:

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

Zrób tę metodę jeszcze lepiej iz ładną animacją.

Kyle Clegg
źródło
14
upvoted tylko dlatego, że oznacza to, że nie musiałem wpisywać tablicy alfabetycznej!
Paul Cezanne
1
heh. to też nie jest złe rozwiązanie ... jabłko przyjęło mnie na obóz kakaowy, używając tego w mojej próbce kodu. czy to znaczy, że oficjalnie to popierają, czy nie ... nie wiem;).
Kyle Clegg
@PaulCezanne Właściwie, aby uczynić ją czystszą, sugeruję zapętlenie przez tablicę ciągów i pobranie pierwszych liter wielkich liter wszystkich ciągów, aby lista była dynamiczna w tym sensie, że nie będziesz mieć żadnych Q, jeśli nie masz żadnych elementów, które zacznij od litery Q. Daj mi znać, jeśli mam zaktualizować mój kod, aby to pokazać.
Kyle Clegg
Dzięki. Zrobiłem to w innej aplikacji, nad którą pracowałem. W praktyce wyglądało to dość dziwnie. Tablica AZ z boku jest dość objaśniająca. Gdy masz tylko kilka liter, które będziesz mieć, jeśli masz tylko niewielką liczbę piosenek lub jeśli dużo przefiltrowałeś, przeznaczenie liter z boku wygląda dziwnie.
Paul Cezanne
1
@Mingebag Spróbuj debugować, sprawdzając, jaka wartość zwracana jest przez indexForFirstChar przy każdym wywołaniu. Za każdym razem powinna wynosić od 0 do 25. Jeśli jest> 25, być może masz znaki niealfabetyczne lub coś innego, co powoduje, że zwraca zbyt wysoką wartość.
Kyle Clegg,
21

Grupa ludzi zapytała, czy można to zrobić bez sekcji. Chciałem tego samego i znalazłem rozwiązanie, które może być trochę podejrzane i nie zwraca wartości do sectionForSectionIndexTitle, ale jeśli jesteś w rogu i nie chcesz tworzyć sekcji dla każdej litery alfabetu, to to pewna poprawka. Przepraszam, że z góry szyfruję nazistów. : 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++;
    }
}

Działa świetnie, jeśli otrzymujesz dużą ilość danych, a ich sekcje wymagałyby dużo pracy. :) Próbowałem użyć zmiennych ogólnych, żebyś wiedział, co robię. d_itemsInTable to NSArray z NSDictionaries, które wymieniam do UITableView.

krut
źródło
1
Zabawny faks: jeśli
zwrócisz
Naprawdę niesamowita próbka, dzięki może przeznaczyć na dodanie hamulca po funduszu = TAK; też nie byłoby tak źle ^^
Mingebag
3

Oto zmodyfikowana wersja funkcji Kyle'a, która obsługuje przypadek kliknięcia indeksu, dla którego nie masz ciągu:

- (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;
}
Danny
źródło
3

Jeśli używasz NSFetchedResultsController, możesz po prostu:

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

- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index {
    return [frc sectionForSectionIndexTitle:title atIndex:index];
}
Bałwan
źródło
2

Zaimplementuj metody delegata -sectionIndexTitlesForTableView:i-tableView:sectionForSectionIndexTitle:atIndex:

UITableViewDataSourceWięcej informacji można znaleźć w dokumentacji.

Alex Reynolds
źródło
2

Oto proste rozwiązanie w Swift, zakładając, że masz nagłówki tytułów w tablicy. Jeśli nie można znaleźć tytułu, zwróci poprzedni indeks w tablicy.

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
}
Samah
źródło
Myślę, że lepiej jest zmienić drugą linię powrotu, return self.headerTitles.count - self.headerTitles.filter{$0 > title}.countaby zwracała indeks pierwszej, a nie ostatniej pasującej sekcji dla danego tytułu indeksu.
Chris C
Zależy, czego tak naprawdę oczekuje klient. Są szanse, że dowolna funkcjonalność, którą wybierzesz, będzie przeciwieństwem tego, czego chcą. :)
Samah
0

Jeśli używasz MonoTouch, Zastąp metodę SectionIndexTitles (UITableView) w klasie UITableViewDataSource. Po prostu zwróć tablicę ciągów, a podklasa zajmie się resztą.

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

* tylko wskazówka dla tych z nas, którzy używają C # i Mono (.NET) do pisania aplikacji na iPhone'a. :)

benhorgen
źródło