He leído un montón de puestos en la adición de cabecera para UICollectionView. En iOS 7+ app en Swift, estoy tratando de agregar un encabezado con un UILabel en cuya altura debe ajustar basado en la altura de UILabel. El UILabel tiene líneas = 0.

He configurado el encabezado del IB con Autodiseño

Dinámica UICollectionView el tamaño de la cabecera basado en UILabel

El ViewController implementa UICollectionViewDelegate, UICollectionViewDataSource. Yo no tenía una clase personalizada para el encabezado, pero estoy usando estas dos funciones:

func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
      //description is a String variable defined in the class
    let size:CGSize = (description as NSString).boundingRectWithSize(CGSizeMake(CGRectGetWidth(collectionView.bounds) - 20.0, 180.0), options: NSStringDrawingOptions.UsesLineFragmentOrigin, attributes: [NSFontAttributeName: UIFont(name: "Helvetica Neue", size: 16.0)], context: nil).size
    return CGSizeMake(CGRectGetWidth(collectionView.bounds), ceil(size.height))
}

func collectionView(collectionView: UICollectionView!, viewForSupplementaryElementOfKind kind: String!, atIndexPath indexPath: NSIndexPath!) -> UICollectionReusableView! {
    var reusableview:UICollectionReusableView = UICollectionReusableView()
    if (kind == UICollectionElementKindSectionHeader) {
                    //listCollectionView is an @IBOutlet UICollectionView defined at class level, using collectionView crashes
            reusableview = listCollectionView.dequeueReusableSupplementaryViewOfKind(UICollectionElementKindSectionHeader, withReuseIdentifier: "ListHeader", forIndexPath: indexPath) as UICollectionReusableView
            let label = reusableview.viewWithTag(200) as UILabel  //the UILabel within the header is tagged with 200
            label.text = description   //description is a String variable defined in the class
        }
    }
    return reusableview
}

La visualización del texto parece estar funcionando, pero el cálculo de altura no parece estar funcionando (ver imagen abajo). También, creo que no puedo acceder a la UILabel a través de la collectionView...referenceSizeForHeaderInSection función. Alguna sugerencia sobre cómo calcular CGSize correctamente?

Dinámica UICollectionView el tamaño de la cabecera basado en UILabel

  • Yo no era capaz de realmente resolver el problema. Pero encontré una solución que participan UX cambios de flujo de trabajo. I definir un encabezado fijo altura y tamaño fijo para el título. Si el título es más, me truncar el texto, pero permite que el usuario hace clic en un referente de la vista que muestra el texto completo. Con iOS, siempre me parece que es difícil de poner en práctica soluciones a problemas sencillos.
  • Supongo que tiene que hacer la misma cosa, probablemente con algo así como un «Leer más…» botón que se encuentra en algún lugar de la interfaz de usuario …
InformationsquelleAutor Dean | 2014-09-03

