Using autolayout in a tableHeaderView

2019-01-08 17:25发布

I have a UIView subclass that contains a multi-line UILabel. This view uses autolayout.

enter image description here

I would like to set this view as the tableHeaderView of a UITableView (not a section header). The height of this header will depend on the text of the label, which in turn depends on the width of the device. The sort of scenario autolayout should be great at.

I have found and attempted many many solutions to get this working, but to no avail. Some of the things I've tried:

  • setting a preferredMaxLayoutWidth on each label during layoutSubviews
  • defining an intrinsicContentSize
  • attempting to figure out the required size for the view and setting the tableHeaderView's frame manually.
  • adding a width constraint to the view when the header is set
  • a bunch of other things

Some of the various failures I've encountered:

  • label extends beyond the width of the view, doesn't wrap
  • frame's height is 0
  • app crashes with exception Auto Layout still required after executing -layoutSubviews

The solution (or solutions, if necessary) should work for both iOS 7 and iOS 8. Note that all of this is being done programmatically. I've set up a small sample project in case you want to hack on it to see the issue. I've reset my efforts to the following start point:

SCAMessageView *header = [[SCAMessageView alloc] init];
header.titleLabel.text = @"Warning";
header.subtitleLabel.text = @"This is a message with enough text to span multiple lines. This text is set at runtime and might be short or long.";
self.tableView.tableHeaderView = header;

What am I missing?

8条回答
三岁会撩人
2楼-- · 2019-01-08 18:19

Your constraints were just a little off. Take a look at this and let me know if you have any questions. For some reason I had difficulty getting the background of the view to stay red? So I created a filler view that fills the gap created by having a titleLabel and subtitleLabel height that is greater than the height of the imageView

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self)
    {
        self.backgroundColor = [UIColor redColor];

        self.imageView = [[UIImageView alloc] initWithImage:[[UIImage imageNamed:@"Exclamation"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]];
        self.imageView.tintColor = [UIColor whiteColor];
        self.imageView.translatesAutoresizingMaskIntoConstraints = NO;
        self.imageView.backgroundColor = [UIColor redColor];
        [self addSubview:self.imageView];
        [self.imageView mas_makeConstraints:^(MASConstraintMaker *make) {
            make.left.equalTo(self);
            make.width.height.equalTo(@40);
            make.top.equalTo(self).offset(0);
        }];

        self.titleLabel = [[UILabel alloc] init];
        self.titleLabel.translatesAutoresizingMaskIntoConstraints = NO;
        self.titleLabel.font = [UIFont systemFontOfSize:14];
        self.titleLabel.textColor = [UIColor whiteColor];
        self.titleLabel.backgroundColor = [UIColor redColor];
        [self addSubview:self.titleLabel];
        [self.titleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
            make.top.equalTo(self).offset(0);
            make.left.equalTo(self.imageView.mas_right).offset(0);
            make.right.equalTo(self).offset(-10);
            make.height.equalTo(@15);
        }];

        self.subtitleLabel = [[UILabel alloc] init];
        self.subtitleLabel.translatesAutoresizingMaskIntoConstraints = NO;
        self.subtitleLabel.font = [UIFont systemFontOfSize:13];
        self.subtitleLabel.textColor = [UIColor whiteColor];
        self.subtitleLabel.numberOfLines = 0;
        self.subtitleLabel.backgroundColor = [UIColor redColor];
        [self addSubview:self.subtitleLabel];
        [self.subtitleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
            make.top.equalTo(self.titleLabel.mas_bottom);
            make.left.equalTo(self.imageView.mas_right);
            make.right.equalTo(self).offset(-10);
        }];

        UIView *fillerView = [[UIView alloc] init];
        fillerView.backgroundColor = [UIColor redColor];
        [self addSubview:fillerView];
        [fillerView mas_makeConstraints:^(MASConstraintMaker *make) {
            make.top.equalTo(self.imageView.mas_bottom);
            make.bottom.equalTo(self.subtitleLabel.mas_bottom);
            make.left.equalTo(self);
            make.right.equalTo(self.subtitleLabel.mas_left);
        }];
    }

    return self;
}
查看更多
Anthone
3楼-- · 2019-01-08 18:23

This should do the trick for a headerView or a footerView for the UITableView using AutoLayout.

extension UITableView {

  var tableHeaderViewWithAutolayout: UIView? {
    set (view) {
      tableHeaderView = view
      if let view = view {
        lowerPriorities(view)
        view.frameSize = view.systemLayoutSizeFitting(UILayoutFittingCompressedSize)
        tableHeaderView = view
      }
    }
    get {
      return tableHeaderView
    }
  }

  var tableFooterViewWithAutolayout: UIView? {
    set (view) {
      tableFooterView = view
      if let view = view {
        lowerPriorities(view)
        view.frameSize = view.systemLayoutSizeFitting(UILayoutFittingCompressedSize)
        tableFooterView = view
      }
    }
    get {
      return tableFooterView
    }
  }

  fileprivate func lowerPriorities(_ view: UIView) {
    for cons in view.constraints {
      if cons.priority.rawValue == 1000 {
        cons.priority = UILayoutPriority(rawValue: 999)
      }
      for v in view.subviews {
        lowerPriorities(v)
      }
    }
  }
}
查看更多
登录 后发表回答