iPhone UITableView. ¿Cómo activar la lista alfabética de una sola letra como la aplicación de música?

82

En la aplicación de música del iPhone, al seleccionar Artista, Canciones o Álbumes, se presenta una vista de tabla con una lista vertical de letras individuales en el lado derecho de la interfaz de usuario que permite un desplazamiento rápido. ¿Cómo habilito esta funcionalidad en mi aplicación?

Saludos, Doug

dugla
fuente

Respuestas:

133

Proporcione sus propios caracteres de índice:

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

y entonces:

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

Necesitará secciones.

zaph
fuente
21
¿Hay alguna forma de tener índices sin tener secciones (para una lista plana)?
EL USO SEPTIEMBRE
12
@ThEuSeFuL Puede tener secciones sin mostrar encabezados de sección. Esto parecería una lista plana para el usuario final.
Jon Hull
10
¿Qué significa "yourSectionIndexForTheSectionForSectionIndexTitle"?
ravoorinandan
2
@ravoorinandan esencialmente significa devolver el índice de la variable de título en su matriz de títulos. Entonces, si el título es @ "a", debería devolver 0. Si el título es @ "i", debería devolver 2, y así sucesivamente. Puede hacer esto haciendo que la matriz esté disponible para ambos métodos que se muestran aquí y luego llamando a [matriz indexOfObject: title] en el segundo método. Aunque diría que la respuesta debajo de esto (relacionada con UILocalizedIndexedCollation) es probablemente mejor.
Brian Sachetta
Por si acaso, alguien cometió el mismo error que el mío, seleccione tableView en el guión gráfico y cambie el color del texto al color deseado. En mi caso, el color del texto se seleccionó automáticamente como color claro y, por lo tanto, el índice no se enumeraba. Perdí bastante tiempo averiguando cuál era el problema.
Vincent Joy
35

Otra cosa que debes considerar es la localización de las secciones para cada idioma. Después de investigar un poco, me pareció UILocalizedIndexedCollationbastante útil:

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

Se me ocurrió un enfoque alternativo para manejar una lista alfabética de una sola letra sin usar secciones. Es similar a la respuesta de Zaph, pero en lugar de obtener cualquier valor al devolver un nuevo índice (ya que siempre tendremos 1 sección), calculamos el índice para la ubicación del primer elemento en la matriz que comienza con un cierto carácter, luego nos desplazamos lo.

La desventaja es que esto requiere buscar la matriz cada vez (¿es esto absolutamente terrible?), Sin embargo, no noté ningún retraso o comportamiento lento en el simulador de iOS o en mi 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;
}

agregando propiedad para almacenar el último índice seleccionado como

 @property (assign, nonatomic) NSInteger previousSearchIndex;

y almacenar esta propiedad cada vez como:

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

y actualizando scrollToRowcódigo como:

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

Haz este método aún mejor y con buena animación.

Kyle Clegg
fuente
14
¡votado solo porque significa que no tuve que escribir en la matriz del alfabeto!
Paul Cezanne
1
je. Tampoco es una mala solución ... Apple me aceptó para el campamento de cacao usando esto en mi muestra de código. si eso significa que lo respaldan oficialmente o no ... no lo sé;).
Kyle Clegg
@PaulCezanne En realidad, para hacerlo más limpio, le sugiero que recorra su matriz de cadenas y tome las primeras letras de la caja superior de todas las cadenas para que su lista sea dinámica, ya que no tendrá ninguna Q si no tiene elementos comience con la letra Q. Avíseme si desea que actualice mi código para mostrar esto.
Kyle Clegg
Gracias. De hecho, lo hice en otra aplicación en la que estaba trabajando. Se veía bastante extraño en la práctica. La matriz AZ en el lateral es bastante explicativa. Cuando tiene solo unas pocas letras, que tendrá si solo tiene una pequeña cantidad de canciones o si ha filtrado muchas, el propósito de las letras a lo largo del costado parece extraño.
Paul Cezanne
1
@Mingebag Intenta depurarlo comprobando cuál es el valor de retorno de indexForFirstChar cada vez que se llama. Debe estar entre 0 y 25 cada vez. Si es> 25, entonces quizás tenga caracteres no alfabéticos o algo más que haga que devuelva un valor demasiado alto.
Kyle Clegg
21

Mucha gente preguntó si era posible hacer esto sin secciones. Quería lo mismo y encontré una solución que puede ser un poco turbia y no devuelve un valor a sectionForSectionIndexTitle, pero si estás en una esquina y no quieres tener que hacer una sección para cada letra del alfabeto, esto es una solución segura. Perdón por cualquier código nazis de antemano. :PAG

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

Funciona muy bien si obtiene una gran cantidad de datos y dividirlos requeriría mucho trabajo. :) Intenté usar variables genéricas para que supieras lo que estaba haciendo. d_itemsInTable es un NSArray de NSDictionaries que incluyo en UITableView.

krut
fuente
1
Fax divertido: si devuelve -1, ignorará las advertencias del compilador y no intentará desplazarse a esa sección desde el método
sectionForSectionIndexTitle
Muestra realmente impresionante, gracias, tal vez agregue un freno después de fondo = YES; tampoco sería tan malo ^^
Mingebag
3

Aquí hay una versión modificada de la función de Kyle que maneja el caso de hacer clic en un índice para el que no tiene una cadena:

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

Si está usando un NSFetchedResultsController, puede hacer lo siguiente:

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

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

Implementar los métodos delegados -sectionIndexTitlesForTableView:y-tableView:sectionForSectionIndexTitle:atIndex:

Consulte la UITableViewDataSourcedocumentación para obtener más información.

Alex Reynolds
fuente
2

Aquí hay una solución simple en Swift, asumiendo que tiene los encabezados de sus títulos en una matriz. Si no se pudo encontrar el título, devolverá el índice anterior en la matriz.

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
fuente
Creo que es preferible cambiar la segunda línea de return self.headerTitles.count - self.headerTitles.filter{$0 > title}.countretorno para devolver el índice de la primera sección coincidente en lugar de la última para el título de índice dado.
Chris C
Depende de lo que el cliente espere, de verdad. Lo más probable es que la funcionalidad que elija sea lo contrario de lo que quieren. :)
Samah
0

Si está utilizando MonoTouch, anule el método SectionIndexTitles (UITableView) en la clase UITableViewDataSource. Simplemente devuelva una matriz de cadenas y la subclase se encarga del resto.

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

* solo una pista para aquellos de nosotros que usamos C # y Mono (.NET) para escribir aplicaciones de iPhone. :)

benhorgen
fuente