7 Comentarios

  1. 25

    Esta es la forma en que lo hice:

    let labels = [
    "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc ac lorem enim. Curabitur rhoncus efficitur quam, et pretium ipsum. Nam eu magna at velit sollicitudin fringilla nec nec nisi. Quisque nec enim et ipsum feugiat pretium. Vestibulum hendrerit arcu ut ipsum gravida, ut tincidunt justo pellentesque. Etiam lacus ligula, aliquet at lorem vel, ullamcorper commodo turpis. Nullam commodo sollicitudin mauris eu faucibus.",
    "Lorem ipsum dolor",
    "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc ac lorem enim. Curabitur rhoncus efficitur quam, et pretium ipsum. Nam eu magna at velit sollicitudin fringilla nec nec nisi. Quisque nec enim et ipsum feugiat pretium."]

    La idea básica es crear una idéntica UILabel a la que se muestra en el encabezado de la sección. Esa etiqueta se utiliza para establecer el tamaño deseado para el encabezado de la referenceSizeForHeaderInSection método.

    Tengo una etiqueta de salida llamada label en mi UICollectionReusableView subclase (MyHeaderCollectionReusableView), el que yo uso para mi sección de encabezado de la vista mediante la asignación en el guión gráfico (configuración de «MyHeader» como la Reutilización Identificador de la vista de sección). Que la mencionada etiqueta tiene el espacio horizontal y vertical restricciones a la sección de encabezado de las fronteras para el diseño automático correctamente.

    override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
            return 3
        }
    
    override func collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView {
    
            let headerView =
            collectionView.dequeueReusableSupplementaryViewOfKind(kind,
                withReuseIdentifier: "MyHeader",
                forIndexPath: indexPath)
                as MyHeaderCollectionReusableView
    
            headerView.label.text = labels[indexPath.section]
    
            return headerView
    
        }
    
    func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
            //that -16 is because I have 8px for left and right spacing constraints for the label.
            let label:UILabel = UILabel(frame: CGRectMake(0, 0, collectionView.frame.width - 16, CGFloat.max))
            label.numberOfLines = 0
            label.lineBreakMode = NSLineBreakMode.ByWordWrapping
           //here, be sure you set the font type and size that matches the one set in the storyboard label
            label.font = UIFont(name: "Helvetica", size: 17.0)
            label.text = labels[section]
            label.sizeToFit()
    
    //Set some extra pixels for height due to the margins of the header section.  
    //This value should be the sum of the vertical spacing you set in the autolayout constraints for the label. + 16 worked for me as I have 8px for top and bottom constraints.
            return CGSize(width: collectionView.frame.width, height: label.frame.height + 16)
        }
    • Es esta realmente la solución más limpia? por qué apple.. ¿por qué?
    • ¿Por qué no carga la punta y el cálculo de la altura de las obras 🙁
    • Impresionante 👏🏽👏🏽👏🏽
    • solución inteligente.
    • Legado de cómo eran las cosas antes de auto-presentación. Diiiirty De Apple
    • He añadido una alternativa de respuesta, donde en lugar de volver a crear el UILabel manualmente, se cola el uno de viewForSupplementaryElementOfKind:at:.

  2. 5

    Como el interrogador, yo tenía un UICollectionView que contiene un encabezado con una sola etiqueta, cuya altura quería variar un poco. He creado una extensión para UILabel para medir la altura de una de varias líneas de la etiqueta con un conocido ancho:

    public extension UILabel {
        public class func size(withText text: String, forWidth width: CGFloat) -> CGSize {
            let measurementLabel = UILabel()
            measurementLabel.text = text
            measurementLabel.numberOfLines = 0
            measurementLabel.lineBreakMode = .byWordWrapping
            measurementLabel.translatesAutoresizingMaskIntoConstraints = false
            measurementLabel.widthAnchor.constraint(equalToConstant: width).isActive = true
            let size = measurementLabel.systemLayoutSizeFitting(UILayoutFittingCompressedSize)
            return size
        }
    }

    Nota: el de arriba es en Swift 3 sintaxis.

    A continuación, implementar el tamaño del encabezado del método de UICollectionViewDelegateFlowLayout como:

    extension MyCollectionViewController : UICollectionViewDelegateFlowLayout {
        func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
            let text = textForHeader(inSection: section)
            var size =  UILabel.size(withAttributedText: text, forWidth: collectionView.frame.size.width)
            size.height = size.height + 16
            return size
        }
    }

    El trabajo de calcular el tamaño de la cabecera es delegada a la anterior UILabel extensión. El +16 es un experimentalmente derivados de la compensación fija (8 + 8), que se basa en márgenes y podría ser obtenida mediante programación.

    Todo lo que se necesita en el encabezado de devolución de llamada es sólo para poner el texto:

    override func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
        if kind == UICollectionElementKindSectionHeader, let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: headerIdentifier, for: indexPath) as?  MyCollectionHeader {
            let text = textForHeader(inSection: section)
            headerView.label.text = text
            return headerView
        }
        return UICollectionReusableView()
    }
  3. 3

    La idea es tener una plantilla de encabezado de instancia en la memoria para calcular la altura deseada antes de crear el resultado de encabezado de la vista. Usted debe mover su sección de encabezado de la vista a otro .archivo nib, el programa de instalación de todos los autodiseño limitaciones y crear una instancia de la plantilla en el método viewDidLoad como este:

    class MyViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
    
      @IBOutlet var collectionView : UICollectionView?
      private var _templateHeader : MyHeaderView
    
      override func viewDidLoad() {
        super.viewDidLoad()
    
        let nib = UINib(nibName: "HeaderView", bundle:nil)
        self.collectionView?.registerNib(nib, forCellWithReuseIdentifier: "header_view_id")
    
        _templateHeader = nib.instantiateWithOwner(nil, options:nil)[0] as! MyHeaderView
      }
    
    }

    Entonces usted será capaz de calcular el tamaño de la cabecera (altura en mi ejemplo) en su diseño de flujo de método de delegado:

    func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
    
        _templateHeader.lblTitle.text = "some title here"
        _templateHeader.lblDescription.text = "some long description"
    
        _templateHeader.setNeedsUpdateConstraints();
        _templateHeader.updateConstraintsIfNeeded()
    
        _templateHeader.setNeedsLayout();
        _templateHeader.layoutIfNeeded();
    
        let computedSize = _templateHeader.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)
    
        return CGSizeMake(collectionView.bounds.size.width, computedSize.height);
    }

    Y, a continuación, crear y devolver su encabezado regular la vista como siempre, ya que usted ya ha calculado su tamaño en el diseño de flujo de método de delegado:

    func collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView {
    
        switch kind {
    
        case UICollectionElementKindSectionHeader:
    
            let headerView = collectionView.dequeueReusableSupplementaryViewOfKind(kind, withReuseIdentifier: "header_view_id", forIndexPath: indexPath) as! MyHeaderView
            headerView.lblTitle.text = "some title here"
            headerView.lblDescription.text = "some long description"
    
            headerView.setNeedsUpdateConstraints()
            headerView.updateConstraintsIfNeeded()
    
            headerView.setNeedsLayout()
            headerView.layoutIfNeeded()
    
            return headerView
        default:
            assert(false, "Unexpected kind")
        }
    
    }

    Olvidé de decir acerca de un momento importante – el encabezado de la vista debe tener un autodiseño restricción en su contentView para adaptarse a la vista de colección de ancho (más o menos el deseado márgenes).

  4. 2

    Lugar de volver a crear una nueva etiqueta de código como se muestra en la respuesta múltiple, simplemente podemos utilizar la existente para calcular el ajuste de tamaño.

    Código para su UICollectionViewDelegate:

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
        //We get the actual header view
        let header = self.collectionView(collectionView, viewForSupplementaryElementOfKind: UICollectionView.elementKindSectionHeader, at: IndexPath(row: 0, section: section)) as! MyHeaderView
        //We ask the label what size it takes (eventually accounting for horizontal margins)
        var size = header.myLabel.sizeThatFits(CGSize(width: collectionView.frame.width - horizontalMargins, height: .greatestFiniteMagnitude))
        //We eventually account for vertical margins
        size.height += verticalMargins
        return size
    }

    Funciona para iOS 11+.

  5. 0

    Tuve la suerte de usar de Vladimir método, pero yo tenía que establecer el marco de la plantilla de vista a tener el mismo ancho para mi colección.

        templateHeader.bounds = CGRectMake(templateHeader.bounds.minX, templateHeader.bounds.minY, self.collectionView.bounds.width, templateHeader.bounds.height)

    Además, mi punto de vista tiene varios componentes de tamaño variable, y tener una plantilla de vista parece lo suficientemente robusta como para lidiar con los cambios. Todavía se siente como debería haber una manera más fácil.

  6. 0

    En su celda agregue el siguiente:

    fileprivate static let font = UIFont(name: FontName, size: 16)
    fileprivate static let insets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)

    Ajustar tanto las fuentes y las inserciones accordignly.

    A continuación, agregue lo siguiente a tu celular

       static func textHeight(_ text: String, width: CGFloat) -> CGFloat {
        let constrainedSize = CGSize(width: width - insets.left - insets.right, height: CGFloat.greatestFiniteMagnitude)
        let attributes = [ NSAttributedStringKey.font: font ]
        let options: NSStringDrawingOptions = [.usesFontLeading, .usesLineFragmentOrigin]
        let bounds = (text as NSString).boundingRect(with: constrainedSize, options: options, attributes: attributes as [NSAttributedStringKey : Any], context: nil)
        return ceil(bounds.height) + insets.top + insets.bottom
    }

    Ahora puede utilizar esta función para calcular automático de la altura de

      func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
        let height = ManuallySelfSizingCell.textHeight("Your text", width: view.frame.width)
        return CGSize(width: view.frame.width, height: height + 16)
    }
  7. -1

    Que tienen que implementar el UICollectionViewDelegate método referenceSizeForHeaderInSection.

    Hay que calcular la altura sin usar la etiqueta llamando boundingRectWithSize:options:context: en la cadena con los atributos adecuados.

    • Creo que ya implementado referenceSizeForHeaderInSection, es la primera función de la lista. Yo depurar más y la delimitación Rect no está calculando correctamente. También he intentado añadir el párrafo envoltura palabra atributo, pero que no funcionó.
    • Lo siento, no el formato de código, de forma que es visible. Tal vez debería, a continuación, cambiar el engañoso título de tu pregunta y mantener la etiqueta de ella?
    • Hola @Mundi ¿cuál es la mejor manera de llegar a usted? Gustaría hacer una pregunta rápida.
    • Acabo de hacer la pregunta y después de un enlace aquí.

Dejar respuesta

Please enter your comment!
Please enter your name here