El problema de tamaño automático del marco UICollectionViewCell contentView en la celda prototipo de Storyboard (Xcode 6, iOS 8 SDK) ocurre cuando se ejecuta solo en iOS 7

158

Estoy usando Xcode 6 Beta 3, iOS 8 SDK. Construye Target iOS 7.0 usando Swift. Consulte mi problema paso a paso con las capturas de pantalla a continuación.

Tengo un UICollectionView en Storyboard. 1 Prototype UICollectionViewCell que contiene 1 etiqueta en el centro (sin regla de autoresizing). Supongo que el fondo púrpura debía marcar una vista de contenido generada en tiempo de ejecución por la celda. Esa vista se redimensionará correctamente en función de mi UICollectionViewLayoutDelegate eventualmente, pero no en iOS 7. Tenga en cuenta que estoy usando Xcode 6 y el problema solo ocurre en iOS 7.

Cuando construyo la aplicación en iOS 8. Todo está bien.

Nota: Purple es contentView , Blue es mi UIButton con esquina redondeada.

http://i.stack.imgur.com/uDNDY.png

Sin embargo, en iOS 7, todas las subvistas dentro de la celda se reducen repentinamente al marco de (0,0,50,50) y nunca más se ajustan a mi regla de Autoresizing.

http://i.stack.imgur.com/lOZH9.png

Supongo que esto es un error en iOS 8 SDK o Swift o tal vez Xcode.


Actualización 1: ¡ Este problema todavía existe en el Xcode 6.0.1 oficial! La mejor solución es como lo que KoCMoHaBTa sugirió a continuación al establecer el marco en cellForItem de la celda (aunque debe subclasificar su celda). Resultó que esto es una incompatibilidad entre iOS 8 SDK e iOS 7 (verifique la respuesta de ecotax a continuación citada por Apple).

Actualización 2: pegue este código al comienzo de su cellForItem y las cosas deberían estar bien:

/** Xcode 6 on iOS 7 hot fix **/
cell.contentView.frame = cell.bounds;
cell.contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
/** End of Xcode 6 on iOS 7 hot fix **/
espesar
fuente
1
Descubrí que este problema todavía existe en Xcode 6 Beta 5. ¿Alguien también experimentó esto?
thkeen
2
Estoy luchando con esto ahora mismo en mi proyecto. iOS 7 y iOS 8 creados con Xcode 5 se ven bien. iOS 8 construido con Xcode 6 beta 6 se ve bien. iOS 7 creado con Xcode 6 beta 6 tiene el problema que está describiendo. Usando Reveal puedo ver que mi UICollectionViewCell se ha dimensionado correctamente. Pero la vista de contenido de la celda no cambió de tamaño, a pesar de que es primaria, la UICollectionViewCell tiene activadas las Subvistas de Autoresize. El tamaño de contentView se establece según el guión gráfico. No estoy usando autolayout en este proyecto. Mi proyecto es completamente objetivo-c.
Del Brown
2
Solo quería agregar que este problema aún existe a partir de la semilla GM de Xcode 6 / iOS 8. La respuesta de @ DanielPlamann para forzar el cambio contentViewde tamaño con la celda funciona bien para solucionar el problema. Supongo que en iOS 8 Apple cambió algo sobre la forma en que se manejan las vistas de contenido celular cuando se crea en Interface Builder (que de todos modos todavía es un poco un cuadro negro). Pero el hecho de que cambie el comportamiento al apuntar a iOS 7 seguramente es un error.
Stuart
Lo mismo aquí con Xcode 6 GM, diseño automático y una celda basada en plumín. Lo arreglo fijando los contentViewbordes a los bordes de la celda.
sergiou87
3
Descargué xcode 6.1 pero aún veo el mismo problema en el simulador.
Haitao Li

Respuestas:

169

contentView está roto. También puede repararse en wakekeFromNib

ObjC:

- (void)awakeFromNib {

    [super awakeFromNib];

    self.contentView.frame = self.bounds;
    self.contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
}

Swift3:

