Pregunta

Mi requisito:Tengo este requisito directo de enumerar los nombres de las personas en orden alfabético en una vista de tabla indexada, ya que es la letra inicial de los alfabetos (además un icono de búsqueda en la parte superior y # para mostrar los valores misceláneos que comienzan con un número y otro especial caracteres).

Lo que he hecho hasta ahora:1. Estoy usando datos de núcleo para el almacenamiento y "last_name" se modela como una propiedad de cadena en la entidad de contactos 2. Estoy usando un NSFetchedResultScontroller para mostrar la vista de tabla indexada ordenada.

Problemas que logran mi requisito:1. Primero, no pude lograr que los títulos de índice de sección fueran la primera letra de alfabetos. La sugerencia de Dave en la siguiente publicación, me ayudó a lograr lo mismo: NsfetchedResultScontroller con secciones creadas por la primera letra de una cadena

El único problema que encontré con la sugerencia de Dave 'es que no pude obtener el MISC con el índice "#".

Lo que he probado:1. Intenté agregar un método de comparación personalizado a NSString (categoría) para verificar cómo se realiza la comparación y la sección, pero ese método personalizado no se llama cuando se especifica en el selector NSSortDescriptor.

Aquí hay algún código:

@interface NSString (SortString)

-(NSComparisonResult) customCompare: (NSString*) aStirng;

@end

@implementation NSString (SortString)

-(NSComparisonResult) customCompare:(NSString *)aString
{
 NSLog(@"Custom compare called to compare : %@ and %@",self,aString);
 return [self caseInsensitiveCompare:aString];
}

@end

Código para obtener datos:

NSArray *sortDescriptors = [NSArray arrayWithObject:[[[NSSortDescriptor alloc] initWithKey:@"last_name"
               ascending:YES selector:@selector(customCompare:)] autorelease]];

  [fetchRequest setSortDescriptors:sortDescriptors];
        fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
            managedObjectContext:managedObjectContext sectionNameKeyPath:@"lastNameInitial" cacheName:@"MyCache"];

¿Puedes decirme qué me estoy perdiendo y cómo se puede lograr el requisito?

¿Fue útil?

Solución

Este es un primer paso realmente ineficiente en este problema, que eventualmente voy a reescribir. Pero espero que esto te ayude.

