La mejor manera de calcular la altura UITableViewCell sin el marco ContentView

RESUMEN

Dado que no siempre sabemos qué va a ser el fotogtwig de una celda o su vista de contenido (debido a la edición, rotation, vistas de accesorios, etc.), cuál es la mejor manera de calcular la altura en tableView:heightForRowAtIndexPath: cuando ¿La celda contiene un campo o label de text de altura variable?

Uno de mis UITableViewController's contiene la siguiente presentación: UITableViewCell con UITextView.

UITextView debe tener el mismo ancho y alto que UITableViewCell.

introduzca la descripción de la imagen aquí

Creé la subclass UITableViewCell, y luego e inicialicé con UITextView (UITextView es un campo privado de mi UITableViewController)

 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ static NSString *CellIdentifier = @"TextViewCell"; UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) { cell = [[[BTExpandableTextViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier textView:_notesTextView] autorelease]; } return cell; } 

Implementé el siguiente método en mi subclass UITableViewCell:

 - (void)layoutSubviews{ [super layoutSubviews]; CGFloat height = [textView.text sizeWithFont:textView.font constrainedToSize:CGSizeMake(textView.frame.size.width, MAXFLOAT)].height + textView.font.lineHeight; textView.frame = CGRectMake(0, 0, self.contentView.frame.size.width, (height < textView.font.lineHeight * 4) ? textView.font.lineHeight * 4 : height); [self.contentView addSubview:textView]; } 

