iPhone UITableView. Wie schalte ich die alphabetische Liste mit einem Buchstaben wie die Musik-App ein?

82

Wenn Sie in der iPhone-Musik-App Künstler, Songs oder Alben auswählen, wird auf der rechten Seite der Benutzeroberfläche eine Tabellenansicht mit einer vertikalen Liste einzelner Buchstaben angezeigt, die ein schnelles Scrollen ermöglicht. Wie aktiviere ich diese Funktionalität in meiner App?

Prost, Doug

Dugla
quelle

Antworten:

133

Geben Sie Ihre eigenen Indexzeichen an:

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

und dann:

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

Sie benötigen Abschnitte.

zaph
quelle
21
Gibt es eine Möglichkeit, Indizes ohne Abschnitte zu haben (für eine flache Liste)?
USA
12
@ThEuSeFuL Sie können Abschnitte haben, ohne Abschnittsüberschriften anzuzeigen. Dies würde für den Endbenutzer wie eine flache Liste aussehen.
Jon Hull
10
Was bedeutet "yourSectionIndexForTheSectionForSectionIndexTitle"?
Ravoorinandan
2
@ravoorinandan bedeutet im Wesentlichen, den Index der Titelvariablen in Ihrem Titelarray zurückzugeben. Wenn der Titel also @ "a" ist, sollte er 0 zurückgeben. Wenn der Titel @ "i" ist, sollte er 2 zurückgeben und so weiter. Sie können dies tun, indem Sie das Array für beide hier gezeigten Methoden verfügbar machen und dann in der zweiten Methode [array indexOfObject: title] aufrufen. Obwohl ich sagen würde, dass die Antwort darunter (im Zusammenhang mit UILocalizedIndexedCollation) wahrscheinlich besser ist.
Brian Sachetta
Für den Fall, dass jemand den gleichen Fehler wie ich gemacht hat, wählen Sie die Tabellenansicht im Storyboard aus und ändern Sie die Textfarbe in die gewünschte Farbe. In meinem Fall wurde die Textfarbe automatisch als klare Farbe ausgewählt und daher wurde der Index nicht aufgelistet. Ich habe ziemlich viel Zeit damit verschwendet, herauszufinden, worum es geht.
Vincent Joy
35

Sie müssen auch die Abschnitte für jede Sprache lokalisieren. Nachdem ich ein bisschen herumgegraben hatte, fand ich UILocalizedIndexedCollationes ziemlich nützlich:

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

Ich habe mir einen alternativen Ansatz für die Behandlung einer Alphabetliste mit einem Buchstaben ohne Verwendung von Abschnitten ausgedacht. Es ähnelt der Antwort von Zaph, aber anstatt einen Wert aus der Rückgabe eines neuen Index zu erhalten (da wir immer einen Abschnitt haben), berechnen wir den Index für die Position des ersten Elements im Array, das mit einem bestimmten Zeichen beginnt, und scrollen dann dazu.

Der Nachteil ist, dass das Array jedes Mal durchsucht werden muss (ist das absolut schrecklich?), Allerdings habe ich im iOS-Simulator oder auf meinem iPhone 4S keine Verzögerung oder langsames Verhalten festgestellt.

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

Hinzufügen einer Eigenschaft zum Speichern des zuletzt ausgewählten Index wie

 @property (assign, nonatomic) NSInteger previousSearchIndex;

und Speichern dieser Eigenschaft jedes Mal wie:

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

und Aktualisieren von scrollToRowCode wie:

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

Mach diese Methode noch besser und mit netter Animation.

