2010-09-12 14 views
13

Ho un UIView con un gruppo di sottoview, tutte posizionate utilizzando layoutSubviews. Quando il view viene ridimensionato, le relative posizioni cambiano tutte. Mi piacerebbe che questi ricalcoli accadessero durante un ridimensionamento animato (usando + [UIView beginAnimations:] chiamate). Questo non sembra accadere. Qualche idea?layoutSubviews durante un'animazione?

risposta

23

Presupposto: si desidera avere più fasi di animazione (ovvero, la posizione non cambia in modo lineare con la dimensione della cornice).

Questo non è possibile con una singola animazione "standard" UIView. Perché? La cornice/limiti viene impostata solo una volta.

Core Animation ha tre "alberi di strato":

  • L'albero modello è dove la vostra applicazione pensa che le cose sono.
  • L'albero di presentazione è approssimativamente ciò che viene visualizzato sullo schermo.
  • L'albero di rendering è approssimativamente ciò che Core Animation sta compositing.

UIView è un involucro (un po 'sottile) attorno al livello del modello. Durante un'animazione UIView, Core Animation aggiorna l'albero di presentazione/rendering — l'albero del modello rappresenta il punto finale delle animazioni. Il risultato è che il tuo codice può (per la maggior parte) trattare le animazioni come istantanee — spostando una vista da A a B immediatamente spostandolo in B; la modifica sembra essere animata all'utente.

Ci sono cose più complicate che si possono fare con CALayer/CAAnimation direttamente, ma non ho indagato molto.

È possibile concatenare più animazioni utilizzando - [UIView setAnimationDidStopSelector:]. (Si potrebbe anche provare a utilizzare più animazioni insieme a setAnimationDelay :, ma non sono sicuro di cosa succede con più animazioni sulla stessa proprietà, si potrebbe avere fortuna con setAnimationBeginsFromCurrentState :.)

Se si desidera un controllo veramente a grana fine , CADisplayLink (OS 3.1+) è un timer che si attiva dopo ogni aggiornamento dello schermo. Un'opzione fallback (per il supporto 3.0) consiste nell'usare un NSTimer a 30/60 Hz circa.

+0

Grazie per la sguardo dettagliato sotto le coperte. Mentre ero perifericamente consapevole della gerarchia ad albero, hai messo insieme una grande sintesi del perché l'architettura di Core Animation fondamentalmente non consente ciò che voglio fare. –

4

Inserimento per completezza. Grazie a tc. per aver spiegato che ciò che voglio fare, esattamente, non è supportato da Core Animation.

Alla fine ho trovato una soluzione ragionevole. Piuttosto, layout le mie sottoview in -layoutSubviews, lo faccio in -setBounds :. Quindi, quando avvolgo un set-set: chiama in un UIView + beginAnimations: block, anche quelle chiamate di posizionamento sono animate, e il risultato finale è tutto che si anima correttamente dove dovrebbe essere dio.

+1

Oh ... Se è così, allora ho interpretato male quello che stavi dicendo. Prova 'view.frame = blah; [view layoutIfNeeded] ' –

+0

no, questo li metterà semplicemente in posizione e quindi animerà i limiti. È quello che ho provato inizialmente. –

+1

Sto cercando di ottenere qualcosa di simile, tuttavia il mio setBounds viene chiamato solo una volta (con i limiti finali), quindi le mie sottoview non vengono riallineate durante l'animazione. C'è qualcosa che mi è mancato? – alexleutgoeb

13

So che questa è una vecchia domanda, ma questo codice funziona molto bene per me (adatto per il tuo esempio di modifica del frame).

-(void)layoutSubviews{ 
    [super layoutSubviews]; 
    // layout your subviews here, or whatever 
} 

-(void)someMethod{ 
    double duration=...; 
    [UIView animateWithDuration:duration animations:^{ 
     self.frame = ...; 
     [self layoutIfNeeded]; 
    }]; 
} 

Ovviamente è possibile chiamare questo metodo da un altro oggetto. Il "trucco" è chiamare layoutIfNeeded (o layoutSubviews direttamente - stessa cosa, se cambi il frame viene chiamato setNeedsLayout).

Come tc.ha spiegato bene gli "alberi dei livelli", basta forzare il livello di presentazione per visualizzare lo stadio finale del livello del modello con l'animazione.

Il vantaggio di questo metodo è la possibilità di controllare quando il frame/bound cambia sia animato e quando è istantaneo.

Spero che questo aiuti qualcuno :).

+0

Questo è stato molto utile !! Non ho mai capito questa parte di Core Animation finché non ho letto la tua risposta. –

+0

@JohnWright: il mio piacere :) – JakubKnejzlik

+1

Potrebbe essere necessario chiamare setNeedsLayout prima di layoutIfNeeded. – Karmeye

11

Completamento @ Anwer di GrizzlyNetch, è possibile impostare l'opzione UIViewAnimationOptionLayoutSubviews di animazione, quindi non c'è bisogno di chiamare layoutIfNeeded:

-(void)someMethod{ 
    double duration = ...; 
    [UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionLayoutSubviews animations:^{ 
     self.frame = ...; 
    } completion:nil]; 
}