override func awakeFromNib() {
    super.awakeFromNib()

    self.contentView.frame = self.bounds
    self.contentView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
}
Igor Palaguta
fuente
3
Hizo el truco sin self.contentView.frame = self.bounds; ¿Crees que lo necesito? ¡Gracias de cualquier manera!
Michal Shatz
Hola Michal, de acuerdo contigo. Pero para estar seguro al 100%, creo que cómo funcionará en los próximos iOSes mejor para agregarlo.
Igor Palaguta
55
Me gusta esta respuesta pero debes llamar a [super awakeFromNib]; Además, estoy pensando en agregar una verificación para iOS 7.1 y menos, porque no estoy seguro de cómo agregar estas máscaras de cambio de tamaño afecta el comportamiento predeterminado en iOS8.
GingerBreadMane
Si no estás usando plumillas o guiones gráficos, esto también funciona si lo pones en applyLayoutAttributes:
cetcet
Esto no funciona para mi. No estoy usando Autolayout !! Cualquier ayuda?
thatzprem
61

Encontré el mismo problema y le pedí ayuda a Apple DTS. Su respuesta fue:

En iOS 7, las vistas de contenido de las celdas se dimensionaron a través de máscaras de tamaño automático. En iOS 8, esto se modificó, las celdas dejaron de usar las máscaras de autoresizing y comenzaron a dimensionar la vista de contenido en layoutSubviews. Si un plumín está codificado en iOS 8 y luego lo decodifica en iOS 7, tendrá una vista de contenido sin una máscara de autorización y ningún otro medio para dimensionarse. Entonces, si alguna vez cambia el marco de la celda, la vista de contenido no seguirá.

Las aplicaciones que se están implementando de nuevo en iOS 7 tendrán que solucionar este problema ajustando el tamaño de la vista de contenido en sí, agregando máscaras de autorización o agregando restricciones.

Supongo que esto significa que no es un error en XCode 6, sino una incompatibilidad entre el SDK de iOS 8 y el SDK de iOS 7, que te afectará si actualizas a Xcode 6, porque automáticamente comenzará a usar el SDK de iOS 8.

Como comenté antes, la solución que Daniel Plamann describió funciona para mí. Sin embargo, las descritas por Igor Palaguta y KoCMoHaBTa parecen más simples y parecen tener sentido al dar la respuesta de Apple DTS, así que las intentaré más adelante.

ecotax
fuente
Esto es interesante, pero todavía espero que lo arreglen. Este comportamiento solo se introdujo en Xcode 6 GM que agregó soporte para iPhone 6. Funcionaba bien en las versiones beta anteriores. Incluso llevé un proyecto a una versión beta anterior después de notarlo y funcionó como se esperaba. Espero que todos presenten errores con Apple en este tema.
arton
@arton Archivé un informe de error el mismo día que le pedí ayuda a DTS. Fue cerrado como un duplicado de 18312246. No estoy seguro de cuánto ayuda esto.
ecotax
He utilizado el trabajo al cambiar el tamaño de ContentView y funciona para mí. - (CGSize) collectionViewContentSize {return CGSizeMake (self.collectionView.bounds.size.width, self.collectionView.bounds.size.height); }
Jesús Hurtado
60

Encontré el mismo problema y espero que Apple solucione esto con la próxima versión de Xcode. Mientras tanto, uso una solución alternativa. En mi UICollectionViewCellsubclase, he anulado layoutSubviewsy redimensionado manualmente contentView en caso de que el tamaño difiera del collectionViewCelltamaño.

- (void)layoutSubviews
{
  [super layoutSubviews];

  BOOL contentViewIsAutoresized = CGSizeEqualToSize(self.frame.size, self.contentView.frame.size);

  if( !contentViewIsAutoresized) {
    CGRect contentViewFrame = self.contentView.frame;
    contentViewFrame.size = self.frame.size;
    self.contentView.frame = contentViewFrame;
  }
}
Daniel Plamann
fuente
Sí, utilicé un trabajo similar pero lo hice en cellForItem / cellForRow, que también funciona.
Thkeen
1
Esta es la mejor solución. Agregar esto en cellForItem / cellForRow causará artefactos extraños si está girando el dispositivo y el tamaño de la celda cambia.
spybart
¡Hola! Tengo un problema con este código. Por primera vez, el contenido booleano ViewIsAutoresized será verdadero si se carga desde el guión gráfico o la celda prototipo. Solo cuando haces un reloadData, el segundo será correcto. Por lo tanto, realmente no necesita verificar el tamaño. En su lugar, simplemente haga: self.contentView.frame = self.bounds;
thkeen
Confirmado: deberíamos hacerlo en cellForItem o cellForRow porque layoutSubviews solo se llama después de que se devuelve la celda. Cualquier cosa que hagas antes, como dibujar cosas, se calculará erróneamente.
Thkeen
1
Hm, ¿quizás sea mejor colocarlo [cell layoutIfNeeded];en cellForItem o cellForRow?
wtorsi
38

