ios - bottom - status bar iphone
Recuperar la altura de la celda del prototipo personalizado desde el guión gráfico (2)
Creé una categoría para UITableView hace algún tiempo que puede ser útil para esto. Almacena células ''prototipo'' utilizando objetos asociados para reutilizar los prototipos y proporciona un método conveniente para obtener la altura de la fila asignada en el guión gráfico. Los prototipos se liberan cuando se desasigna la vista de tabla.
UITableView + PrototypeCells.h
#import <UIKit/UIKit.h>
@interface UITableView (PrototypeCells)
- (CGFloat)heightForRowWithReuseIdentifier:(NSString*)reuseIdentifier;
- (UITableViewCell*)prototypeCellWithReuseIdentifier:(NSString*)reuseIdentifier;
@end
UITableView + PrototypeCells.m
#import "UITableView+PrototypeCells.h"
#import <objc/runtime.h>
static char const * const key = "prototypeCells";
@implementation UITableView (PrototypeCells)
- (void)setPrototypeCells:(NSMutableDictionary *)prototypeCells {
objc_setAssociatedObject(self, key, prototypeCells, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSMutableDictionary *)prototypeCells {
return objc_getAssociatedObject(self, key);
}
- (CGFloat)heightForRowWithReuseIdentifier:(NSString*)reuseIdentifier {
return [self prototypeCellWithReuseIdentifier:reuseIdentifier].frame.size.height;
}
- (UITableViewCell*)prototypeCellWithReuseIdentifier:(NSString*)reuseIdentifier {
if (self.prototypeCells == nil) {
self.prototypeCells = [[NSMutableDictionary alloc] init];
}
UITableViewCell* cell = self.prototypeCells[reuseIdentifier];
if (cell == nil) {
cell = [self dequeueReusableCellWithIdentifier:reuseIdentifier];
self.prototypeCells[reuseIdentifier] = cell;
}
return cell;
}
@end
Uso
Obtener la altura estática establecida en el guión gráfico es tan simple como esto:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return [tableView heightForRowWithReuseIdentifier:@"cellIdentifier"];
}
Asumiendo una vista de tabla de múltiples secciones:
enum {
kFirstSection = 0,
kSecondSection
};
static NSString* const kFirstSectionRowId = @"section1Id";
static NSString* const kSecondSectionRowId = @"section2Id";
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
CGFloat height = tableView.rowHeight; // Default UITableView row height
switch (indexPath.section) {
case kFirstSection:
height = [tableView heightForRowWithReuseIdentifier:kFirstSectionRowId];
break;
case kSecondSection:
height = [tableView heightForRowWithReuseIdentifier:kSecondSectionRowId];
}
return height;
}
Y finalmente, si la altura de la fila es dinámica:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
id thisRowData = self.allData[indexPath.row]; // Obtain the data for this row
// Obtain the prototype cell
MyTableViewCell* cell = (MyTableViewCell*)[self prototypeCellWithReuseIdentifier:@"cellIdentifier"];
// Ask the prototype cell for its own height when showing the specified data
return [cell heightForData:thisRowData];
}
Al usar "Prototipos dinámicos" para especificar el contenido de UITableView
en el guión gráfico, hay una propiedad "Altura de fila" que se puede configurar como Personal.
Al crear instancias de celdas, esta altura de fila personalizada no se tiene en cuenta. Esto tiene sentido, ya que el prototipo de celda que uso se decide por mi código de aplicación en el momento en que se creará la instancia de la celda. Instalar todas las celdas al calcular el diseño introduciría una penalización de rendimiento, por lo que entiendo por qué no se puede hacer.
La pregunta entonces, ¿puedo de alguna manera recuperar la altura dada un identificador de reutilización de celda, por ejemplo,
[myTableView heightForCellWithReuseIdentifier:@"MyCellPrototype"];
o algo por el estilo? ¿O tengo que duplicar las alturas de fila explícitas en mi código de aplicación, con la carga de mantenimiento que sigue?
Resuelto, con la ayuda de @TimothyMoose:
Las alturas se almacenan en las propias celdas, lo que significa que la única forma de obtener las alturas es crear instancias de los prototipos. Una forma de hacerlo es pre-dequear las celdas fuera del método de devolución de llamada normal de la celda. Aquí está mi pequeño POC, que funciona:
#import "ViewController.h"
@interface ViewController () {
NSDictionary* heights;
}
@end
@implementation ViewController
- (NSString*) _reusableIdentifierForIndexPath:(NSIndexPath *)indexPath
{
return [NSString stringWithFormat:@"C%d", indexPath.row];
}
- (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if(!heights) {
NSMutableDictionary* hts = [NSMutableDictionary dictionary];
for(NSString* reusableIdentifier in [NSArray arrayWithObjects:@"C0", @"C1", @"C2", nil]) {
CGFloat height = [[tableView dequeueReusableCellWithIdentifier:reusableIdentifier] bounds].size.height;
hts[reusableIdentifier] = [NSNumber numberWithFloat:height];
}
heights = [hts copy];
}
NSString* prototype = [self _reusableIdentifierForIndexPath:indexPath];
return [heights[prototype] floatValue];
}
- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 3;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (UITableViewCell*) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString* prototype = [self _reusableIdentifierForIndexPath:indexPath];
UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:prototype];
return cell;
}
@end
Para la altura estática (no basada en datos), puede simplemente quitar la cola de la celda una vez y almacenar la altura:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSNumber *height;
if (!height) {
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:@"MyCustomCell"];
height = @(cell.bounds.size.height);
}
return [height floatValue];
}
Para la altura dinámica (basada en datos), puede almacenar una celda prototipo en el controlador de vista y agregar un método a la clase de la celda que calcule la altura, teniendo en cuenta el contenido predeterminado de la instancia del prototipo, como colocación de subvista, fuentes, etc .:
- (MyCustomCell *)prototypeCell
{
if (!_prototypeCell) {
_prototypeCell = [self.tableView dequeueReusableCellWithIdentifier:@"MyCustomCell"];
}
return _prototypeCell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Data for the cell, e.g. text for label
id myData = [self myDataForIndexPath:indexPath];
// Prototype knows how to calculate its height for the given data
return [self.prototypeCell myHeightForData:myData];
}
Por supuesto, si usa una altura personalizada, probablemente tenga varios prototipos de celdas, por lo que los almacenará en un diccionario o algo así.
Por lo que puedo decir, la vista de tabla no intenta reutilizar el prototipo, presumiblemente porque fue quitado fuera de cellForRowAtIndexPath:
Este enfoque nos ha funcionado muy bien porque permite al diseñador modificar diseños de celdas en el guión gráfico sin requerir ningún cambio de código.
Editar: aclaró el significado del código de muestra y agregó un ejemplo para el caso de la altura estática.