ChatGPT解决这个技术问题 Extra ChatGPT

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

自从我发现 AutoLayout 我在任何地方都使用它,现在我尝试将它与 tableHeaderView 一起使用。

我制作了 subclass of UIView 添加了我想要的所有内容(标签等...)以及它们的约束,然后我将此 CustomView 添加到 UITableView'tableHeaderView

一切正常,除了 UITableView 始终显示 above CustomViewabove 我的意思是 CustomView under { 1}所以它不能被看到!

似乎无论我做什么,UITableView'tableHeaderViewheight 都是始终 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]]]];

我正在使用一个方便的库“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;

不知道要不要直接在CustomView上设置一些约束,我觉得CustomView的高度是由里面的UILabel“title”的约束决定的。

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

对的,这是可能的。你能显示你正在使用的代码吗?如果不知道您在标题视图上设置了哪些约束,很难提出建议。
实现此目的的一种简单方法是将 IB 中的视图添加到 tableView..只需在包含 tableview 的同一场景中创建视图并将其拖到表中即可。
我尽量避免使用 IB,到目前为止我还没有使用它,如果我不能让它工作,我会尝试使用 IB
Apple 建议开发人员在自动布局方面尽可能使用 IB。它确实有助于避免很多不一致的问题。
真正完整的自动布局解决方案是 here

W
Will

我问并回答了一个类似的问题 here。总之,我添加了一次标题并使用它来查找所需的高度。然后可以将该高度应用于页眉,并再次设置页眉以反映更改。

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

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

- (void)layoutSubviews
{
    [super layoutSubviews];

    self.titleLabel.preferredMaxLayoutWidth = CGRectGetWidth(self.titleLabel.frame);
    self.subtitleLabel.preferredMaxLayoutWidth = CGRectGetWidth(self.subtitleLabel.frame);
}

或者更一般地说:

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

2015 年 1 月更新

不幸的是,这似乎仍然是必要的。这是布局过程的快速版本:

tableView.tableHeaderView = header
header.setNeedsLayout()
header.layoutIfNeeded()
header.frame.size = header.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)
tableView.tableHeaderView = header

我发现将其移动到 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
        self.tableHeaderView?.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            header.widthAnchor.constraint(equalTo: self.widthAnchor)
        ])
        header.setNeedsLayout()
        header.layoutIfNeeded()
        header.frame.size =  header.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
        self.tableHeaderView = header
    }
}

用法:

let header = SCAMessageView()
header.titleLabel.text = "Warning"
header.subtitleLabel.text = "Warning message here."
tableView.setAndLayoutTableHeaderView(header)

使用 preferredMaxLayoutWidth 的替代方法是在使用 systemLayoutSizeFittingSize: 之前在标题视图上添加宽度约束(等于表格视图的宽度)。
注意:如果您遇到标题在第一个单元格上方,那么您忘记将标题属性重置为 self.tableView.tableHeaderView
经常让我惊讶的是,做这样一件完全微不足道的事情是多么棘手。
注意:如果您需要获得与 tableView 一样的精确宽度,您应该获得具有所需水平优先级的高度 let height = header.systemLayoutSizeFittingSize(CGSizeMake(CGRectGetWidth(self.bounds), 0), withHorizontalFittingPriority: UILayoutPriorityRequired, verticalFittingPriority: UILayoutPriorityFittingSizeLevel).height
Just Use header.setNeedsLayout() header.layoutIfNeeded() header.frame.size = header.systemLayoutSizeFitting(UILayoutFittingCompressedSize) self.tableHeaderView = header 可以在 iOS 10.2 上运行
K
Kampai

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

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

当我将情节提要中的视图添加到我的表视图时,它没有显示任何约束,并且它可以作为标题视图正常工作,因此我认为标题视图的放置不是使用约束完成的。在这方面,它似乎不像一个正常的视图。

宽度自动是表格视图的宽度,您唯一需要设置的是高度 - 原始值被忽略,因此您为它们输入什么并不重要。例如,这很好用(矩形的 0,0,0,80 也是如此):

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

