2013-05-29 11 views
36

Ho problemi con UIScrollView utilizzando i vincoli di layout automatico. Ho la seguente vista gerarchia, con vincoli imposti attraverso IB:Layout automatico UIScrollView con sottoview con altezze dinamiche

- ScrollView (leading, trailing, bottom and top spaces to superview) 
-- ContainerView (leading, trailing, bottom and top spaces to superview) 
--- ViewA (full width, top of superview) 
--- ViewB (full width, below ViewA) 
--- Button (full width, below ViewB) 

Il viewA e viewB hanno altezze iniziali di 200 punti, ma può essere speso in verticale per un'altezza di 400 punti cliccando su di esso. ViewA e ViewB sono espansi aggiornando il loro vincolo di altezza (da 200 a 400). Ecco il corrispondente frammento:

if(self.contentVisible) { 
    heightConstraint.constant -= ContentHeight; 
    // + additional View's internal constraints update to hide additional content 
    self.contentVisible = NO; 
} else { 
    heightConstraint.constant += ContentHeight; 
    // + additional View's internal constraints update to show additional content 
    self.contentVisible = YES; 
} 

[self.view setNeedsUpdateConstraints]; 
[UIView animateWithDuration:.25f animations:^{ 
    [self.view layoutIfNeeded]; 
}]; 

Il mio problema è che se entrambe le viste sono espansi, ho bisogno di essere in grado di scorrere per vedere l'intero contenuto, e in questo momento il rotolo non sta funzionando. Come posso aggiornare la vista di scorrimento usando i vincoli per riflettere i cambiamenti delle altezze ViewA e ViewB?

L'unica soluzione che riesco a pensare finora è impostare manualmente l'altezza di ContainerView dopo l'animazione, che sarà la somma delle altezze del pulsante ViewA + ViewB +. Ma credo che ci sia una soluzione migliore?

Grazie

+0

http://stackoverflow.com/questions/13499467/uiscrollview-doesnt-use-autolayout-constraints è stato anche molto utile –

risposta

57

che utilizzo pura struttura simile alla seguente

-view 
    -scrollView 
    -view A 
    -view B 
    -Button 