y, por supuesto, implementé el siguiente método UITableViewDataSource (¡mira! Estoy usando self.view.frame.size.width (pero realmente necesito ancho de marco ContentView UITableViewCell):

 - (CGFloat)tableView:(UITableView*)tableView heightForRowAtIndexPath:(NSIndexPath*)indexPath{ CGFloat height = [_notesTextView.text sizeWithFont:_notesTextView.font constrainedToSize:CGSizeMake(self.view.frame.size.width, MAXFLOAT)].height; CGFloat groupedCellCap = 20.0; height += groupedCellCap; if(height < [BTExpandableTextViewCell minimumTextViewHeightWithFont:_notesTextView.font]){ height = [BTExpandableTextViewCell minimumTextViewHeightWithFont:_notesTextView.font]; } return height; } 

también implementé el siguiente método (eso no es tan importante, pero lo publico de todos modos, solo para explicar que la altura de la celda es dinámica, se networkingucirá o ampliará después de cambiar el text en UITextView)

  - (void)textViewDidChange:(UITextView *)textView{ CGFloat height = [_notesTextView.text sizeWithFont:_notesTextView.font constrainedToSize:CGSizeMake(_notesTextView.frame.size.width, MAXFLOAT)].height; if(height > _notesTextView.frame.size.height){ [self.tableView beginUpdates]; [self.tableView endUpdates]; } } 

Y ahora, mi pregunta es: Después de cargar la vista, UITableViewController está llamando a los methods en el siguiente order: (eliminaré algunos, como titleForHeaderInSection y etc para simplificación)

 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section - (CGFloat)tableView:(UITableView*)tableView heightForRowAtIndexPath:(NSIndexPath*)indexPath{ 

y solo entonces

 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 

¡Mira! ¡Debería devolver la altura UITableViewCell correcta antes de cellForRowAtIndexPath! Eso significa: no conozco UITableViewCell contentView frame. Y no puedo getlo programáticamente.

Este ancho puede ser uno de:

  1. Mesa plana iPhone, orientación vertical
  2. Mesa plana iPhone, orientación horizontal
  3. Mesa agrupada iPhone, orientación vertical
  4. Mesa agrupada en iPhone, orientación horizontal
  5. y lo mismo para el iPad (otros 4 valores)

Y no olvide que el frame contentView puede ser más pequeño debido a UITableViewCell accessoryType, o debido al estado de edición de UITableView. (por ejemplo, si tenemos UITableViewCell con UILabel multilínea de cualquier altura en cualquier estado de edición y con cualquier accesorioView)

Entonces, este problema es fundamental: simplemente no puedo get el ancho de fotogtwig del contenido de la celda para restringir, porque debería devolver esta altura antes de los layouts de celdas contentView. (Y esto es bastante lógico, por cierto) Pero este marco de ContentView realmente importa.

Por supuesto, a veces puedo conocer exactamente este ancho y "código rígido" (por ejemplo: UITableViewCellAccessoryDisclosureIndicator tiene 20 px de ancho y tableView no puede estar en estado de edición, entonces puedo escribir self.view.frame.size.width – 20 y la tarea está hecho)! ¡O a veces contentView es igual al marco de vista de UITableViewController!

A veces estoy usando self.view.frame.width en -tableView: heightForRowAtIndexPath: method .. (como ahora, y funciona bastante bien, pero no perfectamente debido a UITableView agrupado, debería restar algunos valores constantes, y son diferentes para 2 dispositivos * 2 orientaciones)

A veces tengo algunas constantes #defined en UITableViewCell (si sé el ancho exactamente) …

A veces utilizo un muñeco prefabricado UITableViewCell (lo que es estúpido, pero a veces es bastante elegante y fácil de usar) …

Pero no me gusta nada de eso.

¿Cuál es la mejor decisión? Tal vez debería crear una class auxiliar, que se inicializará con tales parameters: vistas accesorias, orientación del dispositivo, tipo de dispositivo, estado de edición de vista de tabla, estilo de vista de tabla (simple, agrupado), marco de vista del controller y otro que includeá algunas constantes (como el desplazamiento de tableView agrupado, etc.) y lo utilizan para encontrar el ancho de visualización de contenido UITableViewCell esperado? 😉

Gracias

La vista de tabla utiliza el tableView:heightForRowAtIndexPath: método para determinar su contenidoSize antes de crear cualquier UITableViewCell . Si se detiene y piensa en ello, esto tiene sentido, ya que lo primero que haría con un UIScrollView se establecerá su contenido de tamaño. Me he encontrado con un problema similar antes, y lo que he encontrado es que es mejor tener una function auxiliar que pueda llevar el contenido a UITableViewCell y pnetworkingecir la altura de ese UITableViewCell . Entonces, creo que querrás crear algún tipo de estructura de datos que almacene el text en cada UITableViewCell , un NSDictionary con NSIndexPaths como keys y el text como los valores lo haría bien. De esta manera, puede encontrar la altura del text necesario sin hacer reference a UITableViewCell .

Aunque puede calcular alturas para las tags contenidas en las celdas de vista de tabla, de manera realmente dinámica, en '- layoutSubviews' de una subclass UITableViewCell, no hay una manera similar de hacer esto (que yo sepa) para celdas de altura en '- tableView: heightForRowAtIndexPath:' de un delegado de vista de tabla.

Considera esto:

 - (void)layoutSubviews { [super layoutSubviews]; CGSize size = [self.textLabel.text sizeWithFont:self.textLabel.font constrainedToSize:CGSizeMake(self.textLabel.$width, CGFLOAT_MAX) lineBreakMode:self.textLabel.lineBreakMode]; self.textLabel.$height = size.height; } 

Lamentablemente, en el momento '- tableView: heightForRowAtIndexPath:' se llama, es demasiado pronto, porque cell.textLabel.frame aún está configurado en CGRectZero = {{0, 0}, {0, 0}} .

AFAIK, no podrá hacerlo ni con el marco de la vista de contenido, ni con la sum de los frameworks de las tags individuales …

La única forma en que puedo pensar es proponer una class de conveniencia, methods, constantes o similares que traten de cubrir todo el ancho posible en cualquier orientación del dispositivo, en cualquier dispositivo:

 @interface UITableView (Additions) @property (nonatomic, readonly) CGFloat padding; @end @implementation UITableView (Additions) - (CGFloat)padding { if (self.formStyle == PTFormViewStylePlain) { return 0; } if (self.$width < 20.0) { return self.$width - 10.0; } if (self.$width < 400.0 || [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) { return 10.0; } return MAX(31.0, MIN(45.0, self.$width * 0.06)); } @end 

También tenga en count que, recientemente, también tenemos un nuevo iPhone 5 de 4 pulgadas de ancho (568 en lugar de 480) en orientación horizontal.

Todo esto es bastante inquietante, lo sé … Saludos.