我也有这个异常,但是向 UITableView 添加一个类别修复了它,我在那个答案中找到了它:stackoverflow.com/questions/12610783/…
我仍然会尝试你的建议,但是明天早上1:34我要睡觉了,非常感谢你抽出时间来回答! (但我真的不想指定高度,我希望通过我在 CustomView 中的标签上设置的约束来计算它)
我已经尝试过了,是的,设置框架是可行的,但我一直在寻找一种避免设置框架的方法,我会继续寻找,如果我找不到其他任何东西,我会接受你的回答
如果添加的标题视图具有 translatesAutoresizingMaskIntoConstraints = NO,我会收到此异常(当前正在测试 7.1)。打开翻译可以防止错误——我怀疑从 7.1 开始的 UITableView 不会尝试自动布局其标题视图,而是需要预先设置框架的东西。
R
Ramon Vasconcelos

我在这里看到很多方法做了很多不必要的事情,但是你不需要那么多在标题视图中使用自动布局。你只需要创建你的 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
    }

这也适用于我们在 iOS 11 上使用带有多行标签的动态高度标题。
当然也可以去掉 IB 中的 flexibleHeight-Autoresizing-Option。
我一直在尝试设置 tableFooterView 的高度(通过 xib / nib)并且没有成功设置框架、高度、layoutIfNeeded() 等。但是这个解决方案最终允许我设置它。
不要忘记在 xib 文件中为整个视图设置高度约束。
C
Community

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

- (void)viewDidLoad {
    [super viewDidLoad];

    // ....

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

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

编辑 :

您可以通过实现此 function 并在 viewDidLayoutSubviews 中调用它来找到解决此问题的更简洁的方法

- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];

    [self sizeHeaderToFit];
}

@TussLaszlo tableHeaderView 在自动布局方面有点问题。有一些解决方法,比如这个。但自从我写了这篇文章后,我通过在 viewDidLayoutSubviews 中调用它的 - (void)sizeHeaderToFit 找到了一个更好和更清洁的解决方案 stackoverflow.com/a/21099430/127493
P
Phil

代码:

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

它有效。为所有 tableheaderview 子视图提供适当的自动布局约束。如果您错过了一个约束,它将不起作用。
F
Frederic Adda

将此解决方案 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

昨天花了一天时间试图让 tableHeader 正确地自动调整大小/布局。这个解决方案对我有用。谢谢一堆。
你好!你能解释一下self.tableFooterView.transform部分吗?为什么有必要?
@mrvn 转换用于将页脚移动到 tableView 的底部。
C
Community

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

J
Jonathan

您可以使用 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

或者在 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

还应注意,在此特定实例中,在您的子类中覆盖 requiresConstraintBasedLayout 会导致执行布局传递,但是此布局传递的结果将被忽略,系统框架设置为 tableView 的宽度和 0 高度。


D
David Nix

以下对我有用。

使用普通的旧 UIView 作为标题视图。将子视图添加到该 UIView 在子视图上使用自动布局

我看到的主要好处是限制帧计算。 Apple 确实应该更新 UITableView 的 API 以使其更容易。

使用 SnapKit 的示例:

let layoutView = UIView(frame: CGRect(x: 0, y: 0, width: tableView.bounds.width, height: 60))
layoutView.backgroundColor = tableView.backgroundColor
tableView.tableHeaderView = layoutView

let label = UILabel()
layoutView.addSubview(label)
label.text = "I'm the view you really care about"
label.snp_makeConstraints { make in
    make.edges.equalTo(EdgeInsets(top: 10, left: 15, bottom: -5, right: -15))
}

H
HotJard

奇怪的事情发生了。 systemLayoutSizeFittingSize 适用于 iOS9,但不适用于 iOS 8。所以这个问题很容易解决。只需在超级调用更新标题视图边界后通过将高度插入为 CGRectGetMaxY(yourview.frame) + padding 来获取标题和 viewDidLayoutSubviews 中底部视图的链接

UPD:有史以来最简单的解决方案:因此,在标题视图中放置子视图并将其固定在左侧、右侧、顶部。在该子视图中放置具有自动高度约束的子视图。之后将所有工作交给自动布局(无需计算)

- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];

    CGFloat height = CGRectGetMaxY(self.tableView.tableHeaderView.subviews.firstObject.frame);
    self.tableView.tableHeaderView.bounds = CGRectMake(0, 0, CGRectGetWidth(self.tableView.bounds), height);
    self.tableView.tableHeaderView = self.tableView.tableHeaderView;
}

