2013-10-13 6 views
12

Ich habe derzeit Schwierigkeiten zu verstehen, warum der folgende Komponententest auf einem iPad 2 fehlschlägt. Auto-Layout scheint leicht (durch 0,5 Punkte) falsche Position view innerhalb superview relativ zu der genauen Zentrierung, die von zwei Layout-Einschränkungen erforderlich ist. Besonders merkwürdig erscheint, dass der entscheidende Test (aber letzte Behauptung) auf einem iPhone 5 läuft, so dass der scheinbare Rundungsfehler nur eine (iOS 6) Plattform betrifft. Was ist denn hier los?Warum führt das iOS-Auto-Layout zu scheinbaren Rundungsfehlern auf Pre-Retina-Displays (Unit-Test enthalten)

UPDATE 1 Ich habe den Code, um sicherzustellen, dass geändert, dass beide Rahmen ausreichend in Bezug auf die Breiten und Höhen beschränkt sind, auch wenn translatesAutoresizingMaskIntoConstraints ist NO, wie als möglicherweise im Zusammenhang Abhilfe here vorgeschlagen. Dies ändert jedoch offensichtlich nichts an der 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 

UPDATE 2 Ich habe eine andere Instanz (scheinbar) das gleiche Problem in einer zweiten Einheit Test isoliert. Diesmal handelt es sich um eine obere (nicht zentrale) Abhängigkeit, und diesmal scheint eine gebrochene Punktkoordinate der Auslöser zu sein. (Der Test ist auch auf Pre-Retina-Geräten erfolgreich, zB mit y = 951, also einer ungeraden Punktkoordinate.) Ich habe verschiedene Simulatorkonfigurationen eingecheckt (neben meinem physischen iPad 2 und iPhone 5), scheint in der Tat an das Fehlen eines Ratina gebunden zu sein Anzeige. (Nochmals, dank @ ArkadiuszHolko für die Führung.)

Mein aktueller Sinn dieser Tests ist, dass man ungerade Höhen und gebrochene y-Koordinaten vermeiden muss, wenn punktgenaues automatisches Layout auf Pre-Retina-Displays erforderlich ist. Aber warum?

- (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? 
} 
+0

es nur passieren Hat iPad 2 auf iOS 6? –

+0

@ArkadiuszHolko Das Problem wird auf dem iPad 2 mit iOS 6.1.3 angezeigt. Es erscheint nicht auf dem iPhone 5 mit iOS 6.1.4. Dies sind die neuesten iOS 6-Versionen. Ich habe derzeit nicht die Möglichkeit, auf iOS 7 zu testen. Was könnte ich noch ausprobieren? – Drux

+1

Können Sie sehen, was passiert, wenn Sie die Höhe der "Ansicht" von 21 auf 22 ändern? Sind die Ergebnisse zwischen iPhone und iPad nun konsistent? Das iPad hat nicht das Retina-Display, das den Unterschied verursacht haben könnte. –

Antwort

11

Was Sie gezeigt haben, ist, dass das automatische Layout nicht korrekt ausgerichtete Ansichten behandelt. Auf Nicht-Retina-Geräten ist der nächste Pixel der nächste Punkt, so dass es auf ganze Zahlen runden wird. Auf Retina-Bildschirmen ist der nächste Pixel der nächste halbe Punkt, so dass es auf die nächste .5. Sie können dies demonstrieren, indem Sie y in Ihrem zweiten Test auf 950.25 ändern und beachten, dass view.frame {{0, 950.5}, {768, 8}} bleibt (anstatt zu {{0, 950.25}, {768, 8} zu wechseln) }).

(Nur um zu beweisen, dass er und nicht ceil ing rundet, wenn Sie y ändern, um 950,2 view.frame wird {{0, 950}, {768, 8}}.)

+0

Also eine implizite Einschränkung, bei der Ansichten genau auf ganze Pixelkoordinaten ausgerichtet sein müssen, hat höhere Priorität als alle expliziten Einschränkungen, nehme ich an ... – Drux