自升级到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来过滤掉那些。我还没有找到另一种有效测试的方法。这很烦人。
答案 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进行解码,但在第一次布局后解析。是否可以在初始布局之前分配图像,并在第一次布局解决后将图像分配给图像视图?请发送样品以帮助我们进一步分析。 !谢谢"
目前我们已经为他们提供了样本项目。我的观察:
虽然Apple声称它将成为Xcode 8的标准,但视图将设置为1000x1000,但这种行为似乎并不一致。
希望这会有所帮助。
答案 2 :(得分:6)
我遇到了同样的问题,并通过参考上述建议尝试解决这个问题。
似乎它应该是Apple要解决的错误。我终于通过更改将XIB文档保存回Xcode 7.x格式并将我的UI恢复正常来找到解决方案。
在Apple发布修复程序之前,我不想把时间用在黑客上。
答案 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 = YES
和self.iconView.layer.cornerRadius = self.iconView.frame.size.width/2
给自己一个圆形图像。尝试从cellForRowAtIndexPath
和willDisplayCell
调用我的单元格配置方法,但都没有工作。
以下是有效的:
将分层代码移动到单元格的-layoutSubviews
方法中,如下所示:
-(void)layoutSubviews {
[super layoutSubviews];
self.iconView.clipsToBounds = YES;
self.iconView.layer.cornerRadius = self.iconView.frame.size.width/2;
}
在此之后,图像应正确加载,并且分层代码也应该有效。
答案 6 :(得分:0)