具有自动布局和部分重新加载的 UITableViewHeaderFooterView 子类不能很好地协同工作

UITableViewHeaderFooterView subclass with auto layout and section reloading won#39;t work well together(具有自动布局和部分重新加载的 UITableViewHeaderFooterView 子类不能很好地协同工作)

本文介绍了具有自动布局和部分重新加载的 UITableViewHeaderFooterView 子类不能很好地协同工作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试将自动布局合并到我的 UITableViewHeaderFooterView 子类中.该类非常基础,只有两个标签.这是完整的子类:

I am trying to incorporate auto layout into my UITableViewHeaderFooterView subclass. The class is pretty basic, just two labels. This is the complete subclass:

@implementation MBTableDetailStyleFooterView

static void MBTableDetailStyleFooterViewCommonSetup(MBTableDetailStyleFooterView *_self) {
    UILabel *rightLabel = [[UILabel alloc] init];
    _self.rightLabel = rightLabel;
    rightLabel.translatesAutoresizingMaskIntoConstraints = NO;
    [_self.contentView addSubview:rightLabel];

    UILabel *leftLabel = [[UILabel alloc] init];
    _self.leftLabel = leftLabel;
    leftLabel.translatesAutoresizingMaskIntoConstraints = NO;
    [_self.contentView addSubview:leftLabel];

    NSDictionary *views = NSDictionaryOfVariableBindings(rightLabel, leftLabel);

    NSArray *horizontalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:@"|-10-[leftLabel]-(>=10)-[rightLabel]-10-|" options:0 metrics:nil views:views];
    [_self.contentView addConstraints:horizontalConstraints];

    // center views vertically in super view
    NSLayoutConstraint *leftCenterYConstraint = [NSLayoutConstraint constraintWithItem:leftLabel attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:_self.contentView attribute:NSLayoutAttributeCenterY multiplier:1 constant:0];
    [_self.contentView addConstraint:leftCenterYConstraint];
    NSLayoutConstraint *rightCenterYConstraint = [NSLayoutConstraint constraintWithItem:rightLabel attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:_self.contentView attribute:NSLayoutAttributeCenterY multiplier:1 constant:0];
    [_self.contentView addConstraint:rightCenterYConstraint];

    // same height for both labels
    NSLayoutConstraint *sameHeightConstraint = [NSLayoutConstraint constraintWithItem:leftLabel attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:rightLabel attribute:NSLayoutAttributeHeight multiplier:1 constant:0];
    [_self.contentView addConstraint:sameHeightConstraint];
}

+ (BOOL)requiresConstraintBasedLayout {
    return YES;
}

- (id)initWithReuseIdentifier:(NSString *)reuseIdentifier {
    self = [super initWithReuseIdentifier:reuseIdentifier];
    MBTableDetailStyleFooterViewCommonSetup(self);
    return self;
}

@end

该类用作具有 2 个部分的 tableView 的第一部分的页脚.第一部分包含动态项目.第二部分只有一行,用于向第一部分添加新项目.

This class is used as a footer in the first section in a tableView with 2 sections. The first section contains dynamic items. The second section has only one row, which is used to add new items to the first section.

如果第一部分中没有项目,我会隐藏 footerView.因此,当我添加第一个新项目时,我必须重新加载该部分,以便显示 footerView.完成这一切的代码如下所示:

If there are no items in the first section I hide the footerView. So when I add the first new item I have to reload the section so the footerView appears. The code that does all this looks like this:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    [tableView deselectRowAtIndexPath:indexPath animated:YES];
    if (indexPath.section == 1) {
        BOOL sectionNeedsReload = ([self.data count] == 0); // reload section when no data (and therefor no footer) was present before the add
        [self.data addObject:[NSDate date]];
        NSIndexPath *newIndexPath = [NSIndexPath indexPathForRow:[self.data count]-1 inSection:0];
        if (sectionNeedsReload) {
            [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationAutomatic];
        }
        else {
            [self.tableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
        }
        [self configureFooter:(MBTableDetailStyleFooterView *)[tableView footerViewForSection:0] forSection:0];
    }
}

- (void)configureFooter:(MBTableDetailStyleFooterView *)footer forSection:(NSInteger)section {
    footer.leftLabel.text = @"Total";
    footer.rightLabel.text = [NSString stringWithFormat:@"%d", [self.data count]];
}

- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {
    MBTableDetailStyleFooterView *footer = nil;
    if (section == 0 && [self.data count]) {
        footer = [tableView dequeueReusableHeaderFooterViewWithIdentifier:@"Footer"];
        [self configureFooter:footer forSection:section];
    }
    return footer;
}

- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
    CGFloat height = 0;
    if (section == 0 && [self.data count]) {
        height = 20.0f;
    }
    return height;
}

没什么特别的.但是,只要在我的 tableView 上调用 reloadSections:withRowAnimations: 就会引发异常,因为它无法同时满足约束".

Nothing really fancy. However, as soon as reloadSections:withRowAnimations: is called on my tableView it throws an exception because it is "Unable to simultaneously satisfy constraints.".

tableView 在某处向我的页脚添加了翻译后的自动调整大小掩码约束.

Somewhere the tableView added a translated auto resizing mask constraint to my footer.

