ios10:viewDidLoad框架宽度/高度未正确初始化

时间:2016-09-13 17:05:56

标签: objective-c autolayout frame ios10 xcode8

自升级到XCode8 GM和ios10以来,通过Interface Builder创建的所有视图都没有正确初始化,直到比预期要晚得多。这意味着在viewDidLoad,cellForRowAtIndexPath,viewWillAppear等中,每个视图的帧大小都设置为{1000,1000}。在某些时候他们似乎是正确的,但它为时已晚。

遇到的第一个问题是角落的共同圆角失败:

view.layer.cornerRadius = view.frame.size.width/2

对于依赖于帧大小在代码中进行计算的任何事情,都会出现更多问题。

cellForRowAtIndexPath 

对于cellForRowAtIndexPath,初始表显示时帧大小失败,但滚动后它可以正常工作。 willDisplayCell:forRowAtIndexPath也没有正确的帧大小。

我已经对一些值进行了硬编码,但显然这是非常糟糕的代码实践,在我的项目中也是如此。

是否有方法或地点可以获得正确的帧尺寸?

修改

我发现使用高度/宽度约束而不是帧宽高度更可靠。这可能会增加需要大量新IBOutlet的开销,以链接项目的高度/宽度约束。

现在我已经创建了一个UIView类别,它允许我直接访问View的高度/宽度约束而不需要IBOutlets。对于最小的使用,小循环不应该是一个大问题。对于没有明显创建宽度/高度约束的IB项目,不能保证结果。可能最好为常数返回0,或者更糟。此外,如果您没有高度/宽度约束,并且您的视图基于前导/尾随约束动态调整大小,则无法使用。

-viewDidLoad似乎具有正确的帧大小,但如果您在此处进行修改,通常会导致UI的可视化更改。

的UIView + WidthHeightConstraints.h

@interface UIView (WidthHeightConstraints)

-(NSLayoutConstraint*)widthConstraint;
-(NSLayoutConstraint*)heightConstraint;
-(NSLayoutConstraint*)constraintForAttribute:(NSLayoutAttribute)attribute;

@end

的UIView + WidthHeightConstraints.m

#import "UIView+WidthHeightConstraints.h"

@implementation UIView (WidthHeightConstraints)

-(NSLayoutConstraint*)widthConstraint{
    return [self constraintForAttribute:NSLayoutAttributeWidth];
}
-(NSLayoutConstraint*)heightConstraint {
    return [self constraintForAttribute:NSLayoutAttributeHeight];
}
-(NSLayoutConstraint*)constraintForAttribute:(NSLayoutAttribute)attribute {
    NSLayoutConstraint *targetConstraint = nil;
    for (NSLayoutConstraint *constraint in self.constraints) {
        if (constraint.firstAttribute == attribute) {
            targetConstraint = constraint;
            break;
        }
    }
    return targetConstraint;
}

@end

编辑2

上述类别证明只是部分有效。主要是因为ios似乎自动添加一些额外的高度/宽度约束重复项,类型为NSContentSizeLayoutConstraint,实际上与普通约束的大小不同。 NSContentSizeLayoutConstraint也是一个私有类,所以我不能做isKindOfClass来过滤掉那些。我还没有找到另一种有效测试的方法。这很烦人。

7 个答案:

答案 0 :(得分:29)

您描述的最常见问题仅出现在iOS 10中,可以通过添加此行来解决(如有必要):

self.view.layoutIfNeeded()

在代码之上,负责更改约束,layer.cornerRadius等。

将与框架/图层相关的代码放入viewDidLayoutSubviews()方法:

override func viewDidLayoutSubviews() {

    super.viewDidLayoutSubviews()
    view.layer.cornerRadius = self.myView.frame.size.width/2
    view.clipsToBounds = true

    ... etc
}

答案 1 :(得分:16)

我们为类似的问题创建了一个雷达(28342777(标记为重复28221021但是打开)),我们得到的回复如下:

"感谢您报告此问题。我们能获得有关个人资料图片视图的更多信息吗?在Xcode 8中,完全约束,非错位视图不再保存帧以最小化差异并支持自动更新IB中的帧。在运行时,这些视图使用占位符大小1000x1000进行解码,但在第一次布局后解析。是否可以在初始布局之前分配图像,并在第一次布局解决后将图像分配给图像视图?请发送样品以帮助我们进一步分析。 !谢谢"

目前我们已经为他们提供了样本项目。我的观察:

  • 我们过去常常遇到从Xcode 7.x转换为Xcode 8.x的XIB的问题
  • 如果我们故意破坏XIB中的约束,那么viewDidLoad将获得预期的高度和宽度,而不是1000x1000。
  • 对于我们来说,它是一个UIImageView,我们在其上应用了一些分层,使其成为圆形并使用masksToBounds。如果我们设置maskToBounds = NO那么我们一切都工作正常。

虽然Apple声称它将成为Xcode 8的标准,但视图将设置为1000x1000,但这种行为似乎并不一致。

希望这会有所帮助。

答案 2 :(得分:6)

我遇到了同样的问题,并通过参考上述建议尝试解决这个问题。

似乎它应该是Apple要解决的错误。我终于通过更改将XIB文档保存回Xcode 7.x格式并将我的UI恢复正常来找到解决方案。

在Apple发布修复程序之前,我不想把时间用在黑客上。

enter image description here enter image description here

答案 3 :(得分:2)

这样做:

- (NSLayoutConstraint*)widthConstraint{
    return [self constraintForAttribute:NSLayoutAttributeWidth];
}

- (NSLayoutConstraint*)heightConstraint {
    return [self constraintForAttribute:NSLayoutAttributeHeight];
}

- (NSLayoutConstraint*)constraintForAttribute:(NSLayoutAttribute)attribute {
    NSLayoutConstraint *targetConstraint = nil;
    for (NSLayoutConstraint *constraint in self.constraints) {
        //NSLog(@"constraint: %@", constraint);
        if (![constraint isKindOfClass:NSClassFromString(@"NSContentSizeLayoutConstraint")]) {
            if (constraint.firstAttribute == attribute) {
                targetConstraint = constraint;
                break;
            }
        }
    }
    return targetConstraint;
}

答案 4 :(得分:2)

你永远不应该依赖于视图布局的时间。如果之前对你有用,那么出于纯粹的运气。 UIKit对此几乎没有任何保证。如果您依赖的东西采用视图大小,那么正确的做法是在该视图中覆盖layoutSubviews并在那里调整您的内容。

即使您的视图在屏幕上完全呈现,仍然有很多条件可能导致视图的大小发生变化。例如:双倍高度状态栏,iPad上的多任务处理,设备旋转,仅举几例。因此,在特定时间点进行与帧相关的布局更改永远不是一个好主意。

答案 5 :(得分:2)

我遇到了完全相同的问题。我有自定义的UITableViewCell子类,并使用clipsToBounds = YESself.iconView.layer.cornerRadius = self.iconView.frame.size.width/2给自己一个圆形图像。尝试从cellForRowAtIndexPathwillDisplayCell调用我的单元格配置方法,但都没有工作。

以下是有效的:
将分层代码移动到单元格的-layoutSubviews方法中,如下所示:

-(void)layoutSubviews {
    [super layoutSubviews];
    self.iconView.clipsToBounds = YES;
    self.iconView.layer.cornerRadius = self.iconView.frame.size.width/2;
} 

在此之后,图像应正确加载,并且分层代码也应该有效。

答案 6 :(得分:0)

自动布局框中仅更新框架enter image description here