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"
-
Establezca el tamaño
estimatedItemSize
del artículo de su diseño de flujo:collectionViewLayout.estimatedItemSize = UICollectionViewFlowLayoutAutomaticSize
-
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() } ... }
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
.
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.