表头视图自动布局问题
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】:
我以不同的方式重新实现了你迄今为止所拥有的东西。首先,我删除了嵌入UIImageView
和UILabel
的ZeroStateView.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】:这里有几件事让我感到困惑,可能会使事情复杂化。
-
为什么要对标签进行子类化?
这些约束实际上没有意义。
为什么标签有两个高度约束,一个常量和一个≥约束?将它们都删除,您也不需要。
另外,到容器底部的垂直空间也≥。为什么?就目前而言,您的自定义视图没有定义高度,这可能是拟合尺寸返回零高度的原因。
一旦你解决了这些问题,如果你有更好的运气,请告诉我。
【讨论】:
您好,感谢您指出错误。我更新了我的代码,但并没有做太多。我更新了示例项目。你可以玩它。以上是关于表头视图自动布局问题的主要内容,如果未能解决你的问题,请参考以下文章