"El diseño automático sigue siendo necesario después de ejecutar -layoutSubviews" con la subclase UITableViewCell

115

Usando XCode 4.5 y iOS 6, estoy desarrollando una aplicación con una vista de tabla simple con celdas personalizadas. He hecho esto cientos de veces en iOS 5 y versiones anteriores, pero por alguna razón el nuevo sistema de autoLayout me está dando muchos problemas.

Configuré mi vista de tabla y celda de prototipo en IB, agregué subvistas y las conecté como IBOutlets, luego configuré mi delegado y fuente de datos. Sin embargo, ahora, cada vez que se obtiene la primera celda, aparece cellForRowAtIndexPathel siguiente error:

*** Error de afirmación en - [ShopCell layoutSublayersOfLayer:], /SourceCache/UIKit_Sim/UIKit-2372/UIView.m:5776

*** Finalizando la aplicación debido a la excepción no detectada 'NSInternalInconsistencyException', motivo: 'El diseño automático sigue siendo necesario después de ejecutar -layoutSubviews. La implementación de ShopCell de -layoutSubviews necesita llamar a super. '

No he implementado un método -layoutSubviews en mi celda de subclases (ShopCell), e incluso cuando trato de hacer eso y agrego la super llamada, ya que sugiere que todavía obtengo el mismo error. Si elimino las subvistas de la celda en IB y las cambio a una UITableViewCell estándar, todo funciona como se esperaba, aunque, por supuesto, no tengo datos en mis celdas.

Estoy casi seguro de que me falta algo simple, pero no puedo encontrar ninguna documentación o guía que sugiera lo que hice mal. Cualquier ayuda sería apreciada.

Editar: Intenté cambiarlo a UITableViewCell en IB y dejar todas las subvistas en su lugar, sigue siendo el mismo error.

Mike Mayo
fuente
Pruebe lldb [[UIWindow keyWindow] _autoLayoutTrace]en el área del depurador si se utiliza el diseño automático.
A-Live
3
¿Está utilizando UIView para la celda personalizada en lugar de UITableViewCell? Yo tuve el mismo problema. Tenía UIView para la celda personalizada y estaba agregando vistas secundarias a eso. Se cambió a UITableViewCell y funcionó.
Hey Mike, ¿cómo estás definiendo los puntos de venta? ¿Son propiedades en su archivo de implementación en una extensión de clase?
kocodude
@ A-Live Siempre que intento usar ese método, aparece un error en el depurador ... ¿este método sigue siendo válido? Editar: No importa, es una l minúscula en el diseño automático.
borrrden
desmarque la casilla de autoLayout en el inspector, luego limpie y ejecute. funcionará malhumorado.
Nico

Respuestas:

57

Encontré el mismo problema al agregar manualmente restricciones en el código. En código, estaba haciendo lo siguiente:

{
    [self setTranslatesAutoresizingMaskIntoConstraints:YES];
    [self addSubview:someView];
    [self addSubview:someOtherView];
    [self addConstraint:...];
}

Hipótesis

Por lo que puedo decir, el problema es que cuando lo deshabilita translatesAutoresizingMaskIntoConstraints, UITableViewCell comienza a usar Auto Layout y, naturalmente, falla porque la implementación subyacente de layoutSublayersForLayerno llama a super. Alguien con Hopper o alguna otra herramienta puede confirmar esto. Como está utilizando IB, probablemente se esté preguntando por qué esto es un problema ... y eso se debe a que el uso de IB desactiva automáticamente las translatesAutoresizingMaskIntoConstraintsvistas a las que agrega restricciones (agregará automáticamente una restricción de ancho y alto en su lugar).

Solución

Mi solución fue trasladar todo al contentView.

{
   [self.contentView addSubview:someView];
   [self.contentView addSubview:someOtherView];
   [self.contentView addConstraint:...];
}

No estoy 100% seguro de si esto funcionará en Interface Builder, pero si empuja todo fuera de su celular (asumiendo que tiene algo directamente en él) entonces debería funcionar. ¡Espero que esto te ayude!

Malaxeur
fuente
4
También necesitaba agregar subview.translatesAutoresizingMaskIntoConstraints = NO'en cada subvista que estaba agregando a contentView.
Jay Peyer
5
Esto funcionó para mí. Además, asegúrese de no llamar self.contentView.translatesAutoresizingMaskIntoConstraints = NOal UITableViewCell.
Maurizio
53