(
    "<NSLayoutConstraint:0x718a1f0 H:[UILabel:0x7189130]-(10)-|   (Names: '|':_UITableViewHeaderFooterContentView:0x7188df0 )>",
    "<NSLayoutConstraint:0x7189e30 H:[UILabel:0x71892c0]-(>=10)-[UILabel:0x7189130]>",
    "<NSLayoutConstraint:0x718a0a0 H:|-(10)-[UILabel:0x71892c0]   (Names: '|':_UITableViewHeaderFooterContentView:0x7188df0 )>",
    "<NSAutoresizingMaskLayoutConstraint:0x7591ab0 h=--& v=--& H:[_UITableViewHeaderFooterContentView:0x7188df0(0)]>"
)

当我将 reloadSections:withRowAnimations: 替换为对 reloadData 的调用时,不会添加自动调整大小的掩码约束,并且一切正常.

When I replace reloadSections:withRowAnimations: with a call to reloadData no autoresizing mask constraint is added and everything works fine.

有趣的是,异常告诉我它试图打破约束 <NSLayoutConstraint:0x7189e30 H:[UILabel:0x71892c0]-(>=10)-[UILabel:0x7189130]>

The interesting thing is that the exception tells me that it tries to break the constraint <NSLayoutConstraint:0x7189e30 H:[UILabel:0x71892c0]-(>=10)-[UILabel:0x7189130]>

但是当我在后续调用 configureFooter:forSection: 中记录约束时,这个约束仍然存在,但是自动调整大小的掩码约束消失了

But when I log the constraints in subsequent calls to configureFooter:forSection: this constraint still exists, but the auto resizing mask constraint is gone

约束正是我设置的那些.

The constraints are exactly those that I have set up.

(
    "<NSLayoutConstraint:0x718a0a0 H:|-(10)-[UILabel:0x71892c0]   (Names: '|':_UITableViewHeaderFooterContentView:0x7188df0 )>",
    "<NSLayoutConstraint:0x7189e30 H:[UILabel:0x71892c0]-(>=10)-[UILabel:0x7189130]>",
    "<NSLayoutConstraint:0x718a1f0 H:[UILabel:0x7189130]-(10)-|   (Names: '|':_UITableViewHeaderFooterContentView:0x7188df0 )>",
    "<NSLayoutConstraint:0x718a3f0 UILabel:0x71892c0.centerY == _UITableViewHeaderFooterContentView:0x7188df0.centerY>",
    "<NSLayoutConstraint:0x718a430 UILabel:0x7189130.centerY == _UITableViewHeaderFooterContentView:0x7188df0.centerY>",
    "<NSLayoutConstraint:0x718a4b0 UILabel:0x71892c0.height == UILabel:0x7189130.height>"
)

这个自动调整大小的蒙版约束来自哪里?它去哪儿了?

Where does this auto resizing mask constraint come from? Where does it go?

我错过了什么吗?我第一次研究自动布局是在一周前,所以这是完全可能的.

Am I missing something? The first time I looked into auto layout was like a week ago, so this is totally possible.

推荐答案

iOS 9 的工作解决方案

在您的 UITableViewHeaderFooterView 子类中放置以下代码.

Working Solution as of iOS 9

In your UITableViewHeaderFooterView subclass place the following code.

- (void)setFrame:(CGRect)frame {
    if (frame.size.width == 0) {
        return;
    }
    
    [super setFrame:frame];
}

说明:

tableview 处理标题视图的布局,它通过手动操作框架来实现(即使打开了自动布局也是如此).

The tableview handles the layout of the header views and it does so by manually manipulating the frames (yes even with autolayout turned on).

如果您检查页眉/页脚视图上的宽度约束,则有两个,一个包含在超级视图(表格视图)中的宽度,一个包含在页眉/页脚视图本身的宽度.

If you inspect the width constraints that are on the header/footer views there are two, one contained on the superview (the table view) for the width, and one contained in the header/footer view itself for the width.

包含在超级视图上的约束是一个 NSAutoresizingMaskLayoutConstraint,它是 tableview 依赖于框架来操作标题的赠品.在标题视图上将 translatesAutoresizingMaskIntoConstraints 切换为 NO 会有效地破坏其外观,这是另一个放弃.

The constraint contained on the super view is a NSAutoresizingMaskLayoutConstraint which is the giveaway that the tableview depends on frames to manipulate the headers. Switching the translatesAutoresizingMaskIntoConstraints to NO on the header view affectively breaks its appearance which is another give away.

似乎在某些情况下,这些页眉/页脚视图的框架宽度会更改为零,对我来说,这是插入行并重用页眉视图的时候.我的猜测是,在 UITableView 代码中的某处,即使您没有使用动画,也可以通过从零宽度开始帧来为动画做准备.

It appears that under some circumstances these header/footer views will have their frames change to a width of zero, for me it was when rows were inserted and the header views were reused. My guess is that somewhere in the UITableView code a preparation for an animation is made by starting the frame at zero width, even if you are not using an animation.

此解决方案应该运行良好,并且不会影响滚动性能.

This solution should work well and should not impact scroll performance.

这篇关于具有自动布局和部分重新加载的 UITableViewHeaderFooterView 子类不能很好地协同工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!

本文标题为:具有自动布局和部分重新加载的 UITableViewHeaderFooterView 子类不能很好地协同工作