-systemLayoutSizeFittingSize: returning incorrect height for tableHeaderView under iOS 8

前端 未结 6 1787
旧时难觅i
旧时难觅i 2021-02-07 00:08

There are numerous threads about correctly sizing a tableHeaderView with auto-layout (one such thread) but they tend to pre-date iOS 8.

I have a situation with numerous

相关标签:
6条回答
  • 2021-02-07 00:43

    Since this question is a year and a half old, here is a updated and complete version, in Swift. Some of the code from the accepted answer is wrong or outdated.

    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
    }
    

    Somewhere else in your code, you'll need to set the preferredMaxLayoutWidth of the label(s) in the header. This should be equal to the tableView (or screen width) minus any padding. The didSet method of your label outlet is a good place:

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

    Note: If the accepted answer worked for you, you aren't using size classes properly.

    0 讨论(0)
  • 2021-02-07 00:49

    Changing your headerview function to the following works for me:

    - (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;
    }
    
    0 讨论(0)
  • 2021-02-07 00:52

    If the header just contains a single label then I use a UILabel extension to find a multiline label height given a width:

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

    Note: the above is in Swift 3 syntax.

    With the size calculated above you can return the correct height in the UITableViewDelegate method:

    func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat
    
    0 讨论(0)
  • 2021-02-07 00:52

    I had a same problem. This works well for me on iOS 8.4.

    - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
        [self.myLabel sizeToFit];
        [self.tableView.tableHeaderView layoutIfNeeded];
        return UITableViewAutomaticDimension;
    }
    
    0 讨论(0)
  • 2021-02-07 00:55

    The proper solution for this problem in Swift 3 is using this class instead of standard 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()
        }
    }
    

    Also make sure that you didn't disable those two on your Cell class:

    translatesAutoresizingMaskIntoConstraints = false
    contentView.translatesAutoresizingMaskIntoConstraints = false
    
    0 讨论(0)
  • Problem can be in non-set preferredMaxLayoutWidth. If you will set it to correct UILabel width, it will determine constraints correctly.

    You can go through all UILabel in header and set preferredMaxLayoutWidth to label width.

    Swift 3 example:

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

    UPDATE 2:

    Also you can have problem with incorrect height calculation if you have subview with aspect ratio constraint and at the same time proportional to superview width constraint

    0 讨论(0)
提交回复
热议问题