Как создать заголовок UITableView, высота которого определяется высотой его метки?

Я хотел бы добавить заголовок в мой tableView. Этот заголовок содержит 1 UILabel. Высота заголовка должна быть рассчитана в зависимости от количества строк, которые имеет метка.

В моем коде я добавляю ограничения со всеми краями заголовка label < > . Это моя попытка:

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

Заголовок не авто-высота

Ответ 1

UILabel используйте UIView intrinsicContentSize(), чтобы сообщить авторам о том, какой размер они должны быть. Однако для многострочной метки внутренний размер содержимого неоднозначен; таблица не знает, должна ли она быть короткой и широкой, высокой и узкой, или что-либо между ними.

Чтобы бороться с этим, 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

Реализация с использованием раскадровки

  • В UItableView добавить UITableViewCell новый UIView и поместить его UILabel Соединяет их через Autolayout
  • В UILabel поместите количество строк в 0.
  • В 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
    }

ScreenShot

введите описание изображения здесь

TestProject

ссылка на тестовый проект

Ответ 3

Первая проблема заключается в том, что заголовок не может быть изменен с помощью автозапуска, для получения дополнительной информации см. Можно ли использовать AutoLayout с таблицей TableHeaderView UITableView?

Следовательно, мы должны вычислить высоту заголовка вручную, например:

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

Ответ в Беслан, вероятно, лучше подходит для вашего случая использования, но мне приятно иметь больше контроля над обработкой макета.

Ответ 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