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.