结果子视图像它应该的那样扩展/收缩,最后它调用viewDidLayoutSubviews。当时我们知道view的实际大小,所以设置headerView的高度,通过重新赋值来更新。奇迹般有效!

也适用于页脚视图。


这在 iOS 10 中为我循环。
G
GreatWiz

您可以在标题和表格视图之间添加顶部+水平位置约束,以正确放置它(如果标题本身包含所有必要的内部布局约束以拥有正确的框架)

在 tableViewController viewDidLoad 方法中

    headerView.translatesAutoresizingMaskIntoConstraints = false

    tableView.tableHeaderView = headerView

    headerView.widthAnchor.constraint(equalTo: tableView.widthAnchor).isActive = true
    headerView.topAnchor.constraint(equalTo: tableView.topAnchor).isActive = true
    headerView.centerXAnchor.constraint(equalTo: tableView.centerXAnchor).isActive = true

这就是所有需要的!事实上,你也可以省略 topAnchor 和 centerXAnchor
p
pfandrade

在大多数情况下,最好的解决方案就是不与框架抗争并采用自动调整大小的蒙版:

// embrace autoresizing masks and let the framework add the constraints for you
headerView.translatesAutoresizingMaskIntoConstraints = true
headerView.autoresizingMask = [.flexibleWidth, .flexibleHeight]

// figure out what's the best size based on the table view width
let width = self.tableView.frame.width
let targetSize = headerView.systemLayoutSizeFitting(CGSize(width: width, height: CGFloat.greatestFiniteMagnitude), withHorizontalFittingPriority: .required, verticalFittingPriority: .fittingSizeLevel)
headerView.frame.size = targetSize
self.tableView.tableHeaderView = headerView

通过使用自动调整掩码,您可以告诉框架当父视图更改其大小时您的视图应如何更改其大小。但此更改基于您设置的初始框架。


好的。自动调整大小的蒙版被严重低估了。
R
Ryan

我的表头视图是一个 UIView 子类 - 我在初始化程序中创建了一个 contentView UIView,它的边界与表头视图的框架相同,并将我的所有对象添加为它的子视图。

然后在表头视图的 layoutSubviews 方法中而不是在初始化程序中添加对象的约束。这解决了崩溃。

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:CGRectMake(0, 0, 0, 44.0)];
    if (self) {
        UIView *contentView = [[UIView alloc] initWithFrame:self.bounds];
        contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth;

        // add other objects as subviews of content view

    }
    return self;
}

- (void)layoutSubviews
{
    [super layoutSubviews];

    // remake constraints here
}

R
RomanV

我的 AutoLayout 运行良好:

CGSize headerSize = [headerView systemLayoutSizeFittingSize:CGSizeMake(CGRectGetWidth([UIScreen mainScreen].bounds), 0) withHorizontalFittingPriority:UILayoutPriorityRequired verticalFittingPriority:UILayoutPriorityFittingSizeLevel];
headerView.frame = CGRectMake(0, 0, headerSize.width, headerSize.height);
self.tableView.tableHeaderView = headerView;

我没有完全做到这一点,但你给了我一个好主意 - 删除 headerView,重新设置它的框架,然后将其添加回来。
H
Hesse Huang

我的解决方案是创建一个这样的新课程。

class BaseTableHeaderView: UIView {

    func sizeToFitBasedOnConstraints(width: CGFloat = Screen.width) {
        let size = systemLayoutSizeFitting(CGSize(width: width, height: 10000),
                                              withHorizontalFittingPriority: .required,
                                              verticalFittingPriority: .fittingSizeLevel)
        frame = CGRect(origin: .zero, size: size)
    }

    override func willMove(toSuperview newSuperview: UIView?) {
        sizeToFitBasedOnConstraints()
        super.willMove(toSuperview: newSuperview)
    }

}

要使用它,只需将所有子视图添加到 BaseTableHeaderView 的实例中并将其附加到您的表格视图中。

let tableHeaderView = BaseTableHeaderView()
tableHeaderView.addSubview(...)
tableView.tableHeaderView = tableHeaderView

它将根据其约束自动调整大小。


M
Marc-Alexandre Bérubé

我知道这是一篇旧帖子,但是在浏览了所有关于此的 SO 帖子并玩了整个下午之后,我终于想出了一个干净但非常简单的解决方案

首先,我的视图层次结构如下所示:

