Why does iOS auto layout lead to apparent rounding errors on pre-Retina displays (unit test included)(为什么 iOS 自动布局会导致预 Retina 显示器出现明显的舍入错误(包括单元测试))
问题描述
我目前很难理解为什么以下单元测试在 iPad 2 上失败.自动布局似乎稍微(0.5 分)错误定位 view
在 superview
相对于两个布局约束所需的精确居中.似乎特别奇怪的是,关键测试(但最后一个断言)在 iPhone 5 上通过,因此明显的舍入误差只影响一个(iOS 6)平台.这是怎么回事?
I'm currently having a hard time understanding why the following unit test fails on an iPad 2. Auto layout seems to slightly (by 0.5 points) mis-position view
inside superview
relative to the exact centering that's required by two layout constraints. What seems especially strange is that the crucial test (but-last assertion) passes on an iPhone 5, so the apparent rounding error affects only one (iOS 6) platform. What's going on here?
更新 1 我已更改代码以确保两个框架在宽度和高度方面都受到充分限制,即使 translatesAutoresizingMaskIntoConstraints
为 NO
,建议作为可能相关的补救措施此处.但是,这显然不会改变这种情况.
UPDATE 1 I've changed the code to ensure that that both frames are sufficiently constrained in terms of widths and heights even if translatesAutoresizingMaskIntoConstraints
is NO
, as suggested as a possibly related remedy here. However, this apparently does not change the situation.
#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 我在第二个单元测试中隔离了另一个(显然)相同问题的实例.这一次它涉及一个顶部(不是中心)约束,这一次小数点坐标似乎是触发器.(测试在 Retina 之前的设备上也成功,例如 y = 951
,即奇点坐标.)我检查了各种模拟器配置(在我的物理 iPad 2 和 iPhone 5 旁边)发生确实似乎与缺少 Ratina 显示器有关.(再次感谢@ArkadiuszHolko 的领导.)
UPDATE 2 I've isolated another instance of (apparently) the same problem in a second unit test. This time it involves a top (not center) constraint, and this time a fractional point coordinate seems to be the trigger. (The test succeeds also on pre-Retina devices e.g. with y = 951
, i.e. an odd point coordinate.) I've checked in various simulator configurations (next to my physical iPad 2 and iPhone 5) occurence indeed seems tied to the absence of a Ratina display. (Again, thanks to @ArkadiuszHolko for the lead.)
我目前从这些测试中的感觉是,如果需要在前 Retina 显示器上进行精确点自动布局,则必须避免奇数高度和分数 y 坐标.但为什么呢?
My current sense from these tests is that one has to avoid odd heights and fractional y-coordinates if point-exact auto layout on pre-Retina displays is required. But why?
- (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?
}
推荐答案
您所展示的是自动布局讨厌未对齐的视图.在非视网膜设备上,最近的 pixel 是最近的 point,因此它会四舍五入为整数.在视网膜屏幕上,最近的 像素 是最近的 半点,因此它四舍五入到最接近的 0.5.您可以通过将第二个测试中的 y 更改为 950.25 并注意 view.frame 保持 {{0, 950.5}, {768, 8}} 来证明这一点(而不是更改为 {{0, 950.25}, {768, 8}}).
What you've shown is that autolayout abhors misaligned views. On non-retina devices the closest pixel is the nearest point, so it rounds to whole numbers. On retina screens the closest pixel is the nearest half point, so it rounds to the nearest .5. You can demonstrate this by changing y in your second test to 950.25 and noting that view.frame remains {{0, 950.5}, {768, 8}} (instead of changing to {{0, 950.25}, {768, 8}}).
(只是为了证明它是四舍五入而不是 ceil
ing,如果将 y 更改为 950.2 view.frame 将变为 {{0, 950}, {768, 8}}.)
(Just to prove that it is rounding and not ceil
ing, if you change y to 950.2 view.frame becomes {{0, 950}, {768, 8}}.)
这篇关于为什么 iOS 自动布局会导致预 Retina 显示器出现明显的舍入错误(包括单元测试)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:为什么 iOS 自动布局会导致预 Retina 显示器出现明显的舍入错误(包括单元测试)
基础教程推荐
- android 应用程序已发布,但在 google play 中找不到 2022-01-01
- Kivy Buildozer 无法构建 apk,命令失败:./distribute.sh -m “kivy"d 2022-01-01
- 在 gmail 中为 ios 应用程序检索朋友的朋友 2022-01-01
- UIWebView 委托方法 shouldStartLoadWithRequest:在 WKWebView 中等效? 2022-01-01
- 如何在 UIImageView 中异步加载图像? 2022-01-01
- 如何在没有IB的情况下将2个按钮添加到右侧的UINavigationbar? 2022-01-01
- 当从同一个组件调用时,两个 IBAction 触发的顺序是什么? 2022-01-01
- 如何让对象对 Cocos2D 中的触摸做出反应? 2022-01-01
- 如何在 iPhone 上显示来自 API 的 HTML 文本? 2022-01-01
- Android:对话框关闭而不调用关闭 2022-01-01