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

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.


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


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ą.


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.


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

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


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
}

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. :)

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.