La idea de esto es "garantizar" recuperar un índice de sección de tabla real al tocar una vista de índice de sección "estándar". Una vista de índice de sección estándar debe tener un icono de lente de aumento para la búsqueda, una marca hash (#) para secciones no alfabéticas y letras A a Z para secciones alfabéticas.

Esta vista estándar se presenta independientemente de cuántas secciones reales hay o de qué están hechas.

En última instancia, este código mapea los índices de vista de la sección a las rutas de nombre de la sección alfabética de existencia real en el controlador de resultados recuperado, o en secciones no alfabéticas (numéricas) no existentes, o en el campo de búsqueda en el encabezado de la tabla.

El usuario solo recreará ocasionalmente la matriz de mapeo de índice de sección (_idxArray) En cada toque del índice de sección, pero recrear la matriz en cada toque es obviamente ineficiente y podría modificarse para caché de resultados precalculados.

Hay muchos lugares para comenzar a endurecer esto: podría hacer el sectionIndexTitleLetters Cadena estática Todo el proceso superior desde el principio, por ejemplo. Sin embargo, es bastante rápido en un teléfono 3GS, así que no he vuelto a visitar esto recientemente.

En el encabezado:

static NSString *sectionIndexTitleLetters = @"abcdefghijklmnopqrstuvwxyz";

En la implementación de la fuente de datos de la vista de tabla:

- (NSArray *) sectionIndexTitlesForTableView:(UITableView *)tv {
    if (tv != searchDisplayController.searchResultsTableView) {
        NSMutableArray *_indexArray = [NSMutableArray arrayWithCapacity:([sectionIndexTitleLetters length]+2)];

        [_indexArray addObject:@"{search}"];
        [_indexArray addObject:@"#"];

        for (unsigned int _charIdx = 0; _charIdx < [sectionIndexTitleLetters length]; _charIdx++) {
            char _indexChar[2] = { toupper([sectionIndexTitleLetters characterAtIndex:_charIdx]), '\0'};
            [_indexArray addObject:[NSString stringWithCString:_indexChar encoding:NSUTF8StringEncoding]];
        }

        return _indexArray;
    }
    return nil;
}

- (NSInteger) tableView:(UITableView *)tv sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index {
    if (tv != searchDisplayController.searchResultsTableView) {
        if (index == 0) {
            //
            // This is the search bar "section"
            //
            [currentTableView scrollRectToVisible:[[currentTableView tableHeaderView] bounds] animated:YES];
            return -1;
        }
        else if (index == 1) {
            //
            // This is the "#" section, which covers non-alphabetic section headers (e.g. digits 0-9)
            //
            return 0;
        }
        else {
            //
            // This is a bit more involved because the section index array may contain indices that do not exist in the 
            // fetched results controller's sections->name info.
            //
            // What we are doing here is building a "fake-index" array that will return a real section index regardless of 
            // whether the section index title being touched exists or not. 
            //
            // The fake array will be of length of the section index title array, and each index will contain an unsigned 
            // integer from 1 to {numOfRealSections}. 
            //
            // The value this array returns will be "nearest" to the real section that is in the fetched results controller.
            //

            NSUInteger _alphabeticIndex = index-2;

            unsigned int _idxArray[26];
            for (unsigned int _initIdx = 0; _initIdx < [sectionIndexTitleLetters length]; _initIdx++) {
                _idxArray[_initIdx] = [[fetchedResultsController sections] count] - 1;
            }

            unsigned int _previousChunkIdx = 0;
            NSNumberFormatter *_numberFormatter = [[NSNumberFormatter alloc] init];
            NSLocale *_enUSLocale = [[NSLocale alloc] initWithLocaleIdentifier: @"en_US"];
            [_numberFormatter setLocale:_enUSLocale];
            [_enUSLocale release];

            for (unsigned int _sectionIdx = 0; _sectionIdx < [[fetchedResultsController sections] count]; _sectionIdx++) {
                NSString *_sectionTitle = [[[fetchedResultsController sections] objectAtIndex:_sectionIdx] name];
                if (![_numberFormatter numberFromString:_sectionTitle]) {
                    // what's the index of the _sectionTitle across sectionIndexTitleLetters?
                    for (unsigned int _titleCharIdx = 0; _titleCharIdx < [sectionIndexTitleLetters length]; _titleCharIdx++) {
                        NSString *_titleCharStr = [[sectionIndexTitleLetters substringWithRange:NSMakeRange(_titleCharIdx, 1)] uppercaseString];
                        if ([_titleCharStr isEqualToString:_sectionTitle]) {
                            // put a chunk of _sectionIdx into _idxArray
                            unsigned int _currentChunkIdx;
                            for (_currentChunkIdx = _previousChunkIdx; _currentChunkIdx < _titleCharIdx; _currentChunkIdx++) {
                                _idxArray[_currentChunkIdx] = _sectionIdx - 1;
                            }
                            _previousChunkIdx = _currentChunkIdx;
                            break;
                        }
                    }               
                }
            }

            [_numberFormatter release];

            return (NSInteger)_idxArray[_alphabeticIndex];
        }
    }
    return 0;
}

Otros consejos

Podría ser ingenuo, pero no entiendo por qué estas soluciones son tan barrocas. Hice esto:

En mi modelo, agregué un método:

-(NSString *)lastInitial {
    return [self.lastname substringToIndex:1];
}

Y en mi Tablecontroller establecí el FetchedResultScontroller para usar ese método:

NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:@"lastInitial" cacheName:@"Master"];

Parece funcionar: ¿hay alguna razón por la que esta es una mala idea? ¿O me estoy beneficiando de nuevas características en iOS5 o algo así?

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top