Creating an autolayout-based metrics view(创建基于自动布局的指标视图)
问题描述
我有一个可重用的视图,我将在 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
来获得正确的大小.
The Autolayout Question
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
其他注意事项
稍微超出了您的问题范围,以下是您可能会遇到的其他一些问题,因为我已经在一个真实的应用程序中解决了这个确切的问题:
Additional Considerations
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:
中计算可能会很昂贵,因此您可能需要预先计算并缓存结果 - 预计算应该在后台线程上完成,但 UIView 布局不能很好地工作,除非它在主线程上完成
- 您绝对应该实施
estimatedHeightForRowAtIndexPath:
以减少这些性能问题的影响
- Calculating this in
heightForRowAtIndexPath:
might be expensive, so you may want to precalculate and cache the results - Precalculation should be done on a background thread, but UIView layout doesn't work well unless it's done on the main thread
- You should definitely implement
estimatedHeightForRowAtIndexPath:
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.
这篇关于创建基于自动布局的指标视图的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:创建基于自动布局的指标视图
基础教程推荐
- Kivy Buildozer 无法构建 apk,命令失败:./distribute.sh -m “kivy"d 2022-01-01
- 如何让对象对 Cocos2D 中的触摸做出反应? 2022-01-01
- UIWebView 委托方法 shouldStartLoadWithRequest:在 WKWebView 中等效? 2022-01-01
- 如何在 iPhone 上显示来自 API 的 HTML 文本? 2022-01-01
- 当从同一个组件调用时,两个 IBAction 触发的顺序是什么? 2022-01-01
- android 应用程序已发布,但在 google play 中找不到 2022-01-01
- 在 gmail 中为 ios 应用程序检索朋友的朋友 2022-01-01
- Android:对话框关闭而不调用关闭 2022-01-01
- 如何在没有IB的情况下将2个按钮添加到右侧的UINavigationbar? 2022-01-01
- 如何在 UIImageView 中异步加载图像? 2022-01-01