-systemLayoutSizeFittingSize:在iOS 8下为tableHeaderView返回不正确的高度

时间:2014-10-15 20:57:37

标签: ios objective-c uitableview

有许多关于使用自动布局(one such thread)正确调整tableHeaderView大小的线程,但它们倾向于预先设置iOS 8。

我的情况有很多表格视图,都有标题,在iOS 7下正确大小但在iOS 8下使用大多数上述线程支持的代码不正确。在表的控制器中,我有以下方法:

- (void)rejiggerTableHeaderView
{
    self.tableView.tableHeaderView = nil;

    UIView *header = self.headerView;

    [header setNeedsLayout];
    [header layoutIfNeeded];

    CGFloat height = [header systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;

    CGRect headerFrame = header.frame;
    headerFrame.size.height = height;

    header.frame = headerFrame;

    self.tableView.tableHeaderView = header;
}

在iOS 7下使用多行标签,这样可以正确调整表格视图的标题大小:

但是在iOS 8下运行的相同代码会产生以下结果:

获取-systemLayoutSizeFittingSize的技巧是什么:在iOS 8下返回正确的大小? Here's a sample project证明了这个问题。

6 个答案:

答案 0 :(得分:17)

将标题视图功能更改为以下内容适用于我:

- (void)rejiggerTableHeaderView
{
    self.tableView.tableHeaderView = nil;

    UIView *header = self.headerView;
    CGRect frame = header.frame;
    frame.size.width = self.tableView.frame.size.width;
    header.frame = frame;

    [header setNeedsLayout];
    [header layoutIfNeeded];

    CGFloat height = [header systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;

    CGRect headerFrame = header.frame;
    headerFrame.size.height = height;

    header.frame = headerFrame;

    self.tableView.tableHeaderView = header;
}

答案 1 :(得分:14)

问题可以在非设定preferredMaxLayoutWidth中。 如果您将其设置为更正UILabel宽度,它将正确确定约束。

您可以浏览标题中的所有UILabel并将preferredMaxLayoutWidth设置为标签宽度。

Swift 3示例:

extension UITableView {
    public func relayoutTableHeaderView() {
        if let tableHeaderView = tableHeaderView {
            let labels = tableHeaderView.findViewsOfClass(viewClass: UILabel.self)
            for label in labels {
                label.preferredMaxLayoutWidth = label.frame.width
            }
            tableHeaderView.setNeedsLayout()
            tableHeaderView.layoutIfNeeded()
            tableHeaderView.frame.height = tableHeaderView.systemLayoutSizeFitting(UILayoutFittingCompressedSize).height
            self.tableHeaderView = tableHeaderView
        }
    }

    public func findViewsOfClass<T:UIView>(viewClass: T.Type) -> [T] {
        var views: [T] = []
        for subview in subviews {
            if subview is T {
                views.append(subview as! T)
            }
            views.append(contentsOf: subview.findViewsOfClass(viewClass: T.self))
        }
        return views
    }
}

更新2:

如果您有具有宽高比约束的子视图并且同时与超视宽度约束成比例,则您可能会遇到高度计算不正确的问题

答案 2 :(得分:6)

由于这个问题已经有一年半了,所以这里是一个更新的完整版本,在Swift中。接受的答案中的一些代码是错误的或过时的。

func fixHeaderHeight() {
    // Set your label text if needed
    // ...
    //

    guard let header = tableView.tableHeaderView else {
        return
    }    
    let height = header.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height
    header.frame.height = height            
    tableView.tableHeaderView = header
}

代码中的其他位置,您需要在标头中设置标签的preferredMaxLayoutWidth。这应该等于tableView(或屏幕宽度)减去任何填充。标签出口的didSet方法是一个好地方:

@IBOutlet weak var headerMessageLabel: UILabel! {
        didSet {
            headerMessageLabel.preferredMaxLayoutWidth = UIScreen.mainScreen().bounds.width - headerMessageLabelPadding
        }
    }

注意:如果接受的答案适合您,则表示您未正确使用大小类。

答案 3 :(得分:0)

如果标题只包含一个标签,那么我使用UILabel扩展名来查找给定宽度的多行标签高度:

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

注意:以上是Swift 3语法。

使用上面计算的size,您可以使用UITableViewDelegate方法返回正确的高度:

func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat

答案 4 :(得分:0)

Swift 3中针对此问题的正确解决方案是使用此类而不是标准UILabel:

class UILabelPreferedWidth : UILabel {
    override var bounds: CGRect {
        didSet {
            if (bounds.size.width != oldValue.size.width) {
                self.setNeedsUpdateConstraints()
            }
        }
    }

    override func updateConstraints() {
        if(preferredMaxLayoutWidth != bounds.size.width) {
            preferredMaxLayoutWidth = bounds.size.width
        }
        super.updateConstraints()
    }
}

另外请确保您没有在Cell类中禁用这两个:

translatesAutoresizingMaskIntoConstraints = false
contentView.translatesAutoresizingMaskIntoConstraints = false

答案 5 :(得分:0)

我遇到了同样的问题。 这适用于iOS 8.4。

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    [self.myLabel sizeToFit];
    [self.tableView.tableHeaderView layoutIfNeeded];
    return UITableViewAutomaticDimension;
}