Aparentemente, la implementación de layoutSubviews de UITableViewCell no llama a super, lo cual es un problema con el diseño automático. Me interesaría ver si colocar la siguiente categoría en proyectos soluciona las cosas. Ayudó en un proyecto de prueba.

#import <objc/runtime.h>
#import <objc/message.h>

@implementation UITableViewCell (FixUITableViewCellAutolayoutIHope)

+ (void)load
{
    Method existing = class_getInstanceMethod(self, @selector(layoutSubviews));
    Method new = class_getInstanceMethod(self, @selector(_autolayout_replacementLayoutSubviews));

    method_exchangeImplementations(existing, new);
}

- (void)_autolayout_replacementLayoutSubviews
{
    [super layoutSubviews];
    [self _autolayout_replacementLayoutSubviews]; // not recursive due to method swizzling
    [super layoutSubviews];
}

@end

Podría agregar el problema que se me mostró al usar una vista de fondo en la celda de la tabla, ya que se agrega como una subvista a la celda (mientras que la mayoría de las subvistas deben agregarse a la vista de contenido de la celda de la tabla, que generalmente debería funcionar mejor).

Nota: Parece que este error se corrigió en iOS7; Pude eliminar este código, o al menos agregar una verificación de tiempo de ejecución para que solo se realice si se ejecuta en iOS6.

Carl Lindberg
fuente
Lo extraño es que funciona bien con un UITableViewCell simple para mí, pero no para una subclase ...
borrrden
Pensarías eso, pero todo lo que tengo es un método init, nada más> <. Nunca he anulado las subvistas de diseño en mi vida jaja. Creo que el problema es la vista personalizada que usa UITableViewCell ya que su vista raíz no puede usar el diseño automático porque anula las subvistas de diseño (por lo que cuando intenta agregar restricciones a la vista raíz, fallará)
borrrden
6
Tuve que crear una categoría de este tipo UITableViewpor la misma razón (iOS 6.1 b1)
Joshua J. McKinnon
5
¿Existe una solución similar para TableHeaderView ya que el problema aún existe en ios 7?
Softlion
1
Esto funciona muy bien. Encontré este problema cuando intenté centrar una subvista UIVIew en un UITableView. Incluso en iOS 7 ocurre la afirmación. Pero no ocurre en iOS 8, por lo que deben haber solucionado el error.
Jordan H
33

Tuve el mismo error durante unos meses. Pero encontré cuál era el problema.

Cuando creo un archivo IB, UIViewya está agregado. Si usa esta vista, la aplicación no se bloquea cuando el diseño automático está deshabilitado (pero hay otros problemas). Cuando se usa la disposición de auto, usted tiene que seleccionar el correcto punto de vista en la biblioteca de objetos: UITableViewCell.

De hecho, usted debe siempre utilizar este elemento porque todos subvistas se añaden a la contentViewde la UITableViewCell.

Eso es todo. Todo estará bien.

Arnaud
fuente
Esta no debería ser la respuesta aceptada porque la pregunta no se trata de una implementación que utilice IB y porque este problema puede ocurrir cuando no está utilizando IB. Si está haciendo sus vistas de manera programática, la respuesta de @ PhilLoden es más viable.
Eric
No entendí la respuesta. ¿Alguien puede explicar más claramente? gracias
Hasan
Creo que tengo ese derecho. ¿Es suficiente verificar la clase att. en el inspector de identidad en el generador de interfaces? o el agregado fue de otro tipo y clase att. se ha actualizado más tarde? ¿Eso también causa el problema?
Hasan
@ hasan83 De hecho, puedes devolver la celda. Un UITableViewCell es básicamente un UIView con un identificador de reutilización.
Arnaud
17

Tuve el mismo problema con custom UITableViewHeaderFooterView+ xib.

Vi algunas respuestas aquí, pero encontré qué implementación -layoutSubviewsen mi clase de vista de pie de página personalizada soluciona el problema:

-(void)layoutSubviews
{
    [super layoutSubviews];
    [self layoutIfNeeded]; // this line is key
}
Sound Blaster
fuente
1
Tenga en cuenta que esto puede resultar en un bucle sin fin y finalmente EXC_BAD_ACCESS KERN_PROTECTION_FAILURE
mbi
15

Veía esto como resultado de la modificación de restricciones en mi implementación de layoutSubviews. Mover la llamada a super desde el principio hasta el final del método solucionó el problema.

Phil Loden
fuente
Esto funcionó para mí. Tengo una UICollectionViewCell personalizada que estoy formateando en layoutSubviews. ¿Alguien sabe por qué funciona esta solución?
STANGMMX
@STANGMMX, la respuesta de A'sa Dickens explica por qué.
Fábio Oliveira
15

