В настоящее время я с трудом понимаю, почему следующее unit test выходит из строя на iPad 2. Автоматическая компоновка кажется слегка (на 0,5 пункта) неправильной позицией view
внутри superview
относительно точной центровки, которая требуемый двумя ограничениями макета. Что особенно странно, так это то, что решающее испытание (но последнее утверждение) проходит на iPhone 5, поэтому очевидная ошибка округления влияет только на одну (iOS 6) платформу. Что здесь происходит?
ОБНОВЛЕНИЕ 1. Я изменил код, чтобы гарантировать, что оба фрейма достаточно ограничены с точки зрения ширины и высоты, даже если translatesAutoresizingMaskIntoConstraints
NO
, как это предлагается как возможное средство здесь. Однако это, по-видимому, не меняет ситуацию.
#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 Я выделил еще один экземпляр (по-видимому) той же проблемы во второй unit test. На этот раз это связано с верхним (не центральным) ограничением, и на этот раз координата дробной точки кажется триггером. (Тест преуспевает также на устройствах предварительной сетчатки, например, с помощью 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?
}