【问题标题】:Is it possible to use AutoLayout with UITableView's tableHeaderView?是否可以将 AutoLayout 与 UITableView 的 tableHeaderView 一起使用?
【发布时间】:2013-05-04 12:33:42
【问题描述】:

自从我发现AutoLayout 之后,我到处都在使用它,现在我正在尝试将它与tableHeaderView 一起使用。

我在UIView 中添加了一个subclass,添加了我想要的所有内容(标签等...),然后我将这个CustomView 添加到UITableView'tableHeaderView

除了UITableView 总是显示上方 CustomView 之外,一切正常,上方 我的意思是CustomView下方 UITableView 所以看不到!

似乎无论我做什么,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

标签: iphone ios ipad uitableview autolayout


【解决方案1】:

我问并回答了一个类似的问题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
【解决方案2】:

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

 "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)。打开翻译可以防止错误 - 我怀疑 UITableView 从 7.1 开始不会尝试自动布局其标题视图,而是需要预先设置框架的东西。
【解决方案3】:

我在这里看到很多方法做了很多不必要的事情,但是你不需要那么多在标题视图中使用自动布局。你只需要创建你的 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 文件中为整个视图设置高度限制。
【解决方案4】:

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

- (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 在自动布局方面有点问题。有一些解决方法,比如这个。但是自从我写了这篇文章后,我在stackoverflow.com/a/21099430/127493 找到了一个更好、更干净的解决方案,方法是在viewDidLayoutSubviews 中调用它的- (void)sizeHeaderToFit
【解决方案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
          }
  }

【讨论】:

  • 它有效。为所有 tableheaderview 子视图提供适当的自动布局约束。如果你错过了一个约束,它将不起作用。
【解决方案6】:

将此解决方案 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 的底部。
【解决方案7】:

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

【讨论】:

    【解决方案8】:

    以下内容对我有用。

    1. 使用普通的旧 UIView 作为标题视图。
    2. UIView 添加子视图
    3. 在子视图上使用自动布局

    我看到的主要好处是限制帧计算。 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))
    }
    

    【讨论】:

      【解决方案9】:

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

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

      - (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 中为我循环。
      【解决方案10】:

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

      【讨论】:

        【解决方案11】:

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

        在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
        【解决方案12】:

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

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

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

        【讨论】:

        • 不错。自动调整蒙版被严重低估了。
        【解决方案13】:

        我的表头视图是一个 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
        }
        

        【讨论】:

          【解决方案14】:

          我的 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,重新设置其框架,然后将其添加回来。
          【解决方案15】:

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

          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
          

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

          【讨论】:

            【解决方案16】:

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

            首先,我的视图层次结构是这样的:

            1. 表格视图
              1. 查看tableHeaderView
                1. 使用名为 headerView 的插座查看

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

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

            1. 到容器的顶部空间:0
            2. 到容器的前导空间:0
            3. 到容器的尾随空格: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;
            }
            

            【讨论】:

              【解决方案17】:

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

              code show

              【讨论】:

                【解决方案18】:

                分享我的方法。

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

                【讨论】:

                  【解决方案19】:

                  一个旧帖子。不过是个好帖子。这是我的 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)
                  }
                  

                  【讨论】:

                    【解决方案20】:

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

                    首先,您需要一个小的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 的子视图实现的说明:

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

                    2. 覆盖requiresConstraintBasedLayout并返回true

                    .

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

                    【讨论】:

                      【解决方案21】:

                      对于 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

                      【讨论】:

                        【解决方案22】:

                        您可以在 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)
                            }
                          }
                        

                        【讨论】:

                          【解决方案23】:

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

                          需要在之前设置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;
                          

                          }

                          可以找到所有细节和代码sn-ps here

                          【讨论】:

                            【解决方案24】:

                            我想出了一个解决方法。将您的自动布局写入 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;
                            

                            【讨论】:

                              【解决方案25】:

                              这是适用于 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();
                              }
                              

                              【讨论】:

                                【解决方案26】:

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

                                这是适合我的版本:

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

                                【讨论】:

                                  【解决方案27】:

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

                                  https://github.com/omaralbeik/StackableTableView

                                  【讨论】:

                                    【解决方案28】:

                                    在检查了过去可以工作但不再工作的各种解决方案后,我发现了以下带有多行标签的标题视图解决方案,它在 iOS 12iOS 13iOS 14 在所有情况下也可以使用设备旋转:

                                    • 像往常一样使用自动布局创建标题视图(这样自动布局可以根据使用的约束计算此视图的高度)。如果您在代码中创建它,请确保设置 translatesAutoresizingMaskIntoConstraints = false。
                                    • 将标题视图分配给 tableView 的 tableHeaderView 属性。
                                    • 设置约束以匹配标题视图的宽度与表格视图的宽度。
                                    • 在 viewDidLayoutSubviews 中再次使用相同的视图设置 tableHeaderView,但前提是它之前尚未设置为相同的高度(以防止 viewDidLayoutSubviews 调用的递归循环)。要使其在所有情况下以及在各种 iOS 版本上正常工作,您需要在 DispatchQueue.main.async 中执行此操作(没有它仅在某些情况下有效)。
                                    • 如果您在视图中更改了可以更改其高度的内容(例如多行标签中的文本),那么您还需要执行与在 viewDidLayoutSubviews 中所做的相同的操作,以使其在所有情况下都能正常工作(同样没有它也能正常工作)仅在某些情况下)。

                                    示例代码:

                                    class TestViewController: UITableViewController {
                                        let headerView = TestHeaderView() // has translatesAutoresizingMaskIntoConstraints = false
                                        var lastSetHeaderViewHeight: CGFloat?
                                        override func viewDidLayoutSubviews() {
                                            super.viewDidLayoutSubviews()
                                            reloadHeaderViewIfNeeded()
                                        }
                                        func reloadHeaderViewIfNeeded() {
                                          DispatchQueue.main.async {
                                            if self.lastSetHeaderViewHeight != self.headerView.bounds.size.height {
                                                self.lastSetHeaderViewHeight = self.headerView.bounds.size.height
                                                self.tableView.tableHeaderView = self.headerView
                                            }
                                          }
                                        }
                                        override func viewDidLoad() {
                                            super.viewDidLoad()
                                            tableView.tableHeaderView = headerView
                                            headerView.widthAnchor.constraint(equalTo: tableView.widthAnchor, constant: 0).isActive = true
                                            // ...
                                        }
                                        func somethingThatCanChangeHeightOfHeaderView() {
                                            // ...
                                            reloadHeaderViewIfNeeded()
                                        }
                                        // ...
                                    }
                                    

                                    【讨论】:

                                      【解决方案29】:

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

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

                                      【讨论】:

                                      • 问题是关于tableHeaderView而不是部分标题。
                                      猜你喜欢
                                      • 2021-11-12
                                      • 2016-04-01
                                      • 2011-01-20
                                      • 2018-08-11
                                      • 2021-08-05
                                      • 2019-03-18
                                      • 2014-03-15
                                      • 2021-03-31
                                      • 2020-12-22
                                      相关资源
                                      最近更新 更多