Tuve el mismo problema en iOS 7 (iOS 8 parece solucionarse). La solución para mí fue llamar [self.view layoutIfNeeded]al final de mi viewDidLayoutSubviewsmétodo.

Keller
fuente
Gracias. Me ayuda Me encontré con este problema ayer (en iOS 7). ayuda para iOS 7.
Alexander
@MaciejSwic vea mi respuesta en la parte superior.
Sound Blaster
¡Esto funcionó para mí! Usando iOS 7.1 con Swift. Estaba eliminando y agregando una restricción en viewDidLayoutSubviews. Eliminé la súper llamada y todavía no funcionaba, ¡pero esta solución funcionó! ¡Dale una hoja a este dinosaurio! :)
jomafer
¡También me funcionó con iOS 7.1!
fdlr
14

Tuve el mismo problema. El problema estaba en la forma en que estaba creando la celda Xib. Creé un Xib como de costumbre y simplemente cambié el tipo de "UIView" predeterminado a mi clase personalizada UITableViewCell. La forma correcta de hacerlo es eliminar primero la vista predeterminada y luego arrastrar el objeto de la celda de la vista de tabla al xib. Más detalles aquí: http://allplayers.github.io/blog/2012/11/18/Custom-UITableViewCell-With-NIB/

Trunal Bhanse
fuente
1
¡Perfecta percepción! Me tomaría años darme cuenta de eso, especialmente porque mis aplicaciones no fallarían si utilizo un UIView con AutoLayout desactivado.
Guilherme
¡Súper! ver también @Arnaud respone a continuación
Lubbo
7

Resolví el problema desactivando "Diseño automático" para todas las subvistas de mi celda de vista de tabla personalizada.

En el xib de una celda personalizada, seleccione una subvista y desmarque el Inspector de archivos> Documento de Interface Builder> Usar diseño automático

Johno
fuente
4
Yo hice lo mismo. Sin embargo, no es realmente una solución si desea usar el diseño automático
ajmccall
7

Tuve un problema similar no UITableViewCellen UITableViewsí mismo sino en sí mismo. Porque es el primer resultado en Google, lo publicaré aquí. Resultó que ese viewForHeaderInSectionera el problema. Creé UITableViewHeaderFooterViewy configuré translatesAutoresizingMaskIntoConstraintsen NO. Ahora aquí viene la parte interesante:

ios 7:

// don't do this on iOS 7
sectionHeader.translatesAutoresizingMaskIntoConstraints = NO;

Si hago esto, la aplicación se bloquea con

El diseño automático sigue siendo necesario después de ejecutar -layoutSubviews. La implementación de UITableView de -layoutSubviews necesita llamar a super.

De acuerdo, pensé que no se puede usar el diseño automático en un encabezado de vista de tabla y solo en las subvistas. Pero esa no es toda la verdad como la verá más adelante. En resumen: no desactive la máscara de cambio de tamaño automático para el encabezado en iOS 7. De lo contrario, está funcionando bien.

iOS 8:

// you have to do this, otherwise you get an auto layout error
sectionHeader.translatesAutoresizingMaskIntoConstraints = NO;

Si no usara esto, obtendría el siguiente resultado:

Incapaz de satisfacer simultáneamente las limitaciones.

Para iOS 8, debe deshabilitar la máscara de cambio de tamaño automático para el encabezado.

No sé por qué se comporta de esta manera, pero parece que Apple solucionó algunas cosas en iOS 8 y el diseño automático funciona de manera diferente en iOS 7 e iOS 8.

pruebas
fuente
¡Compañero, me acaba de salvar el día!
Marcin Małysz
5

Como ya ha dicho alguien anteriormente, cuando crea una vista para usar en un UITableView, debe eliminar la vista creada por defecto y arrastrar un UITableViewCell o UITableViewHeaderFooterView como la vista raíz. Sin embargo, hay una forma de arreglar el XIB en caso de que se haya perdido esa parte. Debe abrir el archivo XIB en un editor de texto y en la etiqueta raíz y su hijo directo agregar / cambiar el atributo translatesAutoresizingMaskIntoConstraintsa YES, por ejemplo

<view contentMode="scaleToFill" horizontalHuggingPriority="1000" id="1" translatesAutoresizingMaskIntoConstraints="YES" customClass="MXWCollapsibleTableHeaderView">

Maksymilian Wojakowski
fuente
2

