2013-06-25 7 views
5

Immaginate questo scenario seguente. Hai un UIImageView e un UIButton. Il primo è 300x360, il secondo è 210x70. imageview contiene un'immagine di catalogo, button dice "catalogo aperto".Centrare due viste verticalmente utilizzando NSLayoutConstraint

vorrei posizionare gli elementi nella vista principale base ai seguenti requisiti:

  • i due elementi devono essere centrati orizzontalmente, cioè le coordinate center.x devono essere tutti uguali (vista, immagini e pulsante);

  • i due elementi devono essere centrati verticalmente nel modo seguente: separatore (flessibile) - imageview - separatore (fisso, diciamo 30 punti) - pulsante - separatore (flessibile). Il separatore più in alto e più in basso dovrebbe avere la stessa dimensione (questo è il significato di centrato).

Non riesco a farlo funzionare con NSLayoutConstraint.

Finora, ciò che ho fatto, è stato il centraggio della coordinata X dei due elementi utilizzando NSLayoutAttributeCenterX e NSLayoutRelationEqual con lo stesso attributo view.

L'ultima parte, secondo la mia idea, è di fissare il loro allineamento verticale. Ho provato a utilizzare @"V:|-[imageview(360)]-[button(70)]-|" ma non funzionerà (Unable to simultaneously satisfy constraints.).

Se uso @"V:|-[imageview(360)]-[button]-|" ottengo tutto parzialmente ok. Vale a dire, la parte superiore è perfetta ma il pulsante è teso in modo da riempire lo spazio tra il separatore interno e il fondo della vista.

Come è possibile rendere tali elementi di dimensioni fisse e consentire al layout automatico di capire come posizionarli nella vista?

+0

In iOS 9, guarda UIStackView e UILayoutGuide per risolvere questo tipo di problemi. –

risposta

10

sono stato in grado di farlo facendo qualcosa di simile a questo:

NSNumber *sepHeight = @60.0F; 

// Center the two views horizontally 
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:imageView 
                 attribute:NSLayoutAttributeCenterX 
                 relatedBy:NSLayoutRelationEqual 
                 toItem:self.view 
                 attribute:NSLayoutAttributeCenterX 
                multiplier:1 
                 constant:0]]; 

[self.view addConstraint:[NSLayoutConstraint constraintWithItem:button 
                 attribute:NSLayoutAttributeCenterX 
                 relatedBy:NSLayoutRelationEqual 
                 toItem:self.view 
                 attribute:NSLayoutAttributeCenterX 
                multiplier:1 
                 constant:0]]; 

// Position the two views one below the other, using the separator height defined above 
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[imageview]-sepHeight-[button]" 
                    options:0 
                    metrics:NSDictionaryOfVariableBindings(sepHeight) 
                    views:views]]; 

// Force the button distance from the bottom to be the half of the size of the content 
CGFloat constant = (imageview.frame.size.height + button.frame.size.height + [sepHeight floatValue])/2.0; 
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:button 
                 attribute:NSLayoutAttributeBottom 
                 relatedBy:NSLayoutRelationEqual 
                 toItem:self.view 
                 attribute:NSLayoutAttributeCenterY 
                multiplier:1 
                 constant:constant]]; 

La parte difficile è il valore costante. Quel valore è la metà dell'altezza di tutte le viste, inclusi i loro separatori. Ciò significa che, se l'altezza dell'immagine è 360, l'altezza del pulsante è 70 e il separatore è 60, tale costante sarà (360 + 70 + 60)/2 = 245.

Ci dovrebbe essere un modo più intelligente, anzi , ma per ora penso che sia ok.

+0

Intelligente! Un buon esempio di utilizzo della trasparenza del pensiero di layout automatico e del coefficiente e della costante. Quindi, un modo per descriverlo è Coefficiente = 0,5 Costante = (imageHeightConstant + imageButtonVerticalSpaceConstant + buttonHeightConstant) O mi sono perso qualcosa ...? Le costanti possono essere memorizzate come variabili o proprietà di CGFloat e adattate per adattamenti appropriati ... :) – uchuugaka

+0

No, il moltiplicatore viene applicato al valore self.view.frame.origin.y e la costante viene aggiunta a tale valore. Ora lo riscriverò usando le variabili. –

+1

Funziona anche per me, grazie! –

0

È necessario rendere gli spazi verticali uguali o maggiori rispetto alla parte superiore della vista dell'immagine e in fondo al pulsante. Ma sembra anche che sarebbe più semplice avere una vista aggiuntiva che contiene l'immagine e il pulsante. Quindi centrare quella vista V e H nella vista super.

Layout automatico indica talvolta visualizzazioni contenitore.

