fecha - ios 12 lanzamiento
En iOS 12, cuando las celdas de diseƱo de UICollectionView, usan autolayout en nib (7)
Aquí hay otra solución que funciona en el ejemplo de código de Cœur , y también funcionó para mi caso particular, donde las otras respuestas no lo hicieron. El siguiente código reemplaza la implementación anterior de la subclase CollectionViewCell
en ViewController.swift
:
class CollectionViewCell: UICollectionViewCell {
@IBOutlet weak var label: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
contentView.translatesAutoresizingMaskIntoConstraints = false
let leftConstraint = contentView.leftAnchor.constraint(equalTo: leftAnchor)
let rightConstraint = contentView.rightAnchor.constraint(equalTo: rightAnchor)
let topConstraint = contentView.topAnchor.constraint(equalTo: topAnchor)
let bottomConstraint = contentView.bottomAnchor.constraint(equalTo: bottomAnchor)
NSLayoutConstraint.activate([leftConstraint, rightConstraint, topConstraint, bottomConstraint])
}
}
Esto está inspirado en la respuesta de ale84 de UICollectionViewFlowLayout estimadoItemSize no funciona correctamente con iOS12, aunque funciona bien con iOS 11. *
El mismo código como este
collectionLayout.estimatedItemSize = CGSize(width: 50, height: 25)
collectionLayout.itemSize = UICollectionViewFlowLayoutAutomaticSize
collectionLayout.minimumInteritemSpacing = 10
for _ in 0 ..< 1000 {
let length = Int(arc4random() % 8)
let string = randomKeyByBitLength(length)
array.append(string!)
}
collectionView.reloadData()
restricciones de celda:
cuando lo ejecuto en iOS 12, es diferente. El simulador de la izquierda es iOS 11 y la derecha es iOS 12:
Pero, cuando lo muevo, los marcos de las celdas serán normales.
Proyecto de ejemplo para reproducir el problema: https://github.com/Coeur/StackOverflow51375566
El problema es que la característica que se presenta aquí, las celdas de vista de colección que se dimensionan a sí mismas en función de sus restricciones internas, no existe . Nunca ha existido. Apple afirma que lo hace, pero no lo hace. He presentado un error en esto cada año desde que se introdujeron las vistas de la colección y esta reclamación se hizo por primera vez; y mis informes de errores nunca se han cerrado, porque el error es real. No existe tal cosa como el tamaño de las celdas de la vista de colección.
Véase también mi respuesta aquí: https://.com/a/51585910/341994 /
En algunos años, el intento de usar celdas de auto-tamaño se ha estrellado. En otros años, no se bloquea, pero hace mal el diseño. Pero no funciona .
La única manera de hacer este tipo de cosas es implementar el método delegado sizeForItemAt
y suministrar el tamaño usted mismo . Puedes hacerlo fácilmente llamando
cell.contentView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
en una celda modelo que haya configurado de antemano. Eso es lo que el tiempo de ejecución debería hacer por usted, pero no lo hace.
Así que aquí está mi solución a la pregunta original. En lugar de una simple matriz de cadenas, hemos generado una matriz de pares de tamaño de cadena (como una tupla). Entonces:
override func collectionView(_ collectionView: UICollectionView,
cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! MyCell
cell.label.text = self.array[indexPath.row].0
return cell
}
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {
return self.array[indexPath.row].1
}
La solución 2 de Cœur ''s evita que el diseño parpadee o se actualice frente al usuario. Pero puede crear problemas al girar el dispositivo. Estoy usando una variable "shouldInvalidateLayout" en viewWillLayoutSubviews y la establezco en false en viewDidAppear.
private var shouldInvalidateLayout = true
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
shouldInvalidateLayout = false
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
if shouldInvalidateLayout {
collectionView.collectionViewLayout.invalidateLayout()
}
}
Para todas las soluciones, tenga en cuenta que no es necesario llamar explícitamente a reloadData
en viewDidLoad
: esto sucederá automáticamente.
Solución 1
Inspirado por la idea de Samantha : invalidateLayout
asincrónicamente en viewDidLoad
.
override func viewDidLoad() {
super.viewDidLoad()
//[...]
for _ in 0 ..< 1000 {
array.append(randomKeyByBitLength(Int(arc4random_uniform(8)))!)
}
DispatchQueue.main.async {
self.collectionView.collectionViewLayout.invalidateLayout()
}
}
Solucion 2
(imperfecto, ver DHennessy13 mejora en él)
Basado en la respuesta de Peter Lapisu . invalidateLayout
en viewWillLayoutSubviews
.
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
collectionView.collectionViewLayout.invalidateLayout()
}
Como señaló DHennessy13, esta solución actual con viewWillLayoutSubviews
es imperfecta, ya que invalidará el viewWillLayoutSubviews
al rotar la pantalla.
Puede seguir la mejora de DHennessy13 con respecto a esta solución.
Solucion 3
Basado en una combinación de la respuesta de Tyler Sheaffer , el puerto de Shawn Aukstak a Swift y la idea de Samantha. Subclase su CollectionView para realizar invalidateLayout
en layoutSubviews
.
class AutoLayoutCollectionView: UICollectionView {
private var shouldInvalidateLayout = false
override func layoutSubviews() {
super.layoutSubviews()
if shouldInvalidateLayout {
collectionViewLayout.invalidateLayout()
shouldInvalidateLayout = false
}
}
override func reloadData() {
shouldInvalidateLayout = true
super.reloadData()
}
}
Esta solución es elegante ya que no requiere cambiar su código de ViewController. Lo he implementado en la rama AutoLayoutCollectionView de este proyecto de ejemplo https://github.com/Coeur/51375566/tree/AutoLayoutCollectionView .
Solucion 4
Reescriba las restricciones predeterminadas de UICollectionViewCell. Ver la respuesta de Larry .
Solucion 5
Implemente collectionView(_:layout:sizeForItemAt:)
y devuelva cell.contentView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
. Ver respuesta mate .
Prueba esto
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
DispatchQueue.main.async {
self.collectionView.collectionViewLayout.invalidateLayout()
}
}
Por viewDidAppear
agregar a viewDidAppear
y viewWillAppear
funcionará. Pero viewDidAppear
causará una falla en el usuario.
Tengo el mismo problema, las celdas usan el tamaño estimado (en lugar del tamaño automático) hasta que se desplaza. El mismo código creado con Xcode 9.x funciona perfectamente bien en iOS 11 y 12, y construido en Xcode 10 funciona correctamente en iOS 11 pero no en iOS 12.
La única manera que he encontrado hasta ahora para solucionar este problema es invalidar el diseño de la vista de colección en viewDidAppear
, lo que puede causar algunos saltos, o en un bloque asíncrono dentro de viewWillAppear
(no estoy seguro de cuán confiable es esa solución).
override func viewDidLoad() {
super.viewDidLoad()
let layout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout
layout?.estimatedItemSize = CGSize(width: 50, height: 50)
layout?.itemSize = UICollectionViewFlowLayout.automaticSize
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// The following block also "fixes" the problem without jumpiness after the view has already appeared on screen.
DispatchQueue.main.async {
self.collectionView.collectionViewLayout.invalidateLayout()
}
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// The following line makes cells size properly in iOS 12.
collectionView.collectionViewLayout.invalidateLayout()
}
Tuvimos el mismo problema en nuestro proyecto. También notamos diferencias entre los múltiples dispositivos en iOS 12, que requieren una llamada a layoutIfNeeded
& invalidateLayout
. La solución se basa en el enfoque de @ DHennessy13, pero no requiere un booleano para administrar estados que parecían un poco piratas.
Aquí se basa en un código Rx, básicamente, la primera línea es cuando los datos están cambiando, dentro de la subscribe
es lo que se debe hacer para corregir el desagradable problema de la interfaz de usuario de iOS 12:
viewModel.cellModels.asObservable()
.subscribe(onNext: { [weak self] _ in
// iOS 12 bug in UICollectionView for cell size
self?.collectionView.layoutIfNeeded()
// required for iPhone 8 iOS 12 bug cell size
self?.collectionView.collectionViewLayout.invalidateLayout()
})
.disposed(by: rx.disposeBag)
Editar:
Por cierto, parece ser un problema conocido en iOS 12: https://developer.apple.com/documentation/ios_release_notes/ios_12_release_notes (en la sección UIKit).