Me encuentro con esto y parece que está relacionado con las subclases de UITableViewCell como celdas prototipo que específicamente tienen otras subclases de UIView personalizadas agregadas. Hago hincapié en la 'costumbre' aquí porque he tenido éxito con celdas que solo tienen hijos UIKit, pero se cae al tratar de construir las restricciones para las vistas que he creado a medida, arrojando el error indicado en la pregunta del autor.

Tuve que separar mis celdas en plumillas independientes que no usan AutoLayout.

Esperemos que Apple limpie este lío.

Lee Probert
fuente
2

Agregue sus subvistas al contentView de la celda en lugar de la celda en sí. Entonces en lugar de:

[self addSubview:someView];

debes usar

[self.contentView addSubview:someView];

Christian Aberger
fuente
1

Encontré este porque inicialmente había agregado un UIView en lugar de un UITableViewCell a un archivo xib.

mjmdavis
fuente
1

Eliminé este error desacoplando el backgroundViewconector de mi fondo UIImageViewy el accessoryViewconector de mis UIButtonpersonalizaciones. Sospecho que estos no estaban destinados a ser usados ​​de la forma en que los estaba usando.

Owen Godfrey
fuente
1

Hoy me había encontrado con este problema por primera vez. Hasta ahora, tenía varias experiencias en el uso de subclases prototipo UITableViewCell, pero nunca encontré este problema. Lo diferente de la celda con la que estaba trabajando era que tenía un IBOutlet para -backgroundView que estaba usando para colorear la celda. Descubrí que si creaba una nueva propiedad y aún agregaba una UIView nueva que extendía el lapso de toda la celda, esta afirmación desaparecía. Para verificar que esta era la causa, volví a adjuntar esta vista a la salida backgroundView y reapareció la afirmación. Hasta ahora, no hay otros problemas al usar AutoLayout en un prototipo de subclase UITableViewCell desde que hice este cambio.

Eric Schramm
fuente
1

No obtuve ninguna solución adecuada para este problema, pero puede solucionarlo usando marcos y no configurando la propiedad translatesAutoresizingMaskIntoConstraints en No (de forma predeterminada es sí, así que no lo configure)

CGRect headerViewFrame = CGRectMake(0,0,320,60); //Frame for your header/footer view
UIView *tableHeaderView = [[UIView alloc] initWithFrame:headerViewFrame];
tableHeaderView.translatesAutoresizingMaskIntoConstraints = Yes; //Or don't set it at all
[self.tableView setTableHeaderView:tableHeaderView];
sanjana
fuente
0

Yo he estado experimentando lo mismo. Resultó que si agrega programáticamente una subvista desde su ShopCell .xib / storyboard, que usa el diseño automático, como una subvista a otra vista, esa excepción podría lanzarse, dependiendo de cómo estén configuradas sus restricciones. Supongo que las restricciones creadas en IB son las que crean los problemas al agregar programáticamente una vista como una subvista, ya que luego mantiene las restricciones de viewA -> viewB mientras tanto, puede agregar viewB como una subvista de viewC. ¿Lo entendiste (esa frase incluso me confunde)?

En mi situación, dado que fueron vistas muy simples las que causaron el problema, creé las vistas programáticamente y no en IB. Eso lo resolvió. Puede extraer esas vistas a otros archivos xib y deshabilitar el diseño automático para esos. Supongo que funcionaría.

Kasper Munck
fuente
0

En algunas situaciones, esto resuelve el problema de diseño fácilmente (dependiendo de su diseño). Dentro de su subclase UITableView, en awakeFromNib o init, configure la máscara de autoresizing:

self.contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;

Por defecto, se establece en UIViewAutoresizingNone

Arie Litovsky
fuente
Esto resolvió el problema al que me enfrentaba. Estoy usando el diseño automático dentro de una celda de la tabla combinado con [tableViewCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].heightpara obtener la altura, que luego uso heightForRowAtIndexPath.
NathanAldenSr
0

En mi caso,

El UIImageView al que se hace referencia para el diseño automático de UITableView se asigna a backgroundView de UITableView.

self.tableView.backgroundView = self.tableBackgroundImageView;

Entonces, eliminé UIImageView para backgroundView de UIView (vista raíz) y restablecí (eliminé) todas las referencias de diseño automático a ese UIImageView. Coloqué ese UIImageView para el fondo en el exterior de UIView (vista raíz). Y luego asigne al backgroundView de UITableView en el código.

Luego arreglado.

Conan Kim
fuente
0

He encontrado la solución.