Kyle Clegg
quelle
14
upvoted, nur weil es bedeutet, dass ich das Alphabet-Array nicht eingeben musste!
Paul Cezanne
1
heh. Es ist auch keine schlechte Lösung ... Apple hat mich für das Kakao-Camp akzeptiert, indem ich dies in meinem Codebeispiel verwendet habe. ob das bedeutet, dass sie es offiziell unterstützen oder nicht ... ich weiß es nicht;).
Kyle Clegg
@PaulCezanne Um es sauberer zu machen, schlage ich vor, dass Sie Ihr String-Array durchlaufen und die ersten Großbuchstaben aller Strings abrufen, damit Ihre Liste dynamisch ist und Sie keine Qs haben, wenn Sie keine Elemente haben, die dies tun Beginnen Sie mit dem Buchstaben Q. Lassen Sie mich wissen, ob ich meinen Code aktualisieren soll, um dies anzuzeigen.
Kyle Clegg
Vielen Dank. Ich habe das tatsächlich in einer anderen App gemacht, an der ich gearbeitet habe. In der Praxis sah es ziemlich komisch aus. Das AZ-Array an der Seite ist ziemlich erklärend. Wenn Sie nur wenige Buchstaben haben, die Sie haben, wenn Sie nur eine kleine Anzahl von Songs haben oder wenn Sie viel gefiltert haben, sieht der Zweck der Buchstaben an der Seite seltsam aus.
Paul Cezanne
1
@Mingebag Versuchen Sie, es zu debuggen, indem Sie bei jedem Aufruf überprüfen, welcher IndexForFirstChar-Rückgabewert ist. Sie sollte jedes Mal zwischen 0 und 25 liegen. Wenn es> 25 ist, haben Sie möglicherweise nicht alphabetische Zeichen oder etwas anderes, was dazu führt, dass ein zu hoher Wert zurückgegeben wird.
Kyle Clegg
21

Eine Gruppe von Leuten fragte, ob dies ohne Abschnitte möglich sei. Ich wollte dasselbe und fand eine Lösung, die möglicherweise etwas zwielichtig ist und keinen Wert an sectionForSectionIndexTitle zurückgibt, aber wenn Sie sich in einer Ecke befinden und nicht für jeden Buchstaben des Alphabets einen Abschnitt erstellen müssen ist eine sichere Lösung. Entschuldigung für jeden Code Nazis im Voraus. : 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++;
    }
}

Es funktioniert hervorragend, wenn Sie eine große Datenmenge erhalten und das Aufteilen eine Menge Arbeit erfordern würde. :) Ich habe versucht, generische Variablen zu verwenden, damit du weißt, was ich tue. d_itemsInTable ist ein NSArray von NSDictionaries, das ich in der UITableView aufführe.

krut
quelle
1
Fun Fax: Wenn Sie -1 zurückgeben, werden Compiler-Warnungen ignoriert und es wird nicht versucht, von der sectionForSectionIndexTitle-Methode
elimirks
Wirklich großartige Probe, danke, vielleicht eine Bremse nach dem Fonds hinzufügen = JA; wäre auch nicht so schlimm ^^
Mingebag
3

Hier ist eine modifizierte Version von Kyles Funktion, die den Fall des Klickens auf einen Index behandelt, für den Sie keine Zeichenfolge haben:

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

Wenn Sie a verwenden NSFetchedResultsController, können Sie einfach Folgendes tun:

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

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

Implementieren Sie die Delegatmethoden -sectionIndexTitlesForTableView:und-tableView:sectionForSectionIndexTitle:atIndex:

UITableViewDataSourceWeitere Informationen finden Sie in der Dokumentation.

Alex Reynolds
quelle
2

Hier ist eine einfache Lösung in Swift, vorausgesetzt, Sie haben Ihre Titelüberschriften in einem Array. Wenn der Titel nicht gefunden wurde, wird der vorherige Index im Array zurückgegeben.

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
quelle
Ich denke, es ist vorzuziehen, die zweite Rückgabezeile so zu ändern return self.headerTitles.count - self.headerTitles.filter{$0 > title}.count, dass der Index des ersten und nicht der letzte übereinstimmende Abschnitt für den angegebenen Indextitel zurückgegeben wird.
Chris C
Kommt darauf an, was der Kunde wirklich erwartet. Die von Ihnen gewählte Funktionalität ist wahrscheinlich das Gegenteil von dem, was sie wollen. :)
Samah
0

Wenn Sie MonoTouch verwenden, überschreiben Sie die SectionIndexTitles-Methode (UITableView) in der UITableViewDataSource-Klasse. Geben Sie einfach ein Array von Zeichenfolgen zurück, und die Unterklasse kümmert sich um den Rest.

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

* Nur ein Hinweis für diejenigen von uns, die C # und Mono (.NET) zum Schreiben von iPhone-Apps verwenden. :) :)

benhorgen
quelle