是否可以将AutoLayout与UITableView的tableHeaderView一起使用?

Its*_*ret 91 iphone uitableview ipad ios autolayout

因为我发现AutoLayout我到处都使用它,现在我正在尝试使用它tableHeaderView.

我做了subclass一些UIView添加的东西(标签等......)我想要他们的约束,然后我把它添加CustomViewUITableView' tableHeaderView.

一切都工作得很好,除了UITableView总是显示上面CustomView,通过以上我指的CustomViewUITableView,因此它不能被看到!

看来,无论我做什么,对heightUITableView" tableHeaderView始终 0(所以是宽度,x和y).

我的问题:如果不手动设置框架,是否可以完成此操作

编辑:我正在使用 的CustomView' subview有这些限制:

_title = [[UILabel alloc]init];
_title.text = @"Title";
[self addSubview:_title];
[_title keep:[KeepTopInset rules:@[[KeepEqual must:5]]]]; // title has to stay at least 5 away from the supperview Top
[_title keep:[KeepRightInset rules:@[[KeepMin must:5]]]];
[_title keep:[KeepLeftInset rules:@[[KeepMin must:5]]]];
[_title keep:[KeepBottomInset rules:@[[KeepMin must:5]]]];
Run Code Online (Sandbox Code Playgroud)

我正在使用一个方便的库'KeepLayout',因为手动编写约束需要永远和一个单一约束的方式太多,但方法是自我解释.

并且UITableView有这些限制:

_tableView = [[UITableView alloc]init];
_tableView.translatesAutoresizingMaskIntoConstraints = NO;
_tableView.delegate = self;
_tableView.dataSource = self;
_tableView.backgroundColor = [UIColor clearColor];
[self.view addSubview:_tableView];
[_tableView keep:[KeepTopInset rules:@[[KeepEqual must:0]]]];// These 4 constraints make the UITableView stays 0 away from the superview top left right and bottom.
[_tableView keep:[KeepLeftInset rules:@[[KeepEqual must:0]]]];
[_tableView keep:[KeepRightInset rules:@[[KeepEqual must:0]]]];
[_tableView keep:[KeepBottomInset rules:@[[KeepEqual must:0]]]];

_detailsView = [[CustomView alloc]init];
_tableView.tableHeaderView = _detailsView;
Run Code Online (Sandbox Code Playgroud)

我不知道是否必须直接设置一些约束CustomView,我认为CustomView的高度取决于其中UILabel"标题" 的约束.

编辑2:经过另一次调查后,似乎正确计算了CustomView的高度和宽度,但CustomView的顶部仍然与UITableView的顶部处于同一级别,当我滚动时它们一起移动.

Ben*_*ard 129

在这里问了并回答了类似的问题.总之,我添加了一次标题并使用它来查找所需的高度.然后可以将该高度应用于标题,并且第二次设置标题以反映更改.

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.header = [[SCAMessageView alloc] init];
    self.header.titleLabel.text = @"Warning";
    self.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.";

    //set the tableHeaderView so that the required height can be determined
    self.tableView.tableHeaderView = self.header;
    [self.header setNeedsLayout];
    [self.header layoutIfNeeded];
    CGFloat height = [self.header systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;

    //update the header's frame and set it again
    CGRect headerFrame = self.header.frame;
    headerFrame.size.height = height;
    self.header.frame = headerFrame;
    self.tableView.tableHeaderView = self.header;
}
Run Code Online (Sandbox Code Playgroud)

如果您有多行标签,这也依赖于自定义视图设置每个标签的preferredMaxLayoutWidth:

- (void)layoutSubviews
{
    [super layoutSubviews];

    self.titleLabel.preferredMaxLayoutWidth = CGRectGetWidth(self.titleLabel.frame);
    self.subtitleLabel.preferredMaxLayoutWidth = CGRectGetWidth(self.subtitleLabel.frame);
}
Run Code Online (Sandbox Code Playgroud)

或者更一般地说:

override func layoutSubviews() {
    super.layoutSubviews()  
    for view in subviews {
        guard let label = view as? UILabel where label.numberOfLines == 0 else { continue }
        label.preferredMaxLayoutWidth = CGRectGetWidth(label.frame)
    }
}
Run Code Online (Sandbox Code Playgroud)

2015年1月更新

不幸的是,这仍然是必要的 这是布局过程的一个快速版本:

tableView.tableHeaderView = header
header.setNeedsLayout()
header.layoutIfNeeded()
header.frame.size = header.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)
tableView.tableHeaderView = header
Run Code Online (Sandbox Code Playgroud)

我发现将它移动到UITableView的扩展中很有用:

extension UITableView {
    //set the tableHeaderView so that the required height can be determined, update the header's frame and set it again
    func setAndLayoutTableHeaderView(header: UIView) {
        self.tableHeaderView = header
        header.setNeedsLayout()
        header.layoutIfNeeded()
        header.frame.size = header.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)
        self.tableHeaderView = header
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

let header = SCAMessageView()
header.titleLabel.text = "Warning"
header.subtitleLabel.text = "Warning message here."
tableView.setAndLayoutTableHeaderView(header)
Run Code Online (Sandbox Code Playgroud)

  • 使用`preferredMaxLayoutWidth`的替代方法是在使用`systemLayoutSizeFittingSize:`之前在标题视图上添加宽度约束(等于表视图的宽度). (8认同)
  • 经常让我惊讶的是做一些像这样完全无关紧要的事情是多么棘手. (6认同)
  • 只需使用`header.setNeedsLayout()header.layoutIfNeeded()header.frame.size = header.systemLayoutSizeFitting(UILayoutFittingCompressedSize)self.tableHeaderView = header`适用于iOS 10.2 (3认同)
  • 注意:如果您遇到标题位于第一个单元格上方,则您忘记将标题属性重置为`self.tableView.tableHeaderView` (2认同)
  • 耶稣!label.preferredMaxLayoutWidth = CGRectGetWidth(label.frame) 很重要!谢谢 (2认同)
  • 注意:如果需要获取精确的tableView宽度,则应使用所需的水平优先级来获得高度`let height = header.systemLayoutSizeFittingSize(CGSizeMake(CGRectGetWidth(self.bounds),0),withHorizo​​ntalFittingPriority:UILayoutPriorityRequired,verticalFittingPriority:UILayoutPriorityFittingSizeLevel).height ` (2认同)

rde*_*mar 24

我一直无法使用约束(在代码中)添加标题视图.如果我给我的视图一个宽度和/或一个高度约束,我得到一个崩溃的消息说:

 "terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Auto Layout still required after executing -layoutSubviews. UITableView's implementation of -layoutSubviews needs to call super."
Run Code Online (Sandbox Code Playgroud)

当我将故事板中的视图添加到我的表视图时,它没有显示任何约束,并且它作为标题视图工作正常,所以我认为标题视图的放置不是使用约束完成的.在这方面,它似乎不像普通的观点.

宽度自动是表格视图的宽度,您需要设置的唯一内容是高度 - 原点值被忽略,因此您为这些放置的内容无关紧要.例如,这个工作正常(对于rect来说也是0,0,0,80):

UIView *headerview = [[UIView alloc] initWithFrame:CGRectMake(1000,1000, 0, 80)];
headerview.backgroundColor = [UIColor yellowColor];
self.tableView.tableHeaderView = headerview;
Run Code Online (Sandbox Code Playgroud)


Ram*_*los 13

我在这里看到很多方法做了很多不必要的事情,但是你不需要在标题视图中使用自动布局那么多.你只需要创建你的xib文件,把你的约束和实例化如下:

func loadHeaderView () {
        guard let headerView = Bundle.main.loadNibNamed("CourseSearchHeader", owner: self, options: nil)?[0] as? UIView else {
            return
        }
        headerView.autoresizingMask = .flexibleWidth
        headerView.translatesAutoresizingMaskIntoConstraints = true
        tableView.tableHeaderView = headerView
    }
Run Code Online (Sandbox Code Playgroud)


Mar*_*tin 6

另一个解决方案是将标题视图创建分配给下一个主线程调用:

- (void)viewDidLoad {
    [super viewDidLoad];

    // ....

    dispatch_async(dispatch_get_main_queue(), ^{
        _profileView = [[MyView alloc] initWithNib:@"MyView.xib"];
        self.tableView.tableHeaderView = self.profileView;
    });
}
Run Code Online (Sandbox Code Playgroud)

注意:当加载的视图具有固定高度时,它会修复错误.当标题高度仅取决于其内容时,我还没有尝试过.

编辑:

通过实现此功能并调用它,您可以找到更清晰的解决方案viewDidLayoutSubviews

- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];

    [self sizeHeaderToFit];
}
Run Code Online (Sandbox Code Playgroud)


Jon*_*han 5

您可以使用systemLayoutSizeFittingSize方法让自动布局为您提供尺寸。

然后,您可以使用它来为您的应用程序创建框架。每当您需要知道内部使用自动布局的视图的大小时,此技术就有效。

swift 中的代码看起来像

//Create the view
let tableHeaderView = CustomTableHeaderView()

//Set the content
tableHeaderView.textLabel.text = @"Hello world"

//Ask auto layout for the smallest size that fits my constraints    
let size = tableHeaderView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)

//Create a frame    
tableHeaderView.frame = CGRect(origin: CGPoint.zeroPoint, size: size)

//Set the view as the header    
self.tableView.tableHeaderView = self.tableHeaderView
Run Code Online (Sandbox Code Playgroud)

或者在 Objective-C 中

//Create the view
CustomTableHeaderView *header = [[CustomTableHeaderView alloc] initWithFrame:CGRectZero];

//Set the content
header.textLabel.text = @"Hello world";

//Ask auto layout for the smallest size that fits my constraints
CGSize size = [header systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];

//Create a frame
header.frame = CGRectMake(0,0,size.width,size.height);

//Set the view as the header  
self.tableView.tableHeaderView = header
Run Code Online (Sandbox Code Playgroud)

还应该注意的是,在这个特定的实例中,在子类中重写 requireConstraintBasedLayout 确实会导致执行布局传递,但是此布局传递的结果将被忽略,并且系统框架设置为 tableView 的宽度和 0 高度。


Phi*_*hil 5

代码:

  extension UITableView {

          func sizeHeaderToFit(preferredWidth: CGFloat) {
            guard let headerView = self.tableHeaderView else {
              return
            }

            headerView.translatesAutoresizingMaskIntoConstraints = false
            let layout = NSLayoutConstraint(
              item: headerView,
              attribute: .Width,
              relatedBy: .Equal,
              toItem: nil,
              attribute:
              .NotAnAttribute,
              multiplier: 1,
              constant: preferredWidth)

            headerView.addConstraint(layout)

            let height = headerView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height
            headerView.frame = CGRectMake(0, 0, preferredWidth, height)

            headerView.removeConstraint(layout)
            headerView.translatesAutoresizingMaskIntoConstraints = true

            self.tableHeaderView = headerView
          }
  }
Run Code Online (Sandbox Code Playgroud)


k06*_*06a 5

扩展此解决方案http://collindonnell.com/2015/09/29/dynamically-sized-table-view-header-or-footer-using-auto-layout/用于表页脚视图:

@interface AutolayoutTableView : UITableView

@end

@implementation AutolayoutTableView

- (void)layoutSubviews {
    [super layoutSubviews];

    // Dynamic sizing for the header view
    if (self.tableHeaderView) {
        CGFloat height = [self.tableHeaderView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
        CGRect headerFrame = self.tableHeaderView.frame;

        // If we don't have this check, viewDidLayoutSubviews() will get
        // repeatedly, causing the app to hang.
        if (height != headerFrame.size.height) {
            headerFrame.size.height = height;
            self.tableHeaderView.frame = headerFrame;
            self.tableHeaderView = self.tableHeaderView;
        }

        [self.tableHeaderView layoutIfNeeded];
    }

    // Dynamic sizing for the footer view
    if (self.tableFooterView) {
        CGFloat height = [self.tableFooterView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
        CGRect footerFrame = self.tableFooterView.frame;

        // If we don't have this check, viewDidLayoutSubviews() will get
        // repeatedly, causing the app to hang.
        if (height != footerFrame.size.height) {
            footerFrame.size.height = height;
            self.tableFooterView.frame = footerFrame;
            self.tableFooterView = self.tableFooterView;
        }

        self.tableFooterView.transform = CGAffineTransformMakeTranslation(0, self.contentSize.height - footerFrame.size.height);
        [self.tableFooterView layoutIfNeeded];
    }
}

@end
Run Code Online (Sandbox Code Playgroud)


Cal*_*den 5

更新为 Swift 4.2

extension UITableView {

    var autolayoutTableViewHeader: UIView? {
        set {
            self.tableHeaderView = newValue
            guard let header = newValue else { return }
            header.setNeedsLayout()
            header.layoutIfNeeded()
            header.frame.size = 
            header.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
            self.tableHeaderView = header
        }
        get {
            return self.tableHeaderView
        }
    }
}
Run Code Online (Sandbox Code Playgroud)