ios – 创建基于autolayout的指标视图

我有一个可重用的视图,我将在UITableViewCell和UICollectionViewCell中使用,并且需要获取tableView的尺寸:heightForRowAtIndexPath:.一些子视图在layoutSubviews中有内容,所以我不能调用systemLayoutForContentSize:而我的计划是:

>实例化指标视图.
>设置尺寸以包括所需的宽度.
>用数据填充它.
>更新约束/布局子视图.
>抓取视图的高度或内部“大小调整”视图.

我遇到的问题是,我不能强制视图布局而不将其插入视图并等待runloop.

我提炼了一个相当无聊的例子.这是View.xib.子视图未对齐以突出显示视图永远不会布局到基线位置:

在主线程上我打电话:

UIView *view = [[UINib nibWithNibName:@"View" bundle:nil] instantiateWithOwner:nil options:nil][0];

NSLog(@"Subviews: %@",view.subviews);
view.frame = CGRectMake(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约束2).

更新

我观察到,如果我在UIWindow中放置视图,我会得到一些改进:

UIView *view = [[UINib nibWithNibName:@"View" bundle:nil] instantiateWithOwner:nil options:nil][0];
UIWindow *window = [[UIWindow alloc] initWithFrame:CGRectMake(0,200)];
view.frame = CGRectMake(0,200);
[window addSubview:view];
[view layoutSubviews];

如果view.translatesAutoresizingMaskIntoConstraints == YES,则会显示视图的直接子视图,但不会显示其子视图.

解决方法

Autolayout问题

在您提到的基本情况中,您可以通过在容器视图上调用setNeedsLayout然后调用layoutIfNeeded来获得正确的大小.

UIView class reference on layoutIfNeeded

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.

我不认为“整个视图层次结构”适用于您的用例,因为度量标准视图可能不具有超级视图.

示例代码

在示例空项目中,仅使用此代码,在调用layoutIfNeeded之后确定正确的帧:

#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: %@",468}}"

    [self.view setNeedsLayout];

    NSLog(@"Red View frame: %@",468}}"

    [self.view layoutIfNeeded];

    NSLog(@"Red View frame: %@",NsstringFromCGRect(redView.frame));
    // outputs "Red View frame: {{0,100},{100,100}}"
}

@end

其他考虑因素

稍微超出了您的问题的范围,这里有一些您可能遇到的其他问题,因为我在一个真实的应用程序中处理了这个确切的问题:

>在heightForRowAtIndexPath中计算:可能很昂贵,因此您可能需要预先计算并缓存结果
>预先计算应该在后台线程上完成,但UIView布局不能正常工作,除非它在主线程上完成
>您绝对应该实现estimatedHeightForRowAtIndexPath:以减少这些性能问题的影响

使用intrinsicContentSize

回应:

Some subviews have stuff going on inside layoutSubviews so I can’t call systemLayoutForContentSize:

如果实现intrinsicContentSize,则可以使用此方法,该视图允许视图为自身建议最佳大小.一个实现可能是:

- (CGSize) intrinsicContentSize {
    [self layoutSubviews];
    return CGSizeMake(CGRectGetMaxX(self.bottomrightSubview.frame),CGRectGetMaxY(self.bottomrightSubview.frame));
}

这种简单的方法只有在layoutSubviews方法不引用已设置的大小(如self.bounds或self.frame)时才有效.如果是这样,您可能需要执行以下操作:

- (CGSize) intrinsicContentSize {
    self.frame = CGRectMake(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));
}

显然,您需要调整这些值以匹配每个视图的特定布局,并且您可能需要调整性能.

最后,我将补充一点,部分原因是exponentially increasing cost of using auto-layout,除了最简单的表格单元格外,我通常最终使用手动高度计算.

相关文章

当我们远离最新的 iOS 16 更新版本时,我们听到了困扰 Apple...
欧版/美版 特别说一下,美版选错了 可能会永久丧失4G,不过只...
一般在接外包的时候, 通常第三方需要安装你的app进行测...
前言为了让更多的人永远记住12月13日,各大厂都在这一天将应...