表头视图自动布局问题

5

我将一个自定义UIView的XIB文件作为UITableView的header view加载到视图控制器中。

XIB文件的文件所有者是视图控制器。我在UIViewController中声明了视图控制器和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。因此,我得到以下视图作为表头视图:

Header view with height 0

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

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

TableHeaderView大小没有正确调整

下面是该UIView约束的截屏:

带有约束的ZeroState视图XIB文件

我错过了什么?有人能指出来吗?

更新 我创建了一个项目供大家检查问题所在。


说句实话,将你的问题放在第一句总是有帮助的。 - Chris Conover
如果您在两行代码后将其他内容分配给它,就不应该调用 ZeroStateView *sizingView = [ZeroStateView new]; - pteofil
你什么时候调用 prepareHeaderViewForZeroState 函数? - pteofil
它被称为viewWillAppear方法。 - Siddharthan Asokan
好的...我有同样的问题...请检查这个问题...如果它和你相关,那么我有一个解决方案给你...http://stackoverflow.com/questions/29047589/multiline-uilabel-with-uiscrollview-using-autolayout - Bhavin Bhadani
显示剩余2条评论
5个回答

2
我用不同的方式重新实现了你所做的部分。首先,我移除了ZeroStateView.xib中包含UIImageViewUILabelUIView。因为xib的基础部分已经是一个UIView,所以添加另一个UIView是不必要的。

接下来,我改变了约束条件。我记不清楚具体改变了哪些约束条件,所以在这里只列出它们:

  1. UIImageView的约束条件
    • 水平居中对齐至父视图
    • 宽度=60
    • 高度=56
    • 距离父视图顶部的距离=37
    • 距离UILabel的底部的距离=31
  2. UILabel的约束条件
    • 距离父视图左侧的距离=15
    • 距离父视图右侧的距离=15
    • 距离父视图底部的距离=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;
}

现在我们是表视图的代理,我们可以获得两个方法调用,以便自定义表头视图并适当设置其高度。

- (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,在这里可以查看。


1
当您从viewwillappear调用prepareHeaderViewForZeroState方法时。此时您的布局尚未计算。因此,在调用systemLayoutSizeFittingSize方法计算单元格高度之前,需要强制进行布局计算。以下是在调用systemLayoutSizeFittingSize之前需要编写的代码。
UIView *header = self.tableView.tableHeaderView;

    [header setNeedsLayout];
    [header layoutIfNeeded];

编辑:

您只在ZeroStateView.xib中留下了一个约束条件。那就是底部间距 : 父视图。请参考截图。

enter image description here

输出:

enter image description here

在这里,您有更新的代码

希望这对您有所帮助。


我更新了我的代码,加入了你建议的内容,但效果不大。我也更新了示例项目,你可以在方便的时候试用一下。 - Siddharthan Asokan

1

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

在设置新的框架大小后,您必须再次调用self.tableView.tableHeaderView = headerView;


但是 systemLayoutSizeFittingSize 返回零。 - Siddharthan Asokan
无论你还有什么其他问题,你“必须”再次设置self.tableView.tableHeaderView = headerView;否则,头视图高度的更改将无法影响表视图。虽然我认为这是iOS中的一个错误(但实际上并非如此),但你必须这样做。 - gnasher729

0

我不确定,但你可以检查一下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];

还要为警报图标图像视图添加前导、尾随和顶部约束。 - A C
我希望ImageView居中显示。如果我设置了一个尾部空间,ImageView的图标会从视图中消失。 - Siddharthan Asokan
是的,好的,你尝试在获取系统布局大小之前为视图添加宽度约束,然后再删除宽度约束。 - A C

0

这里有几个让我感到困惑的问题,可能会使事情变得复杂。

  1. 为什么要将标签定义为子类?
  2. 约束条件并不是很合理。
    • 为什么标签有两个高度约束条件,一个常量和一个≥约束条件?将它们都去掉,你都不需要。
    • 此外,您的自定义视图底部的垂直空间也是≥。为什么?就目前而言,您的自定义视图没有定义高度,这可能是拟合大小返回零高度的原因。

解决了这些问题后,请让我知道您是否有更好的运气。


嗨,感谢指出错误。我更新了代码,但效果不大。我还更新了示例项目,你可以在方便的时候试用一下。 - Siddharthan Asokan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接