为什么iOS自动布局会导致视网膜前显示出现明显的舍入错误(包括单元测试)
我现在很难理解为什么下面的单元测试在iPad2上失败。自动布局相对于两个布局约束所需的精确居中,似乎稍微(0.5点)错误定位为什么iOS自动布局会导致视网膜前显示出现明显的舍入错误(包括单元测试),ios,cocoa-touch,autolayout,retina-display,Ios,Cocoa Touch,Autolayout,Retina Display,我现在很难理解为什么下面的单元测试在iPad2上失败。自动布局相对于两个布局约束所需的精确居中,似乎稍微(0.5点)错误定位view内部superview。特别奇怪的是,关键测试(但最后一个断言)通过了iPhone 5,因此明显的舍入误差只影响一个(iOS 6)平台。这是怎么回事 更新1我已更改代码,以确保两个框架在宽度和高度方面都受到足够的约束,即使翻译自动调整大小GMaskintoConstraints为否,这可能是一种相关的补救措施。然而,这显然没有改变情况 #import "BugTes
view
内部superview
。特别奇怪的是,关键测试(但最后一个断言)通过了iPhone 5,因此明显的舍入误差只影响一个(iOS 6)平台。这是怎么回事
更新1我已更改代码,以确保两个框架在宽度和高度方面都受到足够的约束,即使翻译自动调整大小GMaskintoConstraints
为否
,这可能是一种相关的补救措施。然而,这显然没有改变情况
#import "BugTests.h"
@implementation BugTests
- (void)testCenteredLayout {
UIView *superview = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 768, 88)];
superview.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin;
superview.translatesAutoresizingMaskIntoConstraints = YES;
UILabel *view = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
view.text = @"Single Round against iPad.";
view.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin;
view.translatesAutoresizingMaskIntoConstraints = NO;
[view addConstraint:[NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:0.0 constant:206.0]];
[view addConstraint:[NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:0.0 constant: 21.0]];
[superview addSubview:view];
[superview addConstraint:[NSLayoutConstraint constraintWithItem:superview attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:view attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.0]];
[superview addConstraint:[NSLayoutConstraint constraintWithItem:superview attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:view attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0.0]];
STAssertEquals(superview.center, CGPointMake(384, 44), nil); // succeeds
STAssertEquals(view.center, CGPointMake( 0, 0), nil); // succeeds
[superview setNeedsLayout];
[superview layoutIfNeeded];
STAssertTrue(!superview.hasAmbiguousLayout, nil);
STAssertEquals(superview.frame.size, CGSizeMake(768, 88), nil); // succeeds
STAssertEquals(view.frame.size, CGSizeMake(206, 21), nil); // succeeds
STAssertEquals(superview.center, CGPointMake(384, 44), nil); // succeeds
STAssertEquals(superview.center, view.center, nil); // fails: why?
STAssertEquals(view.center, CGPointMake(384, 44.5), nil); // succeeds: why?
}
@end
更新2我在第二次单元测试中分离出了(显然)相同问题的另一个实例。这一次,它涉及一个顶部(而非中心)约束,而这一次,分数点坐标似乎是触发点。(该测试在视网膜前设备上也获得了成功,例如具有y=951
,即奇点坐标)我检查了各种模拟器配置(在我的物理iPad 2和iPhone 5旁边)的出现似乎确实与没有Ratina显示器有关。(再次感谢@ArkadiuszHolko的领导。)
从这些测试中我目前的感觉是,如果需要在视网膜前显示器上进行精确的自动布局,就必须避免奇数高度和分数y坐标。但是为什么呢
- (void)testNonRetinaAutoLayoutProblem2 {
UIView *superview = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 768, 1004)];
superview.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleBottomMargin;
superview.translatesAutoresizingMaskIntoConstraints = YES;
CGFloat y = 950.5; // see e.g. pageControlTopConstraint
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
view.translatesAutoresizingMaskIntoConstraints = NO;
[superview addConstraint:[NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:superview attribute:NSLayoutAttributeLeading multiplier:1.0 constant:0.0]];
[superview addConstraint:[NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:superview attribute:NSLayoutAttributeTrailing multiplier:1.0 constant:0.0]];
[superview addConstraint:[NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:superview attribute:NSLayoutAttributeTop multiplier:1.0 constant:y]];
[superview addConstraint:[NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:0.0 constant:8]];
[superview addSubview:view];
[superview setNeedsLayout];
[superview layoutIfNeeded];
STAssertTrue(!superview.hasAmbiguousLayout, nil);
STAssertTrue(!view.hasAmbiguousLayout, nil);
STAssertEquals(superview.frame, CGRectMake(0, 0, 768, 1004), nil); // succeeds
STAssertEquals(view.frame, CGRectMake(0, y, 768, 8), nil); // fails: why?
STAssertEquals(view.frame, CGRectMake(0, y + 0.5, 768, 8), nil); // succeeds: why?
}
您所展示的是,autolayout讨厌未对齐的视图。在非视网膜设备上,最近的像素是最近的点,因此它四舍五入为整数。在视网膜屏幕上,最近的像素是最近的半点,因此它四舍五入到最近的点。您可以通过将第二个测试中的y更改为950.25并注意view.frame仍然是{{0,950.5}、{768,8}(而不是更改为{0,950.25}、{768,8})来演示这一点
(为了证明它是四舍五入的,而不是
ceil
ing,如果你把y改为950.2 view.frame变成{{0,950},{768,8}。)这只发生在iOS 6上的iPad2上吗?@ArkadiuszHolko这个问题出现在运行iOS 6.1.3的iPad2上。它不会出现在运行iOS 6.1.4的iPhone 5上。这些是最新的iOS 6版本。我目前还没有办法在iOS 7上进行测试。我还可以尝试什么?您能看到当您将视图的高度从21更改为22时会发生什么吗?现在iPhone和iPad的结果一致吗?iPad没有视网膜显示器,这可能是造成差异的原因。如果我将view
的高度从21改为22,问题就不会再出现了。(感谢您的建议!)问题在于,与我在简单单元测试中隔离问题不同,在实际应用程序中,这些坐标中的一些是计算出来的,而不是简单地设置的。在我更好地理解根本原因的性质和有效性之前,我赶紧修改所有这些计算。也许这是奇点坐标、自动布局和预视网膜iPad显示的组合,正如你的评论所暗示的。因此,视图必须与整个像素坐标精确对齐的隐式约束比所有显式约束具有更高的优先级,我想。。。