+0

Questo dovrebbe essere ok ma il problema è che se si crea un contenitore, si ha lo stesso problema nel contenitore :) –

+0

Niente affatto. Il contenitore può essere vincolato in cima a una vista secondaria e alla parte inferiore di un'altra. – uchuugaka

+0

Sei corretto. Se si rende l'altezza del contenitore la somma di tutte le altezze (e dei separatori), allora si ottiene una buona soluzione. Non mi piace molto l'idea di avere una visione complementare, ma penso che la soluzione sia forse più facile da capire rispetto alla mia. –

5

Ci sono due modi per avvicinarsi a questo.

  1. Come suggerisce @uchuugaka, posiziona la visualizzazione di immagini e il pulsante in una vista Contenitore. Non hai lo stesso problema di centratura se le sottoview sono bloccate ai bordi del contenitore (che tu ridimensionerai di conseguenza - l'altezza sarebbe 360 ​​+ 30 + 70 == 460. Quindi puoi centrare questo contenitore nella tua vista con un semplice vincolo allinea-centro-Y.

  2. È possibile utilizzare le visualizzazioni spaziatore. Aggiungendo due viste nascoste è possibile specificare i vincoli per posizionarli sopra/sotto la vista e il pulsante immagine, con altezze uguali tali che fungono da molle. Ecco il codice che fa questo, utilizzando le dimensioni:


- (void) viewDidLoad 
{ 
    [super viewDidLoad]; 

    UIView* imageview = [UIView new]; 
    imageview.backgroundColor = [UIColor blueColor]; 
    imageview.translatesAutoresizingMaskIntoConstraints = NO; 

    UIView* button = [UIView new]; 
    button.backgroundColor = [UIColor greenColor]; 
    button.translatesAutoresizingMaskIntoConstraints = NO; 


    UIView* spacer1 = [UIView new]; 
    spacer1.backgroundColor = [[UIColor redColor] colorWithAlphaComponent: 0.5]; 
    spacer1.translatesAutoresizingMaskIntoConstraints = NO; 
    spacer1.hidden = YES; // comment out to show spacer! 

    UIView* spacer2 = [UIView new]; 
    spacer2.backgroundColor = [[UIColor redColor] colorWithAlphaComponent: 0.5]; 
    spacer2.translatesAutoresizingMaskIntoConstraints = NO; 
    spacer2.hidden = YES; // comment out to show spacer! 

    [self.view addSubview: imageview]; 
    [self.view addSubview: button]; 
    [self.view addSubview: spacer1]; 
    [self.view addSubview: spacer2]; 

    NSDictionary* views = NSDictionaryOfVariableBindings(imageview, button, spacer1, spacer2); 

    NSArray* constraints; 
    constraints = [NSLayoutConstraint constraintsWithVisualFormat: @"V:|[spacer1(==spacer2)][imageview(360)]-30-[button(70)][spacer2(==spacer1)]|" 
                  options: 0 
                  metrics: nil 
                  views: views]; 
    [self.view addConstraints: constraints]; 

    [self.view addConstraint:[NSLayoutConstraint constraintWithItem:imageview 
                  attribute:NSLayoutAttributeWidth 
                  relatedBy:NSLayoutRelationEqual 
                  toItem:nil 
                  attribute:NSLayoutAttributeNotAnAttribute 
                 multiplier:1 
                  constant:300]]; 

    [self.view addConstraint:[NSLayoutConstraint constraintWithItem:button 
                  attribute:NSLayoutAttributeWidth 
                  relatedBy:NSLayoutRelationEqual 
                  toItem:nil 
                  attribute:NSLayoutAttributeNotAnAttribute 
                 multiplier:1 
                  constant:210]]; 


    [self.view addConstraint:[NSLayoutConstraint constraintWithItem:imageview 
                  attribute:NSLayoutAttributeCenterX 
                  relatedBy:NSLayoutRelationEqual 
                  toItem:self.view 
                  attribute:NSLayoutAttributeCenterX 
                 multiplier:1 
                  constant:0]]; 

    [self.view addConstraint:[NSLayoutConstraint constraintWithItem:button 
                  attribute:NSLayoutAttributeCenterX 
                  relatedBy:NSLayoutRelationEqual 
                  toItem:self.view 
                  attribute:NSLayoutAttributeCenterX 
                 multiplier:1 
                  constant:0]]; 
} 
+2

Mi piace l'idea delle viste spaziali. Tranne che mi ricorda le GIF spaziali HTML degli anni 90;) – uchuugaka

+0

Viste spaziatore o una vista extra del contenitore, a meno che non si conosca l'altezza (fissa) in anticipo. Oh bene. :) –