表视图视图 tableHeaderView 带有名为 headerView 的插座的视图

现在在视图(No.3)中,我设置了所有约束,就像我通常将底部空间包括到容器一样。这将使容器(即 3.View 即 headerView)根据其子视图及其约束来调整自身大小。

之后,我将 3. View2. View 之间的约束设置为:

到容器的顶部空间:0 到容器的前导空间:0 到容器的尾随空间:0

请注意,我有意省略了底部空间。

一旦所有这些都在故事板中完成,剩下要做的就是粘贴这三行代码:

if (self.headerView.frame.size.height != self.tableView.tableHeaderView.frame.size.height) {
    UIView *header = self.tableView.tableHeaderView;
    CGRect frame = self.tableView.tableHeaderView.frame;
    frame.size.height = self.headerView.frame.size.height + frame.origin.y;
    header.frame = frame;
    self.tableView.tableHeaderView = header;
}

u
user1511613

Tips:如果使用setAndLayoutTableHeaderView方法,需要更新subviews的frame,所以这种情况下UILabel的preferredMaxLayoutWidth应该在systemLayoutSizeFittingSize调用之前调用,不要在layoutSubview中调用。

code show


V
Vincent Sit

分享我的方法。

UITableView+XXXAdditions.m

- (void)xxx_setTableHeaderView:(UIView *)tableHeaderView layoutBlock:(void(^)(__kindof UIView *tableHeaderView, CGFloat *containerViewHeight))layoutBlock {
      CGFloat containerViewHeight = 0;
      UIView *backgroundView = [[UIView alloc] initWithFrame:CGRectZero];
      [backgroundView addSubview:tableHeaderView];
      layoutBlock(tableHeaderView, &containerViewHeight);

      backgroundView.frame = CGRectMake(0, 0, 0, containerViewHeight);

      self.tableHeaderView = backgroundView;
}

用法。

[self.tableView xxx_setTableHeaderView:myView layoutBlock:^(__kindof UIView * _Nonnull tableHeaderView, CGFloat *containerViewHeight) {
    *containerViewHeight = 170;

    [tableHeaderView mas_makeConstraints:^(MASConstraintMaker *make) {
      make.top.equalTo(@20);
      make.centerX.equalTo(@0);
      make.size.mas_equalTo(CGSizeMake(130, 130));
    }];
  }];

佚名

一个老帖子。不过是个好帖子。这是我的 2 美分。

首先,确保您的标题视图安排了它的约束,以便它可以支持它自己的内在内容大小。然后执行以下操作。

//ViewDidLoad
headerView.translatesAutoresizingMaskIntoConstraints = false
headerView.configure(title: "Some Text A")

//Somewhere else
headerView.update(title: "Some Text B)

private var widthConstrained = false

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    if widthConstrained == false {
        widthConstrained = true
        tableView.addConstraint(NSLayoutConstraint(item: headerView, attribute: .width, relatedBy: .equal, toItem: tableView, attribute: .width, multiplier: 1, constant: 0))
        headerView.layoutIfNeeded()
        tableView.layoutIfNeeded()
    }
}

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    super.viewWillTransition(to: size, with: coordinator)
    coordinator.animate(alongsideTransition: { (context) in
        self.headerView.layoutIfNeeded()
        self.tableView.layoutIfNeeded()
    }, completion: nil)
}

Y
Yevhen Dubinin

我能够通过以下方法实现它(这对页脚同样适用)。

首先,您需要小的 UITableView 扩展:

斯威夫特 3

extension UITableView {
    fileprivate func adjustHeaderHeight() {
        if let header = self.tableHeaderView {
            adjustFrame(header)
        }
    }

    private func adjustFrame(_ view: UIView) {
        view.frame.size.height = calculatedViewHeight(view)
    }

    fileprivate func calculatedHeightForHeader() -> CGFloat {
        if let header = self.tableHeaderView {
            return calculatedViewHeight(header)
        }
        return 0.0
    }

    private func calculatedViewHeight(_ view: UIView) -> CGFloat {
        view.setNeedsLayout()
        let height = view.systemLayoutSizeFitting(UILayoutFittingCompressedSize).height
        return height
    }
}

在您的视图控制器类实现中:

// this is a UIView subclass with autolayout
private var headerView = MyHeaderView()

