with uicollectionviewflowlayoutautomaticsize collection automatic ios uicollectionview autolayout

ios - uicollectionviewflowlayoutautomaticsize - UICollectionView, celdas de ancho completo, ¿permiten la altura dinámica de la distribución automática?



uicollectionview resize cell (14)

# 1 Solución para iOS 13

Con Swift 5.1 y iOS 13, puede usar objetos de diseño compositivo para resolver su problema.

El siguiente código de muestra completo muestra cómo mostrar UILabel multilínea dentro de UILabel de ancho UICollectionViewCell :

CollectionViewController.swift

import UIKit class CollectionViewController: UICollectionViewController { let array = [ "Lorem ipsum.", "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt.", "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.", "Lorem ipsum dolor sit amet.", "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam." ] override func viewDidLoad() { super.viewDidLoad() let size = NSCollectionLayoutSize( widthDimension: NSCollectionLayoutDimension.fractionalWidth(1), heightDimension: NSCollectionLayoutDimension.estimated(44) ) let item = NSCollectionLayoutItem(layoutSize: size) let group = NSCollectionLayoutGroup.horizontal(layoutSize: size, subitems: [item]) let section = NSCollectionLayoutSection(group: group) section.contentInsets = NSDirectionalEdgeInsets(top: 5, leading: 5, bottom: 5, trailing: 5) section.interGroupSpacing = 5 let layout = UICollectionViewCompositionalLayout(section: section) collectionView.collectionViewLayout = layout collectionView.register(CollectionViewCell.self, forCellWithReuseIdentifier: CollectionViewCell.cellIdentifier) } override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return array.count } override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CollectionViewCell.cellIdentifier, for: indexPath) as! CollectionViewCell cell.label.text = array[indexPath.row] return cell } }

CollectionViewCell.swift

import UIKit class CollectionViewCell: UICollectionViewCell { static let cellIdentifier = "CellIdentifier" let label = UILabel() override init(frame: CGRect) { super.init(frame: frame) label.numberOfLines = 0 backgroundColor = .orange contentView.addSubview(label) label.translatesAutoresizingMaskIntoConstraints = false label.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true label.leadingAnchor.constraint(equalTo: contentView.leadingAnchor).isActive = true label.trailingAnchor.constraint(equalTo: contentView.trailingAnchor).isActive = true label.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } }

# 2 Solución para iOS 12

Con Swift 5 y iOS 12, puede subclasificar UICollectionViewFlowLayout y establecer su propiedad estimatedItemSize UICollectionViewFlowLayoutAutomaticSize en UICollectionViewFlowLayoutAutomaticSize (esto le indica al sistema que desea lidiar con la autorización de UICollectionViewCell s). Luego deberá anular layoutAttributesForElements(in:) y layoutAttributesForItem(at:) para establecer el ancho de las celdas. Por último, deberá anular el método preferredLayoutAttributesFitting(_:) sus celdas preferredLayoutAttributesFitting(_:) y calcular su altura de ajuste comprimido.

El siguiente código completo muestra cómo mostrar UILabel multilínea dentro de UILabel de ancho UIcollectionViewCell (restringido por el área segura de UICollectionViewFlowLayout y las inserciones de UICollectionViewFlowLayout ):

CollectionViewController.swift

import UIKit class CollectionViewController: UICollectionViewController { let items = [ "Lorem ipsum.", "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt.", "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.", "Lorem ipsum dolor sit amet.", "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam." ] let columnLayout = FlowLayout() override func viewDidLoad() { super.viewDidLoad() collectionView.alwaysBounceVertical = true collectionView.collectionViewLayout = columnLayout collectionView.contentInsetAdjustmentBehavior = .always collectionView.register(Cell.self, forCellWithReuseIdentifier: "Cell") } override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return items.count } override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! Cell cell.label.text = items[indexPath.row] return cell } }

FlowLayout.swift

import UIKit class FlowLayout: UICollectionViewFlowLayout { override init() { super.init() self.minimumInteritemSpacing = 10 self.minimumLineSpacing = 10 self.sectionInset = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10) estimatedItemSize = UICollectionViewFlowLayout.automaticSize } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? { guard let layoutAttributes = super.layoutAttributesForItem(at: indexPath) else { return nil } guard let collectionView = collectionView else { return nil } layoutAttributes.bounds.size.width = collectionView.safeAreaLayoutGuide.layoutFrame.width - sectionInset.left - sectionInset.right return layoutAttributes } override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { guard let superLayoutAttributes = super.layoutAttributesForElements(in: rect) else { return nil } guard scrollDirection == .vertical else { return superLayoutAttributes } let computedAttributes = superLayoutAttributes.compactMap { layoutAttribute in return layoutAttribute.representedElementCategory == .cell ? layoutAttributesForItem(at: layoutAttribute.indexPath) : layoutAttribute } return computedAttributes } override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool { return true } }

