我有一个可重用的视图,我将在 UITableViewCell's
和 UICollectionViewCell
's 中使用,并且需要获取 tableView:heightForRowAtIndexPath:
.一些子视图在 layoutSubviews
内部发生了一些事情,所以我不能调用 systemLayoutForContentSize:
,而是我的计划是:
I have a reusable view I will be using in UITableViewCell's
and UICollectionViewCell
's, and need to get its dimensions for tableView:heightForRowAtIndexPath:
. Some subviews have stuff going on inside layoutSubviews
so I can't call systemLayoutForContentSize:
, instead my plan is to:
我遇到的问题是,如果不将视图插入视图并等待运行循环,我无法强制视图进行布局.
The problem I'm running into is that I cannot force the view to layout without inserting it into the view and waiting for the runloop.
我提炼了一个相当无聊的例子.这是 View.xib.子视图未对齐以突出显示视图从未被布置到基线位置:
I've distilled a rather boring example. Here's View.xib. The subview is misaligned to highlight that the view is never getting laid out even to the baseline position:
在我调用的主线程上:
UIView *view = [[UINib nibWithNibName:@"View" bundle:nil] instantiateWithOwner:nil options:nil][0];
NSLog(@"Subviews: %@", view.subviews);
view.frame = CGRectMake(0, 0, 200, 200);
[view updateConstraints];
[view layoutSubviews];
NSLog(@"Subviews: %@", view.subviews);
[self.view addSubview:view];
[view updateConstraints];
[view layoutSubviews];
NSLog(@"Subviews: %@", view.subviews);
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"Subviews: %@", view.subviews);
});
我得到以下视图信息:
1) "<UIView: 0x8bad9e0; frame = (50 50; 220 468); autoresize = W+H; layer = <CALayer: 0x8be0070>>"
2) "<UIView: 0x8bad9e0; frame = (50 50; 220 468); autoresize = W+H; layer = <CALayer: 0x8be0070>>"
3) "<UIView: 0x8bad9e0; frame = (50 50; 220 468); autoresize = W+H; layer = <CALayer: 0x8be0070>>"
4) "<UIView: 0x8bad9e0; frame = (0 100; 100 100); autoresize = W+H; layer = <CALayer: 0x8be0070>>"
1 表示全新的 NIB 视图尚未布局.2 表示 updateConstraints/layoutSubviews
什么也没做.3 表示将其添加到视图层次结构中没有任何作用.4 最后表示添加到视图层次结构和一个通过主循环布局视图.
1 indicates that the fresh-out-of-the-NIB view hasn't been laid out. 2 indicates that updateConstraints/layoutSubviews
did nothing. 3 indicates that adding it to the view hierarchy did nothing. 4 finally indicates that adding to the view hierarchy and one pass through the main-loop laid out the view.
我希望能够获得视图的尺寸,而不必让应用程序处理它或自己执行手动计算(字符串高度 + 约束 1 + 约束 2).
I would like to get to the point where I can get the view's dimensions without having to let the application handle it or perform manual calculations (string height + constraint1 + constraint2) on my own.
更新
我观察到,如果我将 view
放在 UIWindow
中,我会得到轻微的改进:
I've observed that if I place view
inside a UIWindow
I get a slight improvement:
UIView *view = [[UINib nibWithNibName:@"View" bundle:nil] instantiateWithOwner:nil options:nil][0];
UIWindow *window = [[UIWindow alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
view.frame = CGRectMake(0, 0, 200, 200);
[window addSubview:view];
[view layoutSubviews];
如果 view.translatesAutoresizingMaskIntoConstraints == YES
,视图的直接子视图将被布局,但不会布局它们的子视图.
If view.translatesAutoresizingMaskIntoConstraints == YES
, the view's immediate subviews will be laid out, but none of their children.
在您提到的基本情况下,您可以通过在容器视图上调用 setNeedsLayout
然后 layoutIfNeeded
来获得正确的大小.
In the basic case you mentioned, you can get the correct size by calling setNeedsLayout
and then layoutIfNeeded
on the container view.
来自 layoutIfNeeded
上的 UIView 类参考:
From the UIView class reference on layoutIfNeeded
:
使用此方法在绘制之前强制子视图的布局.从接收者开始,只要超级视图需要布局,此方法就会向上遍历视图层次结构.然后它把整棵树放在那个祖先的下面.因此,调用此方法可能会强制整个视图层次结构的布局.此 UIView 实现调用等效的 CALayer 方法,因此具有与 CALayer 相同的行为.
Use this method to force the layout of subviews before drawing. Starting with the receiver, this method traverses upward through the view hierarchy as long as superviews require layout. Then it lays out the entire tree beneath that ancestor. Therefore, calling this method can potentially force the layout of your entire view hierarchy. The UIView implementation of this calls the equivalent CALayer method and so has the same behavior as CALayer.
我认为整个视图层次结构"不适用于您的用例,因为指标视图可能没有超级视图.
I don't think the "entire view hierarchy" applies to your use case since the metrics view presumably wouldn't have a superview.
在示例空项目中,仅使用此代码,在调用 layoutIfNeeded
后确定正确的帧:
In a sample empty project, with just this code, the correct frame is determined after layoutIfNeeded
is called:
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, strong) UIView *redView;
@end
@implementation ViewController
@synthesize redView;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
redView = [[UIView alloc] initWithFrame:CGRectMake(50, 50, 220, 468)];
redView.backgroundColor = [UIColor redColor];
redView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:redView];
NSLog(@"Red View frame: %@", NSStringFromCGRect(redView.frame));
// outputs "Red View frame: {{50, 50}, {220, 468}}"
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[redView(==100)]" options:0 metrics:Nil views:NSDictionaryOfVariableBindings(redView)]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-100-[redView(==100)]" options:0 metrics:Nil views:NSDictionaryOfVariableBindings(redView)]];
NSLog(@"Red View frame: %@", NSStringFromCGRect(redView.frame));
// outputs "Red View frame: {{50, 50}, {220, 468}}"
[self.view setNeedsLayout];
NSLog(@"Red View frame: %@", NSStringFromCGRect(redView.frame));
// outputs "Red View frame: {{50, 50}, {220, 468}}"
[self.view layoutIfNeeded];
NSLog(@"Red View frame: %@", NSStringFromCGRect(redView.frame));
// outputs "Red View frame: {{0, 100}, {100, 100}}"
}
@end
稍微超出了您的问题范围,以下是您可能会遇到的其他一些问题,因为我已经在一个真实的应用程序中解决了这个确切的问题:
Slightly outside the scope of your question, here are some other issues you may run into, since I've worked on this exact problem in a real app:
heightForRowAtIndexPath:
中计算可能会很昂贵,因此您可能需要预先计算并缓存结果estimatedHeightForRowAtIndexPath:
以减少这些性能问题的影响heightForRowAtIndexPath:
might be expensive, so you may want to precalculate and cache the resultsestimatedHeightForRowAtIndexPath:
to reduce the impact of these performance issues回应:
一些子视图在 layoutSubviews
内部发生了一些事情,所以我不能调用 systemLayoutForContentSize:
Some subviews have stuff going on inside
layoutSubviews
so I can't callsystemLayoutForContentSize:
如果你实现了intrinsicContentSize
,你可以使用这个方法,它可以让视图为自己建议一个最佳尺寸.一种实现可能是:
You can use this method if you implement intrinsicContentSize
, which lets a view suggest an optimal size for itself. One implementation for this might be:
- (CGSize) intrinsicContentSize {
[self layoutSubviews];
return CGSizeMake(CGRectGetMaxX(self.bottomRightSubview.frame), CGRectGetMaxY(self.bottomRightSubview.frame));
}
这种简单的方法只有在您的 layoutSubviews
方法没有引用已经设置的大小(如 self.bounds
或 self.frame代码>).如果是这样,您可能需要执行以下操作:
This simple approach will only work if your layoutSubviews
method doesn't refer to an already-set size (like self.bounds
or self.frame
). If it does, you may need to do something like:
- (CGSize) intrinsicContentSize {
self.frame = CGRectMake(0, 0, 10000, 10000);
while ([self viewIsWayTooLarge] == YES) {
self.frame = CGRectInset(self.frame, 100, 100);
[self layoutSubviews];
}
return CGSizeMake(CGRectGetMaxX(self.bottomRightSubview.frame), CGRectGetMaxY(self.bottomRightSubview.frame));
}
显然,您需要调整这些值以匹配每个视图的特定布局,并且您可能需要调整性能.
Obviously, you'd need to adjust these values to match the particular layout of each view, and you may need to tune for performance.
最后,我要补充一点,部分原因是 成倍增加的成本使用自动布局,除了最简单的表格单元格之外,我通常最终使用手动高度计算.
Finally, I'll add that due in part to the exponentially increasing cost of using auto-layout, for all but the simplest table cells, I usually wind up using manual height calculation.
这篇关于创建基于自动布局的指标视图的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持跟版网!