override func loadView() {
    super.loadView()
    // ...
    self.tableView.tableHeaderView = headerView
    self.tableView.sectionHeaderHeight = UITableViewAutomaticDimension
    // ...
}

override func viewWillLayoutSubviews() {
    super.viewWillLayoutSubviews()

    // this is to prevent recursive layout calls
    let requiredHeaderHeight = self.tableView.calculatedHeightForHeader()
    if self.headerView.frame.height != requiredHeaderHeight {
        self.tableView.adjustHeaderHeight()
    }
}

关于标头 UIView 的子视图实现的注意事项:

您必须 100% 确保您的标题视图具有正确的自动布局设置。我建议从只有一个高度约束的简单标题视图开始,然后尝试上面的设置。覆盖 requiresConstraintBasedLayout 并返回 true:

.

class MyHeaderView: UIView {
   // ...
   override static var requiresConstraintBasedLayout : Bool {
       return true
   }
   // ...
}

G
Gustavo Baiocchi Costa

对于 Xamarin 用户:

public override void ViewDidLayoutSubviews()
{
    base.ViewDidLayoutSubviews();

    TableviewHeader.SetNeedsLayout();
    TableviewHeader.LayoutIfNeeded();

    var height = TableviewHeader.SystemLayoutSizeFittingSize(UIView.UILayoutFittingCompressedSize).Height;
    var frame = TableviewHeader.Frame;
    frame.Height = height;
    TableviewHeader.Frame = frame;
}

假设您将 tableview 的标题视图命名为 TableviewHeader


o
onmyway133

以下是您可以在 UIViewController 中执行的操作

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    if headerView.frame.size.height == 0 {
      headerView.label.preferredMaxLayoutWidth = view.bounds.size.width - 20
      let height = headerView.systemLayoutSizeFitting(UILayoutFittingCompressedSize).height

      headerView.frame.size = CGSize(width: tableView.bounds.size.width, height: height)
    }
  }

m
malex

任何基于约束的 UIView 都可以是很好的 tableHeaderView

需要在 tableFooterView 之前设置一个 tableFooterView,然后对 tableFooterViewtableHeaderView 施加额外的尾随约束。

- (void)viewDidLoad {

    ........................
    // let self.headerView is some constraint-based UIView
    self.tableView.tableFooterView = [UIView new];
    [self.headerView layoutIfNeeded];
    self.tableView.tableHeaderView = self.headerView;

    [self.tableView.leadingAnchor constraintEqualToAnchor:self.headerView.leadingAnchor].active = YES;
    [self.tableView.trailingAnchor constraintEqualToAnchor:self.headerView.trailingAnchor].active = YES;
    [self.tableView.topAnchor constraintEqualToAnchor:self.headerView.topAnchor].active = YES;
    [self.tableFooterView.trailingAnchor constraintEqualToAnchor:self.headerView.trailingAnchor].active = YES;

}

可以找到所有详细信息和代码片段 here


t
tounaobun

我想出了一个解决方法。将您的自动布局写入 xib 标题视图包装在一个空的 uiview 包装器中,并将标题视图分配给 tableView 的 tableViewHeader 属性。

    UIView *headerWrapper = [[UIView alloc] init];
    AXLHomeDriverHeaderView *headerView = [AXLHomeDriverHeaderView loadViewFromNib];
    [headerWrapper addSubview:headerView];
    [headerView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.edges.equalTo(headerWrapper);
    }];
    self.tableView.tableHeaderView = headerView;

D
Drunken Daddy

这是适用于 ios 12 中的 UITableViewController 的方法,

将 UIView 拖放到 TableView 中所有原型单元格上方的页眉和所有原型单元格下方的页脚。根据需要设置页眉和页脚。设置所有必需的约束。

现在使用以下扩展方法

public static class UITableVIewExtensions
{

    public static void MakeHeaderAutoDimension(this UITableView tableView)
    {
        if (tableView.TableHeaderView is UIView headerView) {
            var size = headerView.SystemLayoutSizeFittingSize(UIView.UILayoutFittingCompressedSize);
            if (headerView.Frame.Size.Height != size.Height) {
                var frame = headerView.Frame;
                frame.Height = size.Height;
                headerView.Frame = frame;
                tableView.TableHeaderView = headerView;
                tableView.LayoutIfNeeded();
            }
        }
    }