En mi caso, creé la vista de la celda en el guión gráfico (con el diseño automático habilitado) y definí la interfaz personalizada UITableViewCell en mi ViewController.m, tengo que mover la interfaz a ViewController.h.

Nim Thitipariwat
fuente
0

Encontré el mismo problema cuando uso el guión gráfico para crear el UITableViewCell personalizado. Afortunadamente encontré el problema, porque saqué el accesorio de vista ([UITableViewCell setAccessoryView:]) a UIButton que agregué a la celda.

Entonces ocurrió en mi proyecto cuando se ejecutó en iOS6.

Solución

Libero la salida entre accesorioView y mi botón que contenía la celda personalizada.

Propuesta

No debe utilizar los elementos nativos de UITableViewCell y cambiarlos.

Wanqiang Ji
fuente
0

Este problema puede deberse a olvidar llamar desde [super viewDidAppear:]dentro viewDidAppear, pero estoy seguro de que no es la única causa.

michaelsnowden
fuente
0

Tuve exactamente el mismo problema. Aquí está el problema con mi proyecto:
cuando trabajé en Interface Builder para crear una UITableViewCell personalizada, arrastré una Vista en lugar de una Celda de Vista de Tabla desde el panel de colección de objetos en Xcode
como celda de tabla personalizada.
Si se encuentra en la misma situación, esta es la solución:
elimine la vista en el generador de interfaces, asegúrese de arrastrar una celda de vista de tabla desde el panel de colección de objetos y rehaga la vista de celda de tabla personalizada. Puede copiar los objetos en la vista anterior y pegarlos en el lienzo de la nueva celda de vista de tabla.

us_david
fuente
0

Tuve un problema muy similar con una vista de pie de tabla que estaba configurando en Xcode 6, iOS 7+. La solución estaba en el formato del archivo nib. Aparentemente estaba atascado en formato Xcode 4 o algo así. Cambiar la configuración del archivo a "abre en: Xcode 6.0" (o predeterminado, para el caso), lo solucionó instantáneamente. Encontré la solución por casualidad: me estaba volviendo loco, así que eliminé todo el archivo y lo volví a crear, obviamente con la configuración predeterminada. No tengo idea de por qué simplemente editar el archivo en el último Xcode no lo convirtió a un formato Xcode 5+, como suele suceder.

F

usuario3099609
fuente
0

Fui y tuve el mismo problema. Fui a mi DetailViewController y cambié el nombre del identificador a UIView. Anteriormente estaba en UITableView. Solucionó el problema. Este problema no tiene que estar en su DetailViewController. Podría estar en cualquier otro. Intente cambiarle el nombre al identificador respetado.

RandomDude
fuente
0

Tuve un problema similar con las celdas de vista de tabla estática en IB. Una de las celdas tenía una subvista que tenía una clase que se modificó por error a una subclase de UITextfield. El compilador no dio ninguna advertencia / error. Pero en tiempo de ejecución, el sistema no pudo cargar el controlador de vista con el bloqueo mencionado anteriormente como resultado.

Yannick Winters
fuente
0

El problema es la secuenciación de las llamadas de diseño a las subvistas:

Revisa

Aparece en iOS <8

Shanu ji
fuente
0

Solución: cambie las restricciones antes de llamar al súper diseño

- (void)layoutSubviews
{

    [self _updateConstraints];

    [super layoutSubviews];
}
abuharsky
fuente
0

He modificado la respuesta de Carl Lindberg para anular UITableViewsu lugar y comenzó a trabajar para mí:

UITableView + AutoLayoutFix.h

@interface UITableView (AutoLayoutFix)
@end

UITableView + AutoLayoutFix.m

#import <objc/runtime.h>

@implementation UITableView (AutoLayoutFix)

+ (void)load
{
    Method existingMethod = class_getInstanceMethod(self, @selector(layoutSubviews));
    Method newMethod = class_getInstanceMethod(self, @selector(_autolayout_replacementLayoutSubviews));

    method_exchangeImplementations(existingMethod, newMethod);
}

- (void)_autolayout_replacementLayoutSubviews
{
    [super layoutSubviews];
    [self _autolayout_replacementLayoutSubviews]; // not recursive due to method swizzling
    [super layoutSubviews];
}

@end

Luego MyViewController.m, acabo de importar la categoría:

#import "UITableView+AutoLayoutFix.h"
arador
fuente
0

Encontré el mismo problema y finalmente descubrí que la razón fue que agregué una restricción a UITableViewCell, que debería ser el contentView de UITableViewCell . Cuando cambié la restricción, ¡todo salió bien!

Alfy
fuente