Visualizza le modifiche al frame tra viewWillAppear: e viewDidAppear:


85

Ho scoperto uno strano comportamento nella mia applicazione, in cui un connesso IBOutletha il frame della sua vista connessa tra le chiamate nel mio controller della vista a viewWillAppear:e viewDidAppear:. Ecco il codice pertinente nella mia UIViewControllersottoclasse:

-(void)viewWillAppear:(BOOL)animated {
    NSLog(@"%@", self.scrollView);
}

-(void)viewDidAppear:(BOOL)animated {
    NSLog(@"%@", self.scrollView);
}

e l'output del registro risultante:

MyApp[61880:c07] <UIScrollView: 0x1057eff0; frame = (0 0; 0 0); clipsToBounds = YES; autoresize = TM+BM; gestureRecognizers = <NSArray: 0x10580100>; layer = <CALayer: 0x1057f210>; contentOffset: {0, 0}>
MyApp[61880:c07] <UIScrollView: 0x1057eff0; frame = (0 44; 320 416); clipsToBounds = YES; autoresize = TM+BM; gestureRecognizers = <NSArray: 0x10580100>; layer = <CALayer: 0x1057f210>; contentOffset: {0, 0}>

Il che mostra chiaramente che il frame sta cambiando tra le due chiamate. Volevo fare la configurazione con la vista nel viewDidLoadmetodo, ma se il contenuto non è disponibile per la modifica fino a quando non è sullo schermo, sembra piuttosto inutile. Cosa potrebbe succedere?


2
Stai usando il layout automatico? stai aggiungendo questa vista in Interface Builder o in modo programmatico?
Andrea

Il layout automatico è abilitato e questa vista viene creata in IB da uno storyboard.
Jumhyn

1
Non ho mai usato lo storyboard, ma probabilmente è corretto. L'utilizzo della cornice di layout automatico delle viste viene impostata quando il motore di layout automatico avvia il calcolo. Prova a chiedere la stessa cosa subito dopo super of - (void) viewDidLayoutSubviews mpethod del tuo controller di visualizzazione.
Andrea

Ciò attiva con successo il mio evento al momento giusto, ma quel metodo viene chiamato anche ogni volta che eseguo un'animazione sulla vista.
Jumhyn

1
viewDidLayoutSubviewsera il modo corretto di procedere. Dovevo solo mettere tutto il mio contenuto in una vista secondaria in modo che il metodo non fosse richiamato ogni volta che cambiavo la cornice della vista principale.
Jumhyn

Risposte:


111

Autolayoutha fatto un enorme cambiamento nel modo in cui progettiamo e sviluppiamo la GUI delle nostre opinioni. Una delle differenze principali è che autolayoutnon cambia le dimensioni della nostra vista immediatamente, ma solo quando viene attivato, cioè in un momento specifico, ma possiamo costringerlo a ricalcolare immediatamente i nostri vincoli o contrassegnarli come "bisognosi" di layout. Funziona come -setNeedDisplay.
La grande sfida per me è stata capire e accettare questo, non abbiamo più bisogno di usare maschere di ridimensionamento automatico e la cornice è diventata una proprietà inutile nel posizionare le nostre opinioni. Non abbiamo più bisogno di pensare alla posizione della vista, ma dovremmo pensare a come vogliamo vederli in uno spazio in relazione tra loro.
Quando vogliamo mescolare la vecchia maschera di ridimensionamento automatico e il layout automatico è quando sorgono problemi. Dovremmo pensare molto presto all'implementazione dell'autolayout e cercare di evitare di mescolare il vecchio approccio in una gerarchia di visualizzazione basata sull'autolayout.
Va bene avere una vista contenitore che utilizzi solo maschere di ridimensionamento automatico, come una vista principale di un controller di visualizzazione, ma è meglio se non proviamo a mescolare.
Non ho mai usato lo storyboard, ma probabilmente è corretto. Utilizzando il layout automatico, i frame delle visualizzazioni vengono impostati quando il motore di layout automatico inizia il calcolo. Prova a chiedere la stessa cosa subito dopo il super - (void)viewDidLayoutSubviewsmetodo del tuo controller di visualizzazione.
Questo metodo viene chiamato quando il motore di layout automatico ha terminato per calcolare i frame delle visualizzazioni.


5
- (void) viewDidLayoutSubviews è la risposta per me! Molte grazie!
FrizzTheSnail

Questa risposta non è davvero corretta. Sì certo, ovviamente, da (5?) Anni devi usare il layout automatico. Ma ci sono un certo numero di situazioni ( usando il layout automatico ) in cui devi, per esempio, aggiungere qualcosa su uno schermo, "appena prima che appaia all'utente". (Se lo fai in viewDidAppear avrai uno sfarfallio. Se lo fai in viewWillAppear, le posizioni saranno sbagliate.) La risposta effettiva è in effetti usare viewDidLayoutSubviews.
Fattie

add something on a screen, "just before it appears to the user". The actual answer is indeed to use viewDidLayoutSubviews.... mostrerai quella vista molte volte
Andrea

156

Dalla documentazione:

viewWillAppear:

Notifica al controller della vista che la sua vista sta per essere aggiunta a una gerarchia di viste.

viewDidAppear:

Notifica al controller di visualizzazione che la relativa visualizzazione è stata aggiunta a una gerarchia di visualizzazione.

Di conseguenza, i frame delle visualizzazioni secondarie non sono ancora impostati in viewWillAppear:

Il metodo appropriato per modificare la tua interfaccia utente prima che la visualizzazione venga presentata allo schermo è:

viewDidLayoutSubviews

Notifica al controller della vista che la sua vista ha appena disposto le sue viste secondarie.


2
Uffa, questo è fastidioso. Anche la formulazione della documentazione fa sembrare che viewWillApepar: e viewDidAppear: dovessero accadere direttamente uno dopo l'altro.
Jumhyn

2 mesi in attesa di questa risposta ... grazie l'ho trovata !! GRAZIE MILLE!
Rafael Ruiz Muñoz

6
Va notato che viewDidLayoutSubviewsverrà chiamato più volte e non sempre con lo stesso frame, (penso che sia chiamato con CGRectZero alla prima chiamata a volte). Viene chiamato per ogni vista secondaria aggiunta e altri cambiamenti nella vista.
bauerMusic

Mi sono imbattuto in questo tutto il giorno (viewDidLayoutSubviews è stato chiamato più volte, anche quando si chiudeva la visualizzazione), e ho appena detto ef con esso, e ho inserito la logica in un'istruzione if e fatto un bool che viene impostato su true dopo il il codice è stato eseguito una volta. Penso che debba esserci un modo più pulito, ma è già passato troppo tempo.
solenoide

dobbiamo andare fino in fondo! viewDidLayoutSubviews è orribile, seNeedDisplay non funziona
Yaro

9

chiamata

self.scrollView.layoutIfNeeded ()

nel tuo viewWillAppearmetodo. Successivamente puoi accedere alla sua cornice e avrà lo stesso valore di quando stampiviewDidAppear


1
No, non è corretto. Esempio: hai una barra di navigazione sullo schermo (dal tuo controller di navigazione). Anche dopo layoutIfNeeded (), l'altezza della barra di navigazione non è inclusa, quindi le dimensioni del frame cambieranno.
xaphod

Questo è un buon modo per forzare un ricalcolo della dimensione di un frame scrollview in modo che sia coerente in tutta la gerarchia delle chiamate del layout di visualizzazione se il frame scrollView è vincolato a vincoli all'interno della sua superview.
smakus

4

Nel mio caso, spostando tutti i metodi relativi ai frame in

override func viewWillLayoutSubviews()

ha funzionato perfettamente (stavo cercando di modificare i vincoli dallo storyboard).

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.