Cell.swift

import UIKit class Cell: UICollectionViewCell { let label = UILabel() override init(frame: CGRect) { super.init(frame: frame) label.numberOfLines = 0 backgroundColor = .orange contentView.addSubview(label) label.translatesAutoresizingMaskIntoConstraints = false label.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true label.leadingAnchor.constraint(equalTo: contentView.leadingAnchor).isActive = true label.trailingAnchor.constraint(equalTo: contentView.trailingAnchor).isActive = true label.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes { layoutIfNeeded() let layoutAttributes = super.preferredLayoutAttributesFitting(layoutAttributes) layoutAttributes.bounds.size = systemLayoutSizeFitting(UIView.layoutFittingCompressedSize, withHorizontalFittingPriority: .required, verticalFittingPriority: .defaultLow) return layoutAttributes } }

Aquí hay algunas implementaciones alternativas para preferredLayoutAttributesFitting(_:) :

override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes { layoutIfNeeded() label.preferredMaxLayoutWidth = label.bounds.size.width layoutAttributes.bounds.size.height = systemLayoutSizeFitting(UIView.layoutFittingCompressedSize).height return layoutAttributes }

override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes { label.preferredMaxLayoutWidth = layoutAttributes.size.width - contentView.layoutMargins.left - contentView.layoutMargins.left layoutAttributes.bounds.size.height = systemLayoutSizeFitting(UIView.layoutFittingCompressedSize).height return layoutAttributes }

Visualización esperada:

Aquí hay una pregunta bastante básica sobre el desarrollo actual de iOS, para la cual nunca he encontrado una buena respuesta.

En un (digamos) UICollectionView vertical,

¿Es posible tener celdas de ancho completo , pero, permitir que la altura dinámica sea controlada por autolayout?

(Si eres nuevo en iOS "altura dinámica", lo que significa que la celda tiene algunas, digamos, vistas de texto que podrían tener cualquier altura o imágenes que podrían tener diferentes alturas, por lo que, en última instancia, cada celda tiene una altura totalmente diferente).

¿Si es así, cómo?

Esto me parece quizás la "pregunta más importante en iOS sin una respuesta realmente buena".

Esto es seguramente

El problema más difícil en todo iOS :(

A mediados de 2019 ... agregué otra recompensa: todavía no hay una solución realmente buena para este problema ultrabásico. WTH, Apple? "Hacer que los elementos de vista de colección sean de ancho completo"


  1. Establezca el tamaño estimatedItemSize del artículo de su diseño de flujo:

    collectionViewLayout.estimatedItemSize = UICollectionViewFlowLayoutAutomaticSize

  2. Defina una restricción de ancho en la celda y configúrela para que sea igual al ancho de la supervista:

    class CollectionViewCell: UICollectionViewCell { private var widthConstraint: NSLayoutConstraint? ... override init(frame: CGRect) { ... // Create width constraint to set it later. widthConstraint = contentView.widthAnchor.constraint(equalToConstant: 0) } override func updateConstraints() { // Set width constraint to superview''s width. widthConstraint?.constant = superview?.bounds.width ?? 0 widthConstraint?.isActive = true super.updateConstraints() } ... }

Ejemplo completo

Probado en iOS 11.


Debe heredar la clase UICollectionViewDelegateFlowLayout en su collectionViewController. Luego agregue la función:

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { return CGSize(width: view.frame.width, height: 100) }

Con eso, tiene el ancho del ancho de la pantalla.

Y ahora tiene un collectionViewController con filas como tableViewController.

Si desea que el tamaño de la altura de cada celda sea dinámico, quizás debería crear celdas personalizadas para cada celda que necesite.


Debe agregar restricción de ancho a CollectionViewCell

class SelfSizingCell: UICollectionViewCell { override func awakeFromNib() { super.awakeFromNib() contentView.translatesAutoresizingMaskIntoConstraints = false contentView.widthAnchor.constraint(equalToConstant: UIScreen.main.bounds.width).isActive = true } }


Desde iOS 10, tenemos una nueva API en el diseño de flujo para hacer eso.

Todo lo que tiene que hacer es establecer su flowLayout.estimatedItemSize en una nueva constante, UICollectionViewFlowLayoutAutomaticSize .

Source


En su viewDidLayoutSubviews , establezca el estimatedItemSize en ancho completo (el diseño se refiere al objeto UICollectionViewFlowLayout):

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize { return CGSize(width: collectionView.bounds.size.width, height: 120) }

En su celda, asegúrese de que sus restricciones toquen la parte superior e inferior de la celda (el siguiente código usa Cartografía para simplificar la configuración de las restricciones, pero puede hacerlo con NSLayoutConstraint o IB si lo desea):

constrain(self, nameLabel, valueLabel) { view, name, value in name.top == view.top + 10 name.left == view.left name.bottom == view.bottom - 10 value.right == view.right value.centerY == view.centerY }

¡Voila, tus células ahora crecerán automáticamente en altura!


Encontré una solución bastante fácil para ese problema: dentro de mi CollectionViewCell obtuve un UIView () que en realidad es solo un fondo. Para obtener el ancho completo, solo configuro los siguientes anclajes

bgView.widthAnchor.constraint(equalToConstant: UIScreen.main.bounds.size.width - 30).isActive = true // 30 is my added up left and right Inset bgView.topAnchor.constraint(equalTo: topAnchor).isActive = true bgView.leftAnchor.constraint(equalTo: leftAnchor).isActive = true bgView.rightAnchor.constraint(equalTo: rightAnchor).isActive = true bgView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true

La "magia" ocurre en la primera línea. Establezco el ancho de anclaje dinámicamente al ancho de la pantalla. También es importante restar las inserciones de su CollectionView. De lo contrario, la celda no aparecerá. Si no desea tener una vista de fondo de este tipo, simplemente hágalo invisible.

FlowLayout utiliza la siguiente configuración

layout.itemSize = UICollectionViewFlowLayoutAutomaticSize layout.estimatedItemSize = UICollectionViewFlowLayoutAutomaticSize

El resultado es una celda de ancho completo con altura dinámica.


Hay dos formas de abordar este problema.

Una forma es darle al diseño de flujo de vista de colección un tamaño estimado y calcular el tamaño de celda.

Nota: Como se menciona en los comentarios a continuación, a partir de iOS 10 ya no es necesario proporcionar un tamaño estimado para activar la llamada a un func preferredLayoutAttributesFitting(_ layoutAttributes:) celdas func preferredLayoutAttributesFitting(_ layoutAttributes:) . Anteriormente (iOS 9) requeriría que proporcionara un tamaño estimado si desea consultar las celdas preferidas de LayoutAttributes.

(suponiendo que está utilizando guiones gráficos y la vista de colección está conectada a través de IB)

override func viewDidLoad() { super.viewDidLoad() let layout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout layout?.estimatedItemSize = CGSize(width: 375, height: 200) // your average cell size }

Para celdas simples que generalmente serán suficientes. Si el tamaño sigue siendo incorrecto, en la celda de la vista de colección puede anular func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes , que le dará más control de grano fino sobre el tamaño de la celda. Nota: aún deberá darle al diseño del flujo un tamaño estimado .

Luego, anule func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes para devolver el tamaño correcto.

override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes { let autoLayoutAttributes = super.preferredLayoutAttributesFitting(layoutAttributes) let targetSize = CGSize(width: layoutAttributes.frame.width, height: 0) let autoLayoutSize = contentView.systemLayoutSizeFitting(targetSize, withHorizontalFittingPriority: UILayoutPriorityRequired, verticalFittingPriority: UILayoutPriorityDefaultLow) let autoLayoutFrame = CGRect(origin: autoLayoutAttributes.frame.origin, size: autoLayoutSize) autoLayoutAttributes.frame = autoLayoutFrame return autoLayoutAttributes }

Alternativamente, en su lugar, puede usar una celda de dimensionamiento para calcular el tamaño de la celda en UICollectionViewDelegateFlowLayout .

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { let width = collectionView.frame.width let size = CGSize(width: width, height: 0) // assuming your collection view cell is a nib // you may also instantiate a instance of our cell from a storyboard let sizingCell = UINib(nibName: "yourNibName", bundle: nil).instantiate(withOwner: nil, options: nil).first as! YourCollectionViewCell sizingCell.autoresizingMask = [.flexibleWidth, .flexibleHeight] sizingCell.frame.size = size sizingCell.configure(with: object[indexPath.row]) // what ever method configures your cell return sizingCell.contentView.systemLayoutSizeFitting(size, withHorizontalFittingPriority: UILayoutPriorityRequired, verticalFittingPriority: UILayoutPriorityDefaultLow) }

Si bien estos no son ejemplos perfectos para la producción, deberían ayudarlo a comenzar en la dirección correcta. No puedo decir que esta sea la mejor práctica, pero esto funciona para mí, incluso con celdas bastante complejas que contienen múltiples etiquetas, que pueden o no ajustarse a varias líneas.


Ninguna de las soluciones funcionaba para mí, ya que necesito un ancho dinámico para adaptar el ancho del iPhone.

class CustomLayoutFlow: UICollectionViewFlowLayout { override init() { super.init() minimumInteritemSpacing = 1 ; minimumLineSpacing = 1 ; scrollDirection = .horizontal } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) minimumInteritemSpacing = 1 ; minimumLineSpacing = 1 ; scrollDirection = .horizontal } override var itemSize: CGSize { set { } get { let width = (self.collectionView?.frame.width)! let height = (self.collectionView?.frame.height)! return CGSize(width: width, height: height) } } } class TextCollectionViewCell: UICollectionViewCell { @IBOutlet weak var textView: UITextView! override func prepareForReuse() { super.prepareForReuse() } } class IntroViewController: UIViewController, UITextViewDelegate, UICollectionViewDataSource, UICollectionViewDelegate, UINavigationControllerDelegate { @IBOutlet weak var collectionViewTopDistanceConstraint: NSLayoutConstraint! @IBOutlet weak var collectionViewTopDistanceConstraint: NSLayoutConstraint! @IBOutlet weak var collectionView: UICollectionView! var collectionViewLayout: CustomLayoutFlow! override func viewDidLoad() { super.viewDidLoad() self.collectionViewLayout = CustomLayoutFlow() self.collectionView.collectionViewLayout = self.collectionViewLayout } override func viewWillLayoutSubviews() { self.collectionViewTopDistanceConstraint.constant = UIScreen.main.bounds.height > 736 ? 94 : 70 self.view.layoutIfNeeded() } }


No estoy seguro si esto califica como una "respuesta realmente buena", pero es lo que estoy usando para lograr esto. Mi diseño de flujo es horizontal, y estoy tratando de ajustar el ancho con la distribución automática, por lo que es similar a su situación.

extension PhotoAlbumVC: UICollectionViewDelegateFlowLayout { func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { // My height is static, but it could use the screen size if you wanted return CGSize(width: collectionView.frame.width - sectionInsets.left - sectionInsets.right, height: 60) } }

Luego, en el controlador de vista donde se modifica la restricción de autolayout, enciendo una NSNotification.

NotificationCenter.default.post(name: NSNotification.Name("constraintMoved"), object: self, userInfo: nil)

En mi subclase UICollectionView, escucho esa notificación:

// viewDidLoad NotificationCenter.default.addObserver(self, selector: #selector(handleConstraintNotification(notification:)), name: NSNotification.Name("constraintMoved"), object: nil)

e invalidar el diseño:

func handleConstraintNotification(notification: Notification) { self.collectionView?.collectionViewLayout.invalidateLayout() }

Esto hace que se sizeForItemAt a sizeForItemAt a sizeForItemAt utilizando el nuevo tamaño de la vista de colección. En su caso, debería poder actualizarse dadas las nuevas restricciones disponibles en el diseño.


Personalmente, encontré que las mejores formas de tener una UICollectionView donde AutoLayout determina el tamaño mientras que cada celda puede tener un tamaño diferente es implementar la función UICollectionViewDelegateFlowLayout sizeForItemAtIndexPath mientras uso una celda real para medir el tamaño.

Hablé sobre esto en una de mis publicaciones de blog.

Esperemos que este te ayude a lograr lo que quieres. No estoy 100% seguro, pero creo que, a diferencia de UITableView, en el que puede tener una altura de celdas totalmente automática mediante el uso de la disyunción AutoLayout con

tableView.rowHeight = UITableViewAutomaticDimension tableView.estimatedRowHeight = 44

UICollectionView no tiene tal forma de permitir que AutoLayout determine el tamaño porque UICollectionViewCell no necesariamente llena todo el ancho de la pantalla.

Pero aquí hay una pregunta para usted : si necesita celdas de ancho de pantalla completa, ¿por qué se molesta en usar UICollectionView sobre un viejo UITableView que viene con las celdas de tamaño automático?


Según mi comentario sobre la respuesta de Eric, mi solución es muy similar a la suya, pero tuve que agregar una restricción en preferredSizeFor ... para restringir a la dimensión fija.

override func systemLayoutSizeFitting( _ targetSize: CGSize, withHorizontalFittingPriority horizontalFittingPriority: UILayoutPriority, verticalFittingPriority: UILayoutPriority) -> CGSize { width.constant = targetSize.width let size = contentView.systemLayoutSizeFitting( CGSize(width: targetSize.width, height: 1), withHorizontalFittingPriority: .required, verticalFittingPriority: verticalFittingPriority) print("/(#function) /(#line) /(targetSize) -> /(size)") return size }

Esta pregunta tiene varios duplicados, la respondí en detalle aquí y proporcioné una aplicación de muestra que funciona aquí.


¡¡¡TRABAJANDO!!! Probado en iOS: 12.1 Swift 4.1

Tengo una solución muy simple que simplemente funciona sin romper restricciones.

My ViewControllerClass

class ViewController: UIViewController { @IBOutlet weak var collectionView: UICollectionView! let cellId = "CustomCell" var source = ["nomu", "when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. ", "t is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using ''Content here, content here'', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for ''lorem ipsum'' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by", "Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia,","nomu", "when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. ", "t is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using ''Content here, content here'', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for ''lorem ipsum'' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by", "Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia,","nomu", "when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. ", "t is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using ''Content here, content here'', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for ''lorem ipsum'' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by", "Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia,"] override func viewDidLoad() { super.viewDidLoad() self.collectionView.delegate = self self.collectionView.dataSource = self self.collectionView.register(UINib.init(nibName: cellId, bundle: nil), forCellWithReuseIdentifier: cellId) if let flowLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout { flowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize } } } extension ViewController: UICollectionViewDelegate, UICollectionViewDataSource { func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return self.source.count } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as? CustomCell else { return UICollectionViewCell() } cell.setData(data: source[indexPath.item]) return cell } }

Clase CustomCell:

class CustomCell: UICollectionViewCell { @IBOutlet weak var label: UILabel! @IBOutlet weak var widthConstraint: NSLayoutConstraint! override func awakeFromNib() { super.awakeFromNib() self.widthConstraint.constant = UIScreen.main.bounds.width } func setData(data: String) { self.label.text = data } override func systemLayoutSizeFitting(_ targetSize: CGSize, withHorizontalFittingPriority horizontalFittingPriority: UILayoutPriority, verticalFittingPriority: UILayoutPriority) -> CGSize { return contentView.systemLayoutSizeFitting(CGSize(width: self.bounds.size.width, height: 1)) } }

Ingrediente principal es la systemLayoutSizeFitting función en Customcell. Y también tenemos que establecer el ancho de la vista dentro de la celda con restricciones.


Problema

Está buscando una altura automática y también desea tener un ancho completo, no es posible obtener ambos al usar UICollectionViewFlowLayoutAutomaticSize .

Desea hacerlo utilizando UICollectionView así que a continuación es la solución para usted.

Solución

Paso I : Calcule la altura esperada de la celda

1. Si solo tiene UILabel en CollectionViewCell que establece el número de numberOfLines=0 y que calculó la altura esperada de UIlable , pase los tres parámetros

func heightForLable(text:String, font:UIFont, width:CGFloat) -> CGFloat { // pass string, font, LableWidth let label:UILabel = UILabel(frame: CGRect(x: 0, y: 0, width: width, height: CGFloat.greatestFiniteMagnitude)) label.numberOfLines = 0 label.lineBreakMode = NSLineBreakMode.byWordWrapping label.font = font label.text = text label.sizeToFit() return label.frame.height }

2. Si su CollectionViewCell contiene solo UIImageView y se supone que tiene una altura dinámica, entonces necesita obtener la altura de UIImage (su UIImageView debe tener restricciones de AspectRatio )

// this will give you the height of your Image let heightInPoints = image.size.height let heightInPixels = heightInPoints * image.scale

3. Si contiene ambos, calcule su altura y agréguelos.

PASO II : devuelva el tamaño de CollectionViewCell

1. Agregue el delegado UICollectionViewDelegateFlowLayout en su viewController

2. Implemente el método de delegado

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { // This is just for example, for the scenario Step-I -> 1 let yourWidthOfLable=self.view.size.width let font = UIFont(name: "Helvetica", size: 20.0) var expectedHeight = heightForLable(array[indePath.row], font: font, width:yourWidthOfLable) return CGSize(width: view.frame.width, height: expectedHeight) }

Espero que esto lo pueda ayudar.