fa il tasto sicuro (l'ultima vista) presenta un vincolo (spaziatura verticale dal basso verso superview, che è ScrollView) in questo caso, indipendentemente dalle modifiche per la vista A e la vista B, l'altezza di scrollView verrà modificata di conseguenza.

Mi riferisco a questo grande online book site.

Basta leggere la sezione "Creazione di una vista a scorrimento", dovresti avere un'idea.

Avevo il problema simile che stavo creando una vista di dettaglio e l'utilizzo di Interface Builder con layout automatico è così adatto per l'attività!

Buona fortuna!

(risorse aggiuntive:...

Pila discussione di troppo pieno sulla auto layout for scroll view

iOS 6 ha un Release Notes parlando di supporto Auto Layout per UIScrollView

gratis on line iOS book explanation sulla vista di scorrimento Questo in realtà ha aiutato me molto!

+0

Grazie per le risorse! Piccolo cambiamento di piano, il mio cliente voleva un supporto per iOS 5, quindi ho dovuto disattivare il layout automatico, ma tornerò su questo più tardi. – lukas

+2

Risposta fantastica. – Jesse

+1

"Assicurati che il pulsante (THE LAST view) abbia un vincolo (spaziatura verticale dalla sua parte inferiore a superview, che è la scrollview)" <- Mi piace questa parte! – FDIM

7

Questo è un esempio di come ho disposto un puro UIScrollView di autolayout con una vista di contenitore. Ho commentato per renderlo c learer:

contenitore è un UIView di serie e il corpo è un UITextView

- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 

    //add scrollview 
    [self.view addSubview:self.scrollView]; 

    //add container view 
    [self.scrollView addSubview:self.container]; 

    //body as subview of container (body size is undetermined) 
    [self.container addSubview:self.body]; 

    NSDictionary *views = @{@"scrollView" : self.scrollView, @"container" : self.container, @"body" : self.body}; 
    NSDictionary *metrics = @{@"margin" : @(100)}; 

    //constrain scrollview to superview, pin all edges 
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[scrollView]|" options:kNilOptions metrics:metrics views:views]]; 
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[scrollView]|" options:kNilOptions metrics:metrics views:views]]; 

    //pin all edges of the container view to the scrollview (i've given it a horizonal margin as well for my purposes) 
    [self.scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[container]|" options:kNilOptions metrics:metrics views:views]]; 
    [self.scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-margin-[container]-margin-|" options:kNilOptions metrics:metrics views:views]]; 

    //the container view must have a defined width OR height, here i am constraining it to the frame size of the scrollview, not its bounds 
    //the calculation for constant is so that it's the width of the scrollview minus the margin * 2 
    [self.scrollView addConstraint:[NSLayoutConstraint constraintWithItem:self.container attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self.scrollView attribute:NSLayoutAttributeWidth multiplier:1.0f constant:-([metrics[@"margin"] floatValue] * 2)]]; 

    //now as the body grows vertically it will force the container to grow because it's trailing edge is pinned to the container's bottom edge 
    //it won't grow the width because the container's width is constrained to the scrollview's frame width 
    [self.container addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[body]|" options:kNilOptions metrics:metrics views:views]]; 
    [self.container addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[body]|" options:kNilOptions metrics:metrics views:views]]; 
} 

Nel mio esempio 'corpo' è un UITextView, ma potrebbe essere qualsiasi altra cosa. Se si utilizza UITextView, si noti che per poter crescere verticalmente deve avere un vincolo di altezza che viene impostato in viewDidLayoutSubviews.Quindi aggiungere il seguente vincolo in viewDidLoad e mantenere un riferimento ad esso:

self.bodyHeightConstraint = [NSLayoutConstraint constraintWithItem:self.body attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:nil multiplier:1.0f constant:100.0f]; 
[self.container addConstraint:self.bodyHeightConstraint]; 

Poi nel viewDidLayoutSubviews calcolare l'altezza e aggiornare il vincolo del costante:

- (void)viewDidLayoutSubviews 
{ 
    [super viewDidLayoutSubviews]; 

    [self.bodyHeightConstraint setConstant:[self.body sizeThatFits:CGSizeMake(self.container.width, CGFLOAT_MAX)].height]; 

    [self.view layoutIfNeeded]; 
} 

è necessaria Il secondo passaggio di layout per ridimensionare l'UITextView .

0

In ogni momento la vista di scorrimento dovrebbe sapere le sue dimensioni contenuti. La dimensione del contenuto è dedotta dalle sottoview della scrollview. È molto utile mappare le proprietà del controller ai vincoli nel file xib che descrivono le altezze delle visualizzazioni secondarie. Quindi nel codice (un blocco di animazione) puoi semplicemente cambiare le costanti di queste proprietà di vincolo. Se è necessario modificare l'intero vincolo, mantenere un riferimento ad esso, in modo che sia possibile aggiornarlo successivamente nel contenitore genitore.

+0

Vieni con qualche esempio o codice. È davvero molto facile mettere la tua comprensione a parole. – Developer

23

Diciamo che abbiamo una gerarchia come questo (Label1 è una visualizzazione secondaria di contentView; contentView è una visualizzazione secondaria di ScrollView, ScrollView è una subiview di vista del viewcontroller):

ViewController's View ScrollView ContentView Label1 Label2 Label3

ScrollView è vincolato con autolayout nel modo normale alla vista del viewcontroller.

contentView è appuntato in alto/sinistra/destra/basso verso ScrollView. Il che significa che sono presenti vincoli che vincolano i bordi superiore/inferiore/iniziale/finale del contenuto di ContentView in modo che siano uguali agli stessi bordi di ScrollView. Ecco una chiave: questi vincoli sono per il contentSize di ScrollView, non le sue dimensioni del fotogramma come mostrato nella vista del viewcontroller. Quindi non sta dicendo la contentView essere la stessa dimensione del frame come la struttura ScrollView visualizzata, è piuttosto dicendo Scrollview che il contentView è il suo contenuto e quindi se contentView è più grande del telaio ScrollView allora si ottiene lo scorrimento, proprio come impostazione scrollView.contentSize più grande di scrollView.frame rende il contenuto scorrevole.

Ecco un'altra chiave: ora è necessario disporre di un numero sufficiente di limiti tra ContentView, Label1-3 e qualsiasi altra cosa oltre a ScrollView per ContentView per essere in grado di determinare la larghezza e l'altezza da tali vincoli.

Così, ad esempio, se si desidera un insieme di etichette a scorrimento verticale, si imposta un vincolo per rendere la larghezza di ContentView uguale alla larghezza della vista ViewController, che si occupa della larghezza. Per occuparti dell'altezza, imposta Label1 in alto a ContentView in alto, Label2 in alto a Label1 in basso, Label3 in alto a Label2 in basso, e infine (e soprattutto) inserisci il margine di Label3 nella parte inferiore di ContentView. Ora ha abbastanza informazioni per calcolare l'altezza del ContentView.

Spero che questo qualcuno dà un indizio, come ho letto attraverso i post di cui sopra e ancora non riuscivo a capire come fare di larghezza e altezza dei vincoli del contentView correttamente. Quello che mi mancava era pinning inferiore della Label3 al fondo della contentView, altrimenti come potrebbe contentView sapere quanto alto sia (come Label3 sarebbe solo allora galleggiare, e non ci sarebbe nessun vincolo di dire contentView in cui la sua posizione y fondo è).

+0

Questa parte è stata chiara per me "Quindi non sta dicendo a ContentView che ha le stesse dimensioni del fotogramma di ScrollView visualizzato, ma è piuttosto indicativo di Scrollview che ContentView è il suo contenuto". –

+0

Finora, la risposta più chiara! Grazie! pollice su! –

+0

Grazie Doug. Il tuo "se vuoi un set di scrolling verticale di [] .... imposti un vincolo per rendere la larghezza di ContentView uguale alla larghezza della ViewController View, che si presta alla larghezza" mi ha risolto un sacco di mal di testa. Stavo impostando la larghezza e l'altezza di ContentView in modo uguale alla vista del ViewController. Ho eliminato l'altezza uguale e ho risolto molti problemi di vincoli. Grazie! – Pangu

0

Usa questo codice. ScrollView setContentSize dovrebbe essere chiamato async nel thread principale.

- (void)viewDidLayoutSubviews 
{ 
    [super viewDidLayoutSubviews]; 

    dispatch_async(dispatch_get_main_queue(),^
        { 
         CGRect contentRect = CGRectZero; 
         for (UIView *view in scrollView.subviews) 
          if (!view.hidden) 
           contentRect = CGRectUnion(contentRect, view.frame); 

         contentRect.size.height=contentRect.size.height; 
         scrollView.contentSize = contentRect.size; 
        }); 
} 
0

La mia variante per la vista di scorrimento con! Dynamic! altezza:

1) Aggiungi scroll view al tuo UIView. Pin tutti i vincoli (superiore, inferiore, principale, percorso).

2) Aggiungi UIView a Scroll View. Pin tutti i vincoli (superiore, inferiore, principale, percorso). Sarà il tuo Content view. Puoi anche rinominarlo.

3) controllo di trascinamento Content view-Scroll view - Equal width

4) Aggiungi contenuti al tuo UIView. Imposta i vincoli necessari. E! Alla voce inferiore aggiungere vincolo fondo NONGreater or equal (>=) (Come la maggior parte persone colloqui) MAEqual! Ad esempio, impostalo su 20.

Nella mia situazione ho UIImageView nel contenuto. Ho collegato è height al codice. E se lo cambio a like 1000, lo scroll è visibile. E tutto funziona.

Funziona come un incantesimo per me. Qualsiasi domanda - benvenuti ai commenti.

+0

Cosa succede se anche l'elemento in basso cresce dinamicamente? – jshapy8

+0

@ jshapy8 Non sono sicuro, ma penso che ridimensionerà UIView in visualizzazione di scorrimento. E scrollview adatterà la sua altezza. Puoi provarlo da solo. Non posso farlo atm –

+0

Sì, ho scoperto che lo ridimensiona. Penso che ridisegnare manualmente la cornice della vista dinamica (s) e la vista (s) sotto di essa è la parte difficile, non solo i vincoli – jshapy8

Problemi correlati