表头视图自动布局问题

Posted

技术标签:

【中文标题】表头视图自动布局问题【英文标题】:Tableheader view autolayout issue 【发布时间】:2015-05-01 21:38:33 【问题描述】:

我正在加载自定义 UIView 的 XIB 文件作为视图控制器内 uitableview 的标题视图。

xib 文件的文件所有者是视图控制器。我在 uiviewcontroller 中声明了 viewcontroller 和 uiview 的接口。

ViewController.h

      @class ZeroStateView;

      @interface ViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>

      @property (nonatomic, weak) IBOutlet CustomUITableView *tableView;
      @property (nonatomic,strong) NSMutableArray *dataArray;
      @property (weak, nonatomic) IBOutlet ZeroStateView *zeroStateView;


      @end


     @interface ZeroStateView : UIView
     @property (weak, nonatomic) IBOutlet AutoLayoutLabel *titleLabel;
     @property (weak, nonatomic) IBOutlet UIImageView *titleIcon;

     - (void)updateView;

     @end

ViewController.m

    - (void)prepareHeaderViewForZeroState
            ZeroStateView *sizingView = [ZeroStateView new];
            [[[NSBundle mainBundle] loadNibNamed:@"ZeroStateView" owner:self options:nil] objectAtIndex:0];
            sizingView = self.zeroStateView;
           [sizingView updateView];

           self.tableView.tableHeaderView = sizingView;

           UIView *headerView = self.tableView.tableHeaderView;



           CGFloat height = [headerView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;

         CGRect headerFrame = headerView.frame;
         headerFrame.size.height = height;
         headerView.frame = headerFrame;


         self.tableView.tableHeaderView = headerView;
     

    @end

    @implementation ZeroStateView

    -(void)updateView
      self.titleIcon.alpha = 0.5;

      UIFontDescriptor *titleFontDescriptor = [UIFontDescriptor preferredFontDescriptorWithTextStyle:UIFontTextStyleSubheadline];

      self.titleLabel.text =  @"This is a long text message and its really long. This is a long text message and its really long. This is a long text message and its really long. This is a long text message and its really long. This is a long text message and its really long. This is a long text message and its really long. This is a long text message and its really long. This is a long text message and its really long. ";

     

AutolayoutLabel 类重写了以下方法:

   - (void)setBounds:(CGRect)bounds 
      [super setBounds:bounds];

      // For multiline label, preferredMaxLayoutWidth always matches the frame width
      if (self.numberOfLines == 0 && bounds.size.width != self.preferredMaxLayoutWidth) 
          self.preferredMaxLayoutWidth = self.bounds.size.width;
          [self setNeedsUpdateConstraints];
      
   

systemLayoutSizeFittingSize:UILayoutFittingCompressedSize 计算的高度返回 0。结果我得到以下视图作为表头视图:

当我添加如下实际高度时,uilabel 会溢出。我希望 uiview 会随着标签高度的增长而增长。

 headerFrame.size.height = self.sizingView.frame.size.height;

这是 UIViews 约束的屏幕截图:

我在这里想念什么?谁能指出我?

更新 我创建了一个示例 project 供你们检查到底是什么问题。

【问题讨论】:

对于它的价值,在第一句话中陈述你的问题总是有帮助的。 如果您在 2 行后为其分配了其他内容,则不应调用此 ZeroStateView *sizingView = [ZeroStateView new]; 什么时候给prepareHeaderViewForZeroState 打电话? 它在 viewwillappear 方法中调用 是的,期待视图的高度增加 【参考方案1】:

我以不同的方式重新实现了你迄今为止所拥有的东西。首先,我删除了嵌入UIImageViewUILabelZeroStateView.xib中的UIView。xib的基础已经是UIView,所以没有必要再添加一个UIView .

接下来我改变了周围的约束。我不记得具体更改了哪些约束,所以我将在此处列出它们:

    UIImageView 的约束条件 将中心 X 与 Superview 对齐 宽度 = 60 身高 = 56 到 Superview 的顶部空间 = 37 底部空间到UILabel = 31 UILabel 的约束条件 Superview 的剩余空间 = 15 Superview 的正确空间 = 15 到 Superview 的底部空间 = 45 到UIImageView 的顶部空间 = 31

进入代码。在ViewController.h 中,IBOutlet 据我所知没有做任何事情,所以我将该属性更改为@property (strong, nonatomic) ZeroStateView *zeroStateView;

现在重要的变化:ViewController.m。有两个 UITableViewDelegate 方法将替换 prepareHeaderViewForZeroState。在viewDidLoad 中,初始化zeroStateView 并将表视图的委托设置为self

- (void)viewDidLoad

    //...

    // Load the view
    self.zeroStateView = [[[NSBundle mainBundle] loadNibNamed:@"ZeroStateView" owner:self options:nil] firstObject];
    [self.zeroStateView updateView];
    self.zeroStateView.backgroundColor = [UIColor darkGrayColor];

    // Set self for table view delegate for -heightForHeaderInSection: and viewForHeaderInSection:
    self.dataTable.delegate = self;

现在我们是 table view 的委托,我们得到两个方法调用,这将允许我们自定义 header view 并适当地设置它的高度。

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section

    // This will set the header view to the zero state view we made in viewDidLoad
    return self.zeroStateView;


- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section

    // sizeToFit describes the size that the label can fit itself into.
    // So we are saying that the label can use the width of the view and infinite height.
    CGSize sizeToFit = CGSizeMake(self.view.frame.size.width, MAXFLOAT);

    // Then we ask the label to tell us how it can fit in that size.
    // The label will respond with the width of the view and however much height it needs
    // to display its text. This is the magic of how it grows vertically.
    CGSize size = [self.zeroStateView.titleLabel sizeThatFits:sizeToFit];

    // Add the height the label needs to the overall zero state view. This should be changed
    // to the height of the UIImage + the height we just got + the whitespace above and below
    // each of these views. You can handle that part.
    return self.zeroStateView.frame.size.height + size.height;

我将更改上传到 Dropbox here。

【讨论】:

【参考方案2】:

只是改变 tableHeaderView 的框架大小,不会改变它的大小。您必须再次将其设置为 tableView 以强制重新加载。

你必须在设置新的帧大小后再次调用self.tableView.tableHeaderView = headerView;

【讨论】:

但 systemLayoutSizeFittingSize 返回零 无论您遇到什么其他问题,您必须设置 self.tableView.tableHeaderView = headerView;再次或标题视图中的高度更改将不会影响表格视图。将其视为 ios 中的错误(我愿意),但您需要这样做。【参考方案3】:

当您从viewwillappear 调用prepareHeaderViewForZeroState 方法时。那时你的布局是不计算的。所以在调用systemLayoutSizeFittingSize方法计算单元格高度之前强制布局计算。这是调用systemLayoutSizeFittingSize之前需要编写的代码。

UIView *header = self.tableView.tableHeaderView;

    [header setNeedsLayout];
    [header layoutIfNeeded];

编辑:

您刚刚在ZeroStateView.xib 中留下了 1 个约束。那是Bottom Space to : Superview。请参考截图。

输出:

这里有Updated code

希望对您有所帮助。

【讨论】:

我更新了我的代码以添加你建议的内容,但这并没有起到多大作用。我更新了示例项目。你可以玩它。【参考方案4】:

我不确定,但您可以检查是否有 UIImage 和标签参考超级视图的前导、尾随、顶部和底部约束。

编辑: 在获取systemLayoutSize之前添加宽度约束

NSLayoutConstraint *tempWidthConstraint =
[NSLayoutConstraint constraintWithItem:self.contentView
                             attribute:NSLayoutAttributeWidth
                             relatedBy:NSLayoutRelationEqual
                                toItem:nil
                             attribute:NSLayoutAttributeNotAnAttribute
                            multiplier:1.0
                              constant:CGRectGetWidth(window.frame)];

widthConstraint.constant = tempWidthConstraint.constant;
[self.contentView addConstraint:tempWidthConstraint];

CGSize fittingSize = [self.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
CGFloat height = fittingSize.height +1;
[self.contentView removeConstraint:tempWidthConstraint];

【讨论】:

还为警报图标图像视图添加前导、尾随、顶部约束。 我希望图像视图居中。如果我设置尾随空格,则图像视图图标将从视图中消失 是的,您是否尝试过在获取 systemLayoutSize 之前为视图添加宽度约束,然后删除宽度约束【参考方案5】:

这里有几件事让我感到困惑,可能会使事情复杂化。

    为什么要对标签进行子类化? 这些约束实际上没有意义。 为什么标签有两个高度约束,一个常量和一个≥约束?将它们都删除,您也不需要。 另外,到容器底部的垂直空间也≥。为什么?就目前而言,您的自定义视图没有定义高度,这可能是拟合尺寸返回零高度的原因。

一旦你解决了这些问题,如果你有更好的运气,请告诉我。

【讨论】:

您好,感谢您指出错误。我更新了我的代码,但并没有做太多。我更新了示例项目。你可以玩它。

以上是关于表头视图自动布局问题的主要内容,如果未能解决你的问题,请参考以下文章

UITableViewController 表头添加一个带有自动布局约束的 UIButton

Xcode 6 - 另一个自动布局视图中的自动布局视图

旋转视图是不是与自动布局兼容?

以编程方式将自动布局添加到肋骨中添加的现有自动布局视图

滚动视图和 UI 组件的通用自动布局

使用自动布局删除和重新添加子视图