    public static void MakeFooterAutoDimension(this UITableView tableView)
    {
        if (tableView.TableFooterView is UIView footerView) {
            var size = footerView.SystemLayoutSizeFittingSize(UIView.UILayoutFittingCompressedSize);
            if (footerView.Frame.Size.Height != size.Height) {
                var frame = footerView.Frame;
                frame.Height = size.Height;
                footerView.Frame = frame;
                tableView.TableFooterView = footerView;
                tableView.LayoutIfNeeded();
            }
        }
    }
}

并在 UITableViewController 的子类的 ViewDidLayoutSubviews 中调用

public override void ViewDidLayoutSubviews()
{
    base.ViewDidLayoutSubviews();

    TableView.MakeHeaderAutoDimension();
    TableView.MakeFooterAutoDimension();
}

C
Community

我遇到了获取宽度 375pt 的问题,对我有用的唯一方法是重新布局 tableView 以获得正确的宽度。我也更喜欢自动布局而不是设置框架大小。

这是适合我的版本:

Xamarin.iOS

public static void AutoLayoutTableHeaderView(this UITableView tableView, UIView header)
{
    tableView.TableHeaderView = header;
    tableView.SetNeedsLayout();
    tableView.LayoutIfNeeded();
    header.WidthAnchor.ConstraintEqualTo(tableView.Bounds.Width).Active = true;       
    tableView.TableHeaderView = header;
}

Swift 版本(修改自 @Ben Packard 答案)

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
        self.setNeedsLayout()
        self.layoutIfNeeded()
        header.widthAnchor.widthAnchor.constraint(equalTo: self.bounds.width).isActive = true
        self.tableHeaderView = header
    }
}

O
Omar Albeik

我创建了 UITableView 的子类并将 UIStackView 用于页眉和页脚,它还可以设置多个视图。

https://github.com/omaralbeik/StackableTableView


L
Leszek Szary

在检查了其他曾经有效但不再有效的解决方案后,我为带有多行标签的标题视图创建了以下解决方案,该解决方案在 iOS 12、iOS 13、iOS 14、iOS 15 上对我来说在所有情况下都适用于设备旋转。与其将视图直接设置为表头视图,不如使用包装器视图,如果真实视图更改高度,它会自动更新其高度。将以下类添加到您的项目中:

class TableViewHeaderHelperView: UIView {
  private weak var headerView: UIView?
  private weak var tableView: UITableView?
  private var lastUpdatedHeight: CGFloat = 0

  init(headerView: UIView) {
    self.headerView = headerView
    super.init(frame: .zero)
    translatesAutoresizingMaskIntoConstraints = true
    addSubview(headerView)
  }

  required init?(coder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
  }

  func addToTableView(_ tableView: UITableView) {
    self.tableView = tableView
    tableView.tableHeaderView = self
    headerView?.widthAnchor.constraint(equalTo: tableView.widthAnchor).isActive = true
  }

  func removeFromTableView() {
    self.tableView?.tableHeaderView = nil
    self.tableView = nil
  }

  override func layoutSubviews() {
    super.layoutSubviews()
    refreshHeaderHeightIfNeeded()
  }

  func refreshHeaderHeightIfNeeded() {
    DispatchQueue.main.async {
      let headerViewSize = self.headerView?.bounds.size ?? .zero
      if headerViewSize.height != self.lastUpdatedHeight {
        self.lastUpdatedHeight = headerViewSize.height
        self.frame.size = headerViewSize
        self.tableView?.tableHeaderView = self
      }
    }
  }
}

然后你可以使用这个帮助类,如下所示:

let headerWrapperView = TableViewHeaderHelperView(headerView: yourRealHeaderView)
headerWrapperView.addToTableView(tableView)

或者,我在我的库 LSCategories 中使用了类似的方法,它提供了各种扩展,包括 UITableView 类的扩展,它允许使用一行代码在表头视图或页脚视图中设置自动布局视图,因此您也可以尝试反而。


r
redent84

接受的答案仅对具有单个部分的表有用。对于多部分 UITableView,只需确保您的标题继承自 UITableViewHeaderFooterView,您就可以了。

作为替代方案,只需将当前标题嵌入到 UITableViewHeaderFooterViewcontentView 中。与 UITableViewCell 完全一样。


问题是关于 tableHeaderView 而不是节标题。