Otra solución es establecer el tamaño de contentView y las máscaras de autorización de -collectionView:cellForItemAtIndexPath:la siguiente manera:

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {

     static NSString *cellID = @"CellID";

     UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellID forIndexPath:indexPath];

     // Set contentView's frame and autoresizingMask
     cell.contentView.frame = cell.bounds;
     cell.contentView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleRightMargin |UIViewAutoresizingFlexibleTopMargin |UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleBottomMargin;

     // Your custom code goes here

     return cell;
}

Esto también funciona con el diseño automático, ya que las máscaras de cambio de tamaño automático se traducen en restricciones.

KoCMoHaBTa
fuente
2
Esta es una solución superior, ya que funciona con cualquier tipo de celda sin subclases y no requiere cambios en varios lugares cuando está utilizando más de un tipo de celda en su vista de colección.
Nacross
6

En Xcode 6.0.1, contentView para UICollectionViewCell está roto para dispositivos iOS7. También se puede solucionar agregando restricciones adecuadas a UICollectionViewCell y su contentView en los métodos awakeFromNib o init.

        UIView *cellContentView = self.contentView;
        cellContentView.translatesAutoresizingMaskIntoConstraints = NO;

        [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[cellContentView]|"
                                                                     options:0
                                                                     metrics:0
                                                                       views:NSDictionaryOfVariableBindings(cellContentView)]];
        [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[cellContentView]|"
                                                                     options:0
                                                                     metrics:0
                                                                       views:NSDictionaryOfVariableBindings(cellContentView)]];
SerJ_G
fuente
Solo que esto funciona ahora, ¡debe hacerlo solo para iOS 8, y tengo que llamarlo después de cada espera! Pero esta tampoco es la solución ideal porque la selección múltiple no funciona como debería visualmente ...
Renetik
Lo mejor es usar la distribución automática realmente, con este error ahorras irónicamente tiempo al usar el evento de distribución automática en un caso simple ...
Renetik
La máscara no funciona para mí; ¡Sin embargo, establecer las restricciones funciona!
entropid
4

Esto no funcionará correctamente sin ninguna de las otras soluciones mencionadas debido a un error en Xcode 6 GM con la forma en que Xcode compila los archivos xib en el formato nib. Si bien no puedo decir con 100% de certeza que está relacionado con Xcode y no tiene que ver con el tiempo de ejecución, estoy muy seguro: así es como puedo mostrarlo:

  1. Construir + Ejecutar la aplicación en Xcode 5.1.
  2. Vaya al directorio de la aplicación del simulador y copie el archivo compilado .nib para el xib con el que tiene problemas.
  3. Construir + Ejecutar la aplicación en Xcode 6 GM.
  4. Detener la aplicación
  5. Reemplace el archivo .nib en la carpeta del simulador de la aplicación recién creada con el archivo .nib creado con Xcode 5.1
  6. Vuelva a iniciar la aplicación desde el simulador, NO desde Xcode.
  7. Su celda cargada desde ese .nib debería funcionar como se esperaba.

Espero que todos los que lean esta pregunta presenten un radar con Apple. Este es un problema ENORME y debe abordarse antes de la versión final de Xcode.

Editar: a la luz de la publicación de ecotax , solo quería actualizar esto para decir que ahora se confirman las diferencias de comportamiento entre construir en iOS 8 vs iOS 7, pero no es un error. Mi truco solucionó el problema porque la construcción en iOS 7 agregó la máscara de tamaño automático a la vista de contenido necesaria para que esto funcione, que Apple ya no agrega.

Acey
fuente
No estoy seguro de si legalmente pueden lanzar un xCode diferente a la versión GM
Mabedan
Jaja van a ir a la cárcel? = P Pero con toda seriedad, seguro que pueden. Tenían múltiples versiones de GM para Mavericks si no lo recuerdas. No es muy común, pero puede suceder.
Acey
4

Las respuestas en esta publicación funcionan, lo que nunca entendí es por qué funciona.

Primero, hay dos "reglas":

  1. Para las vistas creadas mediante programación (Ej. [UIView new]), La propiedad translatesAutoresizingMaskIntoConstraintsse establece enYES
  2. Las vistas creadas en el generador de interfaces, con AutoLayout habilitado, tendrán la propiedad translatesAutoresizingMaskIntoConstraintsestablecida enNO

La segunda regla no parece aplicarse a las vistas de nivel superior para las que no define restricciones. (Ej. La vista de contenido)

Al mirar una celda de Storyboard, observe que la celda no está contentViewexpuesta. No estamos "controlando" el contentView, Apple lo está.

Profundice en el código fuente del guión gráfico y vea cómo contentViewse define la celda:

<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">

Ahora las subvistas de la celda (observe el translatesAutoresizingMaskIntoConstraints="NO"):

<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="NaT-qJ-npL" userLabel="myCustomLabel">

El contentViewno tiene está translatesAutoresizingMaskIntoConstraintsconfigurado en NO. Además, carece de definición de diseño, tal vez por lo que dijo @ecotax .

Si nos fijamos en el contentView, tiene una máscara de autoresizing, pero no tiene una definición: <autoresizingMask key="autoresizingMask"/>

Entonces hay dos conclusiones:

  1. contentView translatesAutoresizingMaskIntoConstraintsestá ajustado a YES.
  2. contentView carece de definición de un diseño.

Esto nos lleva a dos soluciones de las que se ha hablado.

Puede configurar las máscaras de tamaño automático manualmente en awakeFromNib:

self.contentView.frame = cell.bounds;
self.contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;

O puede establecer la contentView translatesAutoresizingMaskIntoConstraintsa NOen awakeFromNiby definir restricciones en - (void)updateConstraints.

kgaidis
fuente
4

Esta es la versión Swift de la respuesta de @ Igor que es aceptada y gracias por su buena respuesta compañero.

Primero vaya a su UICollectionViewCellsubclase y pegue el siguiente código, ya que está dentro de la clase.

override func awakeFromNib() {
    super.awakeFromNib()
    self.contentView.frame = self.bounds
    self.contentView.autoresizingMask = [.FlexibleHeight, .FlexibleWidth]
}

Por cierto, estoy usando Xcode 7.3.1 y Swift 2.3. La solución se ha probado en iOS 9.3, que funciona perfectamente.

Gracias, espero que esto haya ayudado.

en la terminación
fuente
lo siento por mi mal inglés, quise decir "funciona como un encanto" :)
Aznix
@Aznix No hay problema .. :)
onCompletion
2

En swift, coloque el siguiente código en la subclase de celdas de la vista de colección:

override var bounds: CGRect {
  didSet {
    // Fix autolayout constraints broken in Xcode 6 GM + iOS 7.1
    self.contentView.frame = bounds
  }
}
Ian
fuente
Esta fue la respuesta que estaba buscando. Solía ​​anular setBounds en Objective-C para solucionar este problema (no estaba seguro de cómo escribirlo en Swift). Gracias :)
Matthew Cawley
1

He descubierto que también hay problemas con el contentViewtamaño en iOS 8. Tiende a presentarse muy tarde en el ciclo, lo que puede causar conflictos de restricción temporales. Para resolver esto, agregué el siguiente método en una categoría de UICollectionViewCell:

- (void)fixupContentView
{
#if __IPHONE_OS_VERSION_MAX_ALLOWED < 80100
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
    if (NSFoundationVersionNumber <= NSFoundationVersionNumber_iOS_7_1) {
        self.contentView.frame = self.bounds;
        self.contentView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleRightMargin |UIViewAutoresizingFlexibleTopMargin |UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleBottomMargin;
    } else {
        [self layoutIfNeeded];
    }
#endif
#endif
}

Este método debe llamarse después de quitar la célula.

phatmann
fuente
Sería ideal si este método se llamara automáticamente. No me encanta, pero esto se puede hacer al rociar dequeueReusableCellWithReuseIdentifier:forIndexPath:.
phatmann
Apple parece haber solucionado este problema en la versión 8.1 del SDK de iOS en Xcode 6.1 GM (compilación 6A1042b). Por lo tanto, he actualizado el código anterior para que no se ejecute al usar el SDK 8.1. Una vez que su equipo se haya mudado a Xcode 6.1, puede eliminar este truco por completo.
phatmann
1

Lo arreglé haciendo esto:

override func layoutSubviews() {
   contentView.superview?.frame = bounds
   super.layoutSubviews()
}

ver: aquí

Serluca
fuente
-1

Solo asegúrese de marcar la casilla de verificación "Autoresize subviews" en la punta de esa celda de vista de colección. Funcionará bien tanto en iOS 8 como en iOS 7.

Deepak GM
fuente
No, no lo hace. Esta es una célula prototipo incrustada.
engrosar el