¿Cómo creo el encabezado UITableView cuya altura está determinada por la altura de su label?

Me gustaría agregar un encabezado a mi tableView. Este encabezado contiene 1 UILabel. La altura del encabezado debe calcularse en function del número de líneas que tiene la label.

En mi código, agrego restricciones con todos los bordes de la label <> encabezado. Este es mi bash:

//Add header to tableView header = UIView() header.backgroundColor = UIColor.yellowColor() tableView!.tableHeaderView = header //Create Label and add it to the header postBody = UILabel() postBody.text = "The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog." postBody.font = UIFont(name: "Lato-Regular", size: 16.0) postBody.numberOfLines = 0 postBody.backgroundColor = FlatLime() header.addSubview(postBody) //Enable constraints for each item postBody.translatesAutoresizingMaskIntoConstraints = false header.translatesAutoresizingMaskIntoConstraints = false //Add constraints to the header and post body let postBodyLeadingConstraint = NSLayoutConstraint(item: postBody, attribute: NSLayoutAttribute.Leading, relatedBy: NSLayoutRelation.Equal, toItem: header, attribute: NSLayoutAttribute.Leading, multiplier: 1, constant: 0) postBodyLeadingConstraint.active = true let postBodyTrailingConstraint = NSLayoutConstraint(item: postBody, attribute: NSLayoutAttribute.Trailing, relatedBy: NSLayoutRelation.Equal, toItem: header, attribute: NSLayoutAttribute.Trailing, multiplier: 1, constant: 0) postBodyTrailingConstraint.active = true let postBodyTopConstraint = NSLayoutConstraint(item: postBody, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: header, attribute: NSLayoutAttribute.Top, multiplier: 1, constant: 0) postBodyTopConstraint.active = true let postBodyBottomConstraint = NSLayoutConstraint(item: postBody, attribute: NSLayoutAttribute.Bottom, relatedBy: NSLayoutRelation.Equal, toItem: header, attribute: NSLayoutAttribute.Bottom, multiplier: 1, constant: 0) postBodyBottomConstraint.active = true //Calculate header size let size = header.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize) var frame = header.frame frame.size.height = size.height header.frame = frame tableView!.tableHeaderView = header header.layoutIfNeeded() 

Esta es mi table:

  let nib = UINib(nibName: "MessagesTableViewCell", bundle: nil) let nibSimple = UINib(nibName: "SimpleMessagesTableViewCell", bundle: nil) self.tableView!.registerNib(nib, forCellReuseIdentifier: "MessagesTableViewCell") self.tableView!.registerNib(nibSimple, forCellReuseIdentifier: "SimpleMessagesTableViewCell") self.tableView!.dataSource = self self.tableView!.delegate = self self.tableView!.rowHeight = UITableViewAutomaticDimension self.tableView!.estimatedRowHeight = 100.0 self.tableView!.separatorStyle = UITableViewCellSeparatorStyle.None self.tableView!.separatorColor = UIColor(hex: 0xf5f5f5) self.tableView!.separatorInset = UIEdgeInsetsMake(0, 0, 0, 0) self.tableView!.clipsToBounds = true self.tableView!.allowsSelection = false self.tableView!.allowsMultipleSelection = false self.tableView!.keyboardDismissMode = .OnDrag 

Como puede ver, el encabezado no tiene en count la altura de la label (que hice numberOfLines = 0)

Encabezado no auto-altura

UILabel s aprovecha el UIView intrinsicContentSize() de UIView para indicar el layout automático del tamaño que deberían ser. Para una label multilínea, sin embargo, el tamaño de contenido intrínseco es ambiguo; la table no sabe si debe ser corta y ancha, alta y estrecha, o cualquier otra cosa.

Para combatir esto, UILabel tiene una propiedad llamada prefernetworkingMaxLayoutWidth . Al establecer esto, se indica a una label multilínea que debería ser como máximo tan ancha, y permite que intrinsicContentSize() descifre y devuelva una altura adecuada para que coincida. Al no configurar el ejemplo prefernetworkingMaxLayoutWidth de MaxLayoutWidth, la label deja su ancho sin límites y, por lo tanto, calcula la altura de una sola línea de text larga.

La única complicación con prefernetworkingMaxLayoutWidth es que normalmente no sabes qué ancho quieres que sea la label hasta que el layout automático haya calculado uno para ti. Por esa razón, el lugar para configurarlo en una subclass de controller de vista (del que parece que su ejemplo de código podría ser) está en viewDidLayoutSubviews :

 override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() postBody.prefernetworkingMaxLayoutWidth = CGRectGetWidth(postBody.frame) // then update the table header view if let header = tableView?.tableHeaderView { header.frame.size.height = header.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height tableView?.tableHeaderView = header } } 

Obviamente, necesitará agregar una propiedad para la label postBody para que esto funcione.

Avísame si no estás en una subclass UIViewController aquí y UIViewController mi respuesta.

Implementación usando el guión gráfico

  1. En UItableView agregue UITableViewCell nueva UIView y UIView UILabel Conecta a través de Autolayout
  2. En UILabel ponga el número de líneas en 0.
  3. En ViewDidLoad su UILabel llama a un método sizeToFit() y especifica un tamaño para UIView , y ese será su encabezado headerView.frame.size.height = headerLabel.frame.size.height

Código

  @IBOutlet weak var tableView: UITableView! @IBOutlet weak var headerView: UIView! @IBOutlet weak var headerLabel: UILabel! override func viewDidLoad() { super.viewDidLoad() headerLabel.text = "tableViewdidReceiveMemoryWarningdidReceiveMemoryWarningdidReceiveMemoryWarningdidReceiveMemoryWarningdidReceiveMemoryWarningdidReceiveMemoryWarningdidReceiveMemoryWarningdidReceiveMemoryWarningdidReceiveMemoryWarning" headerLabel.sizeToFit() headerView.frame.size.height = headerLabel.frame.size.height } 

Captura de pantalla

introduzca la descripción de la imagen aquí

TestProject

probar el enlace del proyecto

El primer problema que tenemos es que no se puede cambiar el tamaño del encabezado mediante autolayout, para más detalles, consulte ¿Es posible usar AutoLayout con tablaHeaderView de UITableView?

Por lo tanto, tenemos que calcular manualmente la altura del encabezado, por ejemplo:

 @IBOutlet var table: UITableView! var header: UIView? var postBody: UILabel? override func viewDidLoad() { super.viewDidLoad() let header = UIView() // don't forget to set this header.translatesAutoresizingMaskIntoConstraints = true header.backgroundColor = UIColor.yellowColor() let postBody = UILabel() postBody.translatesAutoresizingMaskIntoConstraints = false postBody.text = "The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog." postBody.font = UIFont.systemFontOfSize(16.0) // don't forget to set this postBody.lineBreakMode = .ByWordWrapping postBody.numberOfLines = 0 header.addSubview(postBody) let leadingConstraint = NSLayoutConstraint(item: postBody, attribute: NSLayoutAttribute.Leading, relatedBy: NSLayoutRelation.Equal, toItem: header, attribute: NSLayoutAttribute.Leading, multiplier: 1, constant: 0) let trailingConstraint = NSLayoutConstraint(item: postBody, attribute: NSLayoutAttribute.Trailing, relatedBy: NSLayoutRelation.Equal, toItem: header, attribute: NSLayoutAttribute.Trailing, multiplier: 1, constant: 0) let topConstraint = NSLayoutConstraint(item: postBody, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: header, attribute: NSLayoutAttribute.Top, multiplier: 1, constant: 0) let bottomConstraint = NSLayoutConstraint(item: postBody, attribute: NSLayoutAttribute.Bottom, relatedBy: NSLayoutRelation.Equal, toItem: header, attribute: NSLayoutAttribute.Bottom, multiplier: 1, constant: 0) header.addConstraints([leadingConstraint, trailingConstraint, topConstraint, bottomConstraint]) self.table.tableHeaderView = header self.header = header self.postBody = postBody } override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() let text = postBody!.attributedText! let height = text.boundingRectWithSize( CGSizeMake(table.bounds.size.width, CGFloat.max), options: [.UsesLineFragmentOrigin], context: nil ).height header!.frame.size.height = height } 

También es posible que desee utilizar el código en la respuesta de stefandouganhyde . Realmente no importa cómo calcule la altura. El punto es que la reproducción automática no funcionará automáticamente para tableHeaderView .

Resultado:

tabla resultante

Utilizamos NSLayoutManager para estimar rápidamente la altura de los artículos que necesitan cambiar el tamaño según el text. Esta es la idea básica:

 override class func estimatedHeightForItem(text: String, atWidth width: CGFloat) -> CGFloat { let storage = NSTextStorage(string: text!) let container = NSTextContainer(size: CGSize(width: width, height: CGFloat.max)) let layoutManager = NSLayoutManager() layoutManager.addTextContainer(container) storage.addLayoutManager(layoutManager) storage.addAttribute(NSFontAttributeName, value: UIFont.Body, range: NSRange(location: 0, length: storage.length)) container.lineFragmentPadding = 0.0 return layoutManager.usedRectForTextContainer(container).size.height } 

La respuesta de Beslan probablemente sea mejor para su caso de uso, pero me parece agradable tener más control sobre cómo se maneja el layout.

// puede ser que te ayudará.

 header = UIView(frame: CGRectMake(tableview.frame.origin.x,tableview.frame.origin.y, tableview.frame.size.width, 40)) header.backgroundColor = UIColor.yellowColor() //Create Label and add it to the header postBody = UILabel(frame: header.frame) postBody.text = "The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog." postBody.font = UIFont(name: "Lato-Regular", size: 16.0) postBody.numberOfLines = 0 postBody.backgroundColor = FlatLime() header.addSubview(postBody) let maximumLabelSize: CGSize = CGSizeMake(postBody.size.width, CGFloat.max); let options: NSStringDrawingOptions = NSStringDrawingOptions.UsesLineFragmentOrigin let context: NSStringDrawingContext = NSStringDrawingContext() context.minimumScaleFactor = 0.8 let attr: Dictionary = [NSFontAttributeName: postBody.font!] var size: CGSize? = postBody.text?.boundingRectWithSize(maximumLabelSize, options:options, attributes: attr, context: context).size let frame = header.frame frame.size.height = size?.height header.frame = frame postBody.frame = frame tableView!.tableHeaderView = header 

puede calcular la altura de una label utilizando su cadena

 let labelWidth = label.frame.width let maxLabelSize = CGSize(width: labelWidth, height: CGFloat.max) let actualLabelSize = label.text!.boundingRectWithSize(maxLabelSize, options: [.UsesLineFragmentOrigin], attributes: [NSFontAttributeName: label.font], context: nil) let labelHeight = actualLabelSize.height