2013-10-13 6 views
12

Attualmente sto avendo difficoltà a capire perché il seguente test dell'unità non funziona su un iPad 2. Il layout automatico sembra leggermente 0,5 punti) mis-position view all'interno di superview relativo al centraggio esatto richiesto da due vincoli di layout. Ciò che sembra particolarmente strano è che il test cruciale (ma ultima asserzione) passa su un iPhone 5, quindi l'errore di arrotondamento apparente interessa solo una piattaforma (iOS 6). Cosa sta succedendo qui?Perché il layout automatico di iOS causa errori di arrotondamento apparenti sui display pre-Retina (test unitario incluso)

UPDATE 1 ho cambiato il codice per garantire che entrambi i fotogrammi che siano sufficientemente limitate in termini di larghezze e altezze anche se translatesAutoresizingMaskIntoConstraints è NO, come suggerito come rimedio eventualmente correlato here. Tuttavia, questo a quanto pare non cambia la situazione.

#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 ho isolato un altro esempio di (apparentemente) lo stesso problema in un secondo test di unità. Questa volta comporta un vincolo in alto (non al centro), e questa volta una coordinata punto frazionaria sembra essere l'innesco. (Il test ha esito positivo anche su dispositivi pre-Retina, ad esempio con y = 951, ovvero una coordinata di punti dispari.) Ho verificato in varie configurazioni del simulatore (accanto al mio iPad 2 fisico e iPhone 5) la comparsa sembra legata all'assenza di una Ratina display. (Di nuovo, grazie a @ArkadiuszHolko per il lead.)

Il mio senso corrente di questi test è che bisogna evitare altezze dispari e coordinate y frazionali se è necessario disporre di layout auto point-esatto su display pre-retina. Ma perché?

- (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

accade solo su iPad 2 su iOS 6? –

+0

@ArkadiuszHolko Il problema si presenta su iPad 2 con iOS 6.1.3. Non viene visualizzato su iPhone 5 con iOS 6.1.4. Queste sono le ultime versioni di iOS 6. Al momento non ho i mezzi per testare su iOS 7. Cos'altro potrei provare? – Drux

+1

Riesci a vedere cosa succede quando cambi l'altezza della 'vista' da 21 a 22? I risultati ora sono coerenti tra iPhone e iPad? iPad non ha il display retina, che potrebbe aver causato la differenza. –

risposta

11

Quello che hai mostrato è che l'autospegnimento aborre viste disallineate. Sui dispositivi senza retina il pixel 10 più vicino è il più vicino punto, quindi arrotonda a numeri interi. Sugli schermi retina il più vicino pixel è il più vicino mezzo punto, quindi arrotonda al più vicino .5. Puoi dimostrarlo cambiando y nel tuo secondo test a 950.25 e notando che view.frame rimane {{0, 950.5}, {768, 8}} (invece di passare a {{0, 950.25}, {768, 8} }).

(solo per dimostrare che si sta girando e non ceil ING, se si cambia y per 950,2 view.frame diventa {{0, 950}, {768, 8}}.)

+0

Quindi un vincolo implicito per cui le viste devono essere allineate esattamente alle coordinate di pixel interi ha una priorità più alta rispetto a tutti i vincoli espliciti, suppongo ... – Drux