当前位置: 首页 > 知识库问答 >
问题:

在tableHeaderView中使用自动布局

濮阳旺
2023-03-14

我有一个包含多行< code>UILabel的< code>UIView子类。此视图使用自动布局。

我想将此视图设置为 UITable 视图的表标题视图(而不是节标题)。此标头的高度将取决于标签的文本,而标签文本又取决于设备的宽度。自动布局的场景应该很棒。

我已经找到并尝试了许多解决方案来使其工作,但无济于事。我尝试了一些事情:

    < li >在< code>layoutSubviews期间在每个标签上设置< code > preferred maxlayoutwidth < li >定义< code > intrinsicontentsize < li >尝试计算视图所需的大小,并手动设置< code>tableHeaderView的框架。 < li >设置页眉时向视图添加宽度约束 < li >一大堆其他事情

我遇到的一些失败:

  • 标签延伸到视图宽度之外,不包裹
  • 框架高度为0
  • 应用程序崩溃,出现异常执行后仍需要自动布局-layoutSubviews

解决方案(或解决方案,如果有必要)应该适用于iOS7和iOS8。请注意,所有这些都是以编程方式完成的。我已经设置了一个小示例项目,以防您想破解它来查看问题。我已经将我的工作重置为以下起点:

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;

我错过了什么?

共有3个答案

姚钊
2023-03-14

以下 UITableView 扩展解决了表的自动布局和定位的所有常见问题标题视图,而无需使用框架的旧版:

@implementation UITableView (AMHeaderView)

- (void)am_insertHeaderView:(UIView *)headerView
{
    self.tableHeaderView = headerView;

    NSLayoutConstraint *constraint = 
    [NSLayoutConstraint constraintWithItem: headerView
                                 attribute: NSLayoutAttributeWidth
                                 relatedBy: NSLayoutRelationEqual
                                    toItem: headerView.superview
                                 attribute: NSLayoutAttributeWidth
                                multiplier: 1.0
                                  constant: 0.0];
    [headerView.superview addConstraint:constraint];    
    [headerView layoutIfNeeded];

    NSArray *constraints = headerView.constraints;
    [headerView removeConstraints:constraints];

    UIView *layoutView = [UIView new];
    layoutView.translatesAutoresizingMaskIntoConstraints = NO;
    [headerView insertSubview:layoutView atIndex:0];

    [headerView addConstraints: [NSLayoutConstraint constraintsWithVisualFormat:@"|[view]|" options:0 metrics:nil views:@{@"view": layoutView}]];
    [headerView addConstraints: [NSLayoutConstraint constraintsWithVisualFormat:@"V:|[view]|" options:0 metrics:nil views:@{@"view": layoutView}]];

    [headerView addConstraints:constraints];

    self.tableHeaderView = headerView;
    [headerView layoutIfNeeded];
}

@end

“奇怪”步骤的解释:

>

  • 首先,我们将标题视图宽度与表视图宽度联系起来:它有助于旋转,并防止标题视图的X居中子视图的深左移。

    (魔术!)我们在HeaderView中插入假layoutView:此时我们强烈需要删除所有HeaderView约束,将layoutView扩展到HeaderView,然后恢复初始HeaderView约束。恰好约束的顺序有一定的意义!通过这种方式,我们得到了正确的HeaderView高度自动计算,也纠正了所有HeaderView子视图的
    X中心化。

    然后,我们只需要重新布局headerView,以获得正确的tableView
    高度计算和headerView在截面上方的定位,而不相交。

    附言。它也适用于iOS8。在一般情况下,这里不可能注释掉任何代码字符串。

  • 史骏祥
    2023-03-14

    对于仍在寻找解决方案的人来说,这是针对Swift 3的

    extension UITableView {
        // 1.
        func setTableHeaderView(headerView: UIView) {
            headerView.translatesAutoresizingMaskIntoConstraints = false
    
            self.tableHeaderView = headerView
    
            // ** Must setup AutoLayout after set tableHeaderView.
            headerView.widthAnchor.constraint(equalTo: self.widthAnchor).isActive = true
            headerView.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
            headerView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
        }
    
        // 2.
        func shouldUpdateHeaderViewFrame() -> Bool {
            guard let headerView = self.tableHeaderView else { return false }
            let oldSize = headerView.bounds.size        
            // Update the size
            headerView.layoutIfNeeded()
            let newSize = headerView.bounds.size
            return oldSize != newSize
        }
    }
    

    要使用:

    override func viewDidLoad() {
        ...
    
        // 1.
        self.tableView.setTableHeaderView(headerView: customView)
    }
    
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
    
        // 2. Reflect the latest size in tableHeaderView
        if self.tableView.shouldUpdateHeaderViewFrame() {
    
            // **This is where table view's content (tableHeaderView, section headers, cells) 
            // frames are updated to account for the new table header size.
            self.tableView.beginUpdates()
            self.tableView.endUpdates()
        }
    }
    

    要点是您应该让tableView以与表视图单元格相同的方式管理tableHeaderView框架。这是通过tableViewstarinUpdate/endUpdate完成的。

    问题是tableView在更新子帧时不关心AutoLayout。它使用当前tableHeaderView的大小来确定第一个单元格/节标题应该在哪里。

    1) 添加一个宽度约束,以便表标题视图在调用布局时使用此宽度。还要添加 CenterX 和顶部约束,以使其相对于表视图正确定位。

    2)为了让tableView知道tableHeaderView的最新大小,例如,当设备旋转时,在viewDidLayoutSubview中,我们可以在tableHeaderView上调用layoutIfNeed()。然后,如果大小发生变化,则调用startinUpdate/endUpdate。

    请注意,我没有在一个函数中包含starinUpdate/endUpdate,因为我们可能希望将调用推迟到以后。

    查看示例项目

    堵鸿光
    2023-03-14

    到目前为止,我自己的最佳答案包括设置一次< code>tableHeaderView并强制一次布局传递。这允许测量所需的大小,然后我用它来设置标题的框架。和< code>tableHeaderView一样,我必须再次设置它才能应用更改。

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

    对于多行标签,这也依赖于自定义视图(在本例中为消息视图)设置每个标签的首选MaxLayoutWidth

    - (void)layoutSubviews
    {
        [super layoutSubviews];
    
        self.titleLabel.preferredMaxLayoutWidth = CGRectGetWidth(self.titleLabel.frame);
        self.subtitleLabel.preferredMaxLayoutWidth = CGRectGetWidth(self.subtitleLabel.frame);
    }
    

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

    tableView.tableHeaderView = header
    header.setNeedsLayout()
    header.layoutIfNeeded()
    let height = header.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height
    var frame = header.frame
    frame.size.height = height
    header.frame = frame
    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
            header.setNeedsLayout()
            header.layoutIfNeeded()
            let height = header.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height
            var frame = header.frame
            frame.size.height = height
            header.frame = frame
            self.tableHeaderView = header
        }
    }
    

    用法:

    let header = SCAMessageView()
    header.titleLabel.text = "Warning"
    header.subtitleLabel.text = "Warning message here."
    tableView.setAndLayoutTableHeaderView(header)
    
     类似资料:
    • 我有一个自定义视图CustomLayout(蓝色,自定义UIView),该视图包含3个子视图,通过使用约束(布局锚定)垂直对齐,每个视图按照以下顺序对齐: 1视图:幻灯片(红色,自定义UIView) 我想,当我单击按钮(黄色)时,如果使用动画打开,SlideLayout(红色)的高度大小会增加,如果关闭,则会减少。和其他视图必须在动画期间更改位置,如果SlideLayout增加/减少,则父视图(C

    • 我正在深入研究自动布局,并试图了解如何水平居中多个视图,包括具有内在大小的视图(如UILabel或UIButton)。 例如,我有一个固定宽度为 320 的自定义视图。在其中,我有两个子视图,一个用户界面标签和一个用户界面图像视图。我希望 UI图像视图位于 UI标签右侧的 15 个像素,并且我希望 UI标签和 UI图像视图一起在超级视图中水平居中,但我不确定处理此问题的最佳方法。 我应该把它们放到

    • 在我的UITableViewCell中,我有两个UIView堆叠在一起。让我们称之为顶部和底部。 俯视图对superview具有前导、尾随和顶部约束。它的高度约束为20。 底部视图对superview具有前导、尾随和底部约束。它的高度约束为20。 顶部和底部具有垂直约束。 以编程方式“隐藏”底部视图(并使顶部视图接触超级视图的底部)的最简单方法是什么?我不想再创建任何约束,因为我确实在情节提要中设

    • 我花了两天时间尝试了混合和纯自动布局方法的各种解决方案,以实现在自动布局之前简单的滚动视图设置,现在它是正式的了——我一定太傻了。我主要是在故事板中设置这个(嗯,就是这样)。 所以我请求帮助。 视图树: 按钮应该水平滚动(从左到右,反之亦然)。有人能告诉我如何设置约束来使用纯自动布局实现这一点吗??? -- 我尝试了混合方法,如下所示: ...以及根据Apple的TechNote为< code>c

    • 我想使用将我的jar上传到Maven中央存储库,并在其他项目中作为依赖项使用它。我遵循了这个不错的教程,结果我的JAR被上传到Sonatype Nexus(见下面的屏幕截图)。 我的jar的0.1版本可用;线路 在我的从属项目的文件。我通过点击屏幕截图中的关闭和释放按钮发布了0.1版本。之后,我评论了我在这里创建的Jira票据,并被告知中央同步将每两小时运行一次。 我的理解是,如果我现在想发布我的

    •     你可能用过UIViewAutoresizingMask类型的一些常量,应用于当父视图改变尺寸的时候,相应UIView的frame也跟着更新的场景(通常用于横竖屏切换)。     在iOS6中,苹果介绍了自动排版机制,它和自动调整不同,并且更加复杂。     在Mac OS平台,CALayer有一个叫做layoutManager的属性可以通过CALayoutManager协议和CAConst