【问题标题】:How do I create UITableView header whose height is determined by the height of its label?如何创建高度由其标签高度确定的 UITableView 标头?
【发布时间】:2016-04-26 06:49:13
【问题描述】:

我想在我的 tableView 中添加一个标题。此标头包含 1 个 UILabel。标题高度应根据标签的行数计算。

在我的代码中,我使用标签 标头的所有边缘添加约束。这是我的尝试:

    //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()

这是我的桌子:

    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

如你所见,表头没有考虑标签的高度(我做了numberOfLines = 0)

【问题讨论】:

  • 你能发布你的表格视图主要方法代码吗?
  • 如果你设置了一个断点,当你把它分配回标题时,框架是什么样子的?
  • 你没有错过 postBody.translatesAutoresizingMaskIntoConstraints = false 吗?
  • 是否需要是header,还是可以在tableView之上约束header view?
  • 另外,您似乎没有将标签的大小调整为特定宽度。这可以推动它的高度。不确定代码中的其他地方是否有它?

标签: ios swift


【解决方案1】:

UILabels 利用UIViewintrinsicContentSize() 告诉自动布局它们应该是什么大小。然而,对于多行标签,内在内容大小是不明确的;这张桌子不知道它应该是又短又宽、又高又窄,还是介于两者之间。

为了解决这个问题,UILabel 有一个名为 preferredMaxLayoutWidth 的属性。设置此项会告诉多行标签它最多应该这么宽,并允许intrinsicContentSize() 找出并返回一个合适的高度来匹配。通过在您的示例中不设置 preferredMaxLayoutWidth,标签使其宽度不受限制,因此会计算单行长文本的高度。

preferredMaxLayoutWidth 的唯一复杂之处在于,在自动布局为您计算出宽度之前,您通常不知道标签的宽度。因此,在视图控制器子类(看起来您的代码示例可能来自)中设置它的位置位于viewDidLayoutSubviews

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    postBody.preferredMaxLayoutWidth = 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
    }
}

显然,您需要为postBody 标签添加一个属性才能使其正常工作。

如果您不在 UIViewController 子类中,请告诉我,我将编辑我的答案。

【讨论】:

    【解决方案2】:

    使用情节提要实现

    1. UItableView添加UITableViewCellUIView并把他放在UILabel 通过自动布局连接它们
    2. UILabel 中将行数设为0。
    3. ViewDidLoad你的UILabel调用一个方法sizeToFit() 并指定UIView 的大小,这将是您的HeaderVew headerView.frame.size.height = headerLabel.frame.size.height

    代码

        @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
        }
    

    截图

    测试项目

    test project link

    【讨论】:

      【解决方案3】:

      我们遇到的第一个问题是header无法通过autolayout调整大小,详情见Is it possible to use AutoLayout with UITableView's tableHeaderView?

      因此,我们必须手动计算表头的高度,例如:

      @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
      }
      

      您可能还想使用stefandouganhyde's answer 中的代码。如何计算高度并不重要。关键是tableHeaderView 的自动布局不会自动工作。

      结果:

      【讨论】:

        【解决方案4】:

        我们使用 NSLayoutManager 根据文本快速估计需要调整大小的项目的高度。这是基本思想:

        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 
        }
        

        Beslan's answer 可能更适合您的用例,但我发现更好地控制布局的处理方式。

        【讨论】:

          【解决方案5】:

          //可能对你有帮助。

          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
          

          【讨论】:

            【解决方案6】:

            你可以通过使用它的字符串来计算标签的高度

            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
            

            【讨论】:

            • 嗯,这是针对节标题的,而不是针对表标题的。一个非常不同的事情。
            猜你喜欢
            • 2013-02-21
            • 2017-10-27
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2023-03-10
            • 2014-01-26
            • 2011-03-29
            • 2022-08-12
            相关资源
            最近更新 更多