Exibir mudanças de quadro entre viewWillAppear: e viewDidAppear:

85

Eu descobri um comportamento estranho em meu aplicativo, onde um conectado IBOutlettem seu quadro de visão conectado entre as chamadas em meu controlador de visão para viewWillAppear:e viewDidAppear:. Aqui está o código relevante em minha UIViewControllersubclasse:

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

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

e a saída de log resultante:

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

O que mostra claramente que o quadro está mudando entre as duas chamadas. Eu queria fazer a configuração com a view no viewDidLoadmétodo, mas se o conteúdo não estiver disponível para eu alterar até que esteja na tela, parece inútil. O que pode estar acontecendo?

Jumhyn
fonte
2
Você está usando autolayout? você está adicionando esta visão no construtor de interface ou programaticamente?
Andrea
O layout automático está ativado e esta visualização é criada no IB a partir de um storyboard.
Jumhyn
1
Eu nunca usei o storyboard, mas provavelmente está correto. O quadro de layout automático de suas visualizações é definido quando o mecanismo de layout automático inicia o cálculo. Tente perguntar a mesma coisa logo após super do método - (void) viewDidLayoutSubviews do seu controlador de visualização.
Andrea
Isso aciona com êxito meu evento no momento certo, mas esse método também é chamado sempre que executo qualquer animação na exibição.
Jumhyn
1
viewDidLayoutSubviewsera o caminho correto a seguir. Eu apenas tive que colocar todo o meu conteúdo em uma subvisualização para que o método não fosse chamado novamente sempre que eu alterasse o quadro da visão principal.
Jumhyn

Respostas:

112

Autolayoutfez uma grande mudança em como projetamos e desenvolvemos a GUI de nossas visualizações. Uma das principais diferenças é que autolayoutnão altera o tamanho de nossas visualizações imediatamente, mas apenas quando é acionado, ou seja, em um momento específico, mas podemos forçá-lo a recalcular nossas restrições imediatamente ou marcá-las como "em necessidade" de layout. Funciona assim -setNeedDisplay.
O grande desafio para mim foi entender e aceitar que não precisamos mais usar máscaras de redimensionamento automático, e o frame tornou-se uma propriedade inútil para posicionar nossas visualizações. Não precisamos mais pensar sobre a posição da visão, mas devemos pensar como queremos vê-los em um espaço relacionado entre si.
Quando queremos misturar a máscara de redimensionamento antigo e o layout automático, é que surgem os problemas. Devemos pensar na implementação do autolayout muito em breve e tentar evitar misturar a abordagem antiga em uma hierarquia de visualizações baseada no autolayout.
É bom ter uma visualização de contêiner que usa apenas máscaras de redimensionamento automático, como a visualização principal de um controlador de visualização, mas é melhor se não tentarmos misturar.
Eu nunca usei o storyboard, mas provavelmente está correto. Usando o Autolayout, o quadro de suas visualizações é definido quando o mecanismo de layout automático inicia seu cálculo. Tente perguntar a mesma coisa logo após o super - (void)viewDidLayoutSubviewsmétodo do seu controlador de visualização.
Este método é chamado quando o mecanismo de layout automático termina de calcular os quadros de suas visualizações.

Andrea
fonte
6
- (vazio) viewDidLayoutSubviews é a resposta para mim! Muito obrigado!
FrizzTheSnail
Esta resposta realmente não está correta. Sim, claro, obviamente, por (5?) Anos você tem que usar autolayout. Mas há inúmeras situações ( usando autolayout ) em que você precisa, digamos, adicionar algo em uma tela, "logo antes de aparecer para o usuário". (Se você fizer isso em viewDidAppear, terá uma oscilação. Se fizer isso em viewWillAppear - as posições estarão erradas.) A resposta real é usar viewDidLayoutSubviews.
Fattie de
add something on a screen, "just before it appears to the user". The actual answer is indeed to use viewDidLayoutSubviews.... você vai mostrar essa visão várias vezes
Andrea
156

Da documentação:

viewWillAppear:

Notifica o controlador de visualização que sua visualização está prestes a ser adicionada a uma hierarquia de visualização.

viewDidAppear:

Notifica o controlador de visualização que sua visualização foi adicionada a uma hierarquia de visualização.

Como resultado, os frames das subvisualizações ainda não foram definidos no viewWillAppear:

O método apropriado para modificar sua IU antes que a visualização seja apresentada na tela é:

viewDidLayoutSubviews

Notifica o controlador de visualização que sua visualização acabou de apresentar suas subvisualizações.

gsach
fonte
2
Ugh, isso é irritante. Até mesmo o texto da documentação faz parecer que viewWillApepar: e viewDidAppear: devem acontecer diretamente um após o outro.
Jumhyn
2 meses esperando por essa resposta ... obrigado achei !! MUITO OBRIGADO!
Rafael Ruiz Muñoz
6
Deve-se notar que viewDidLayoutSubviewsserá chamado várias vezes e nem sempre com o mesmo frame (acho que às vezes é chamado com CGRectZero na primeira chamada). Ele é chamado para cada subvisão adicionada e outras alterações na visão.
bauerMusic
Eu tenho me intrometido nisso o dia todo (viewDidLayoutSubviews sendo chamados várias vezes, incluindo ao descartar a visualização), e apenas disse o ef com ele, coloquei a lógica em uma declaração if e fiz um bool que é definido como verdadeiro após o o código foi executado uma vez. Acho que tem que haver uma maneira mais limpa, mas já se dedicou muito tempo a ela.
solenóide de
temos que chegar ao fundo disso! viewDidLayoutSubviews é horrível, seNeedDisplay não funciona
Yaro
9

ligar

self.scrollView.layoutIfNeeded ()

em seu viewWillAppearmétodo. Depois você pode acessar a sua moldura e terá o mesmo valor que você imprime noviewDidAppear

andrei
fonte
1
Não, isso não está correto. Exemplo: você tem uma barra de navegação na tela (do seu controlador de navegação). Mesmo depois de layoutIfNeeded (), a altura da barra de navegação não é incluída, portanto, o tamanho do quadro será alterado.
xaphod
Essa É uma boa maneira de forçar um recálculo de uma dimensão de quadro de scrollview para que seja consistente em toda a hierarquia de chamadas de layout de visualização se o quadro scrollView estiver vinculado a restrições dentro de sua supervisão.
smakus
4

No meu caso, mover todos os métodos relacionados a quadros para

override func viewWillLayoutSubviews()

funcionou perfeitamente (eu estava tentando modificar as restrições do storyboard).

Siddhesh Mahadeshwar
fonte