Sinto que é um paradigma bastante comum mostrar / ocultar UIViews
, na maioria das vezes UILabels
, dependendo da lógica de negócios. Minha pergunta é: qual é a melhor maneira de usar o AutoLayout para responder a visualizações ocultas como se seu quadro fosse 0x0. Aqui está um exemplo de uma lista dinâmica de 1-3 recursos.
No momento, tenho um espaço superior de 10 px do botão até o último rótulo, o que obviamente não desliza para cima quando o rótulo está oculto. A partir de agora, criei uma saída para essa restrição e modifique a constante, dependendo de quantos rótulos estou exibindo. Obviamente, isso é um pouco invasivo, pois estou usando valores constantes negativos para pressionar o botão sobre os quadros ocultos. Também é ruim porque não está sendo restrito a elementos de layout reais, apenas cálculos estáticos furtivos com base em alturas / preenchimentos conhecidos de outros elementos e obviamente lutando contra o que o AutoLayout foi criado.
Obviamente, eu poderia simplesmente criar novas restrições, dependendo dos meus rótulos dinâmicos, mas isso é muita microgerenciamento e muita verbosidade para tentar recolher apenas alguns espaços em branco. Existem abordagens melhores? Alterar o tamanho do quadro 0,0 e permitir que o AutoLayout faça seu trabalho sem manipulação de restrições? Removendo completamente as visualizações?
Honestamente, apenas modificar a constante do contexto da exibição oculta exige uma única linha de código com cálculo simples. Recriar novas restrições com constraintWithItem:attribute:relatedBy:toItem:attribute:multiplier:constant:
parece muito pesado.
Editar fev 2018 : Veja a resposta de Ben com UIStackView
s
fonte
Respostas:
UIStackView
é provavelmente o caminho a seguir para o iOS 9+. Além de lidar com a exibição oculta, ele também removerá espaçamento e margens adicionais, se configurados corretamente.fonte
Minha preferência pessoal por mostrar / ocultar vistas é criar um IBOutlet com a restrição de largura ou altura apropriada.
Em seguida, atualizo o
constant
valor para0
ocultar ou qualquer que seja o valor que deve ser exibido.A grande vantagem dessa técnica é que restrições relativas serão mantidas. Por exemplo, digamos que você tenha a visualização A e a visualização B com um espaço horizontal de x . Quando a vista A largura
constant
está definida como0.f
vista, a vista B se move para a esquerda para preencher esse espaço.Não há necessidade de adicionar ou remover restrições, o que é uma operação pesada. Simplesmente atualizar as restrições
constant
fará o truque.fonte
A solução de usar uma constante
0
quando oculta e outra constante se você a mostrar novamente é funcional, mas não é satisfatória se o seu conteúdo tiver um tamanho flexível. Você precisaria medir seu conteúdo flexível e definir um retorno constante. Isso parece errado e tem problemas se o conteúdo mudar de tamanho devido a eventos do servidor ou da interface do usuário.Eu tenho uma solução melhor.
A idéia é definir a regra de altura 0 para ter alta prioridade quando ocultamos o elemento, para que ele não ocupe espaço no layout automático.
Aqui está como você faz isso:
1. configure uma largura (ou altura) de 0 no construtor de interfaces com baixa prioridade.
O Interface Builder não grita sobre conflitos porque a prioridade é baixa. Teste o comportamento da altura definindo temporariamente a prioridade para 999 (1000 é proibido sofrer alterações programáticas, para que não o usemos). O construtor de interfaces provavelmente agora gritará sobre restrições conflitantes. Você pode corrigi-los definindo prioridades em objetos relacionados para 900 ou mais.
2. Adicione uma tomada para poder modificar a prioridade da restrição de largura no código:
3. Ajuste a prioridade ao ocultar seu elemento:
fonte
Nesse caso, mapeio a altura do rótulo do autor para um IBOutlet apropriado:
e quando defino a altura da restrição como 0,0f, preservamos o "preenchimento", porque a altura do botão Reproduzir permite isso.
fonte
Há muitas soluções aqui, mas minha abordagem usual é diferente novamente :)
Configure dois conjuntos de restrições semelhantes à resposta de Jorge Arimany e TMin:
Todas as três restrições marcadas têm o mesmo valor para a constante. As restrições marcadas como A1 e A2 têm prioridade definida como 500, enquanto a restrição marcada como B tem prioridade definida como 250 (ou
UILayoutProperty.defaultLow
no código).Conecte a restrição B a um IBOutlet. Então, quando você oculta o elemento, basta definir a prioridade da restrição como alta (750):
constraintB.priority = .defaultHigh
Mas quando o elemento estiver visível, defina a prioridade novamente como baixa (250):
constraintB.priority = .defaultLow
A vantagem (reconhecidamente menor) dessa abordagem em relação à alteração
isActive
da restrição B é que você ainda tem uma restrição de trabalho se o elemento transitório for removido da visualização por outros meios.fonte
Subclasse a vista e substitua
func intrinsicContentSize() -> CGSize
. Basta retornarCGSizeZero
se a vista estiver oculta.fonte
invalidateIntrinsicContentSize
. Você pode fazer isso de forma anuladasetHidden
.Acabei de descobrir que, para obter um UILabel para não ocupar espaço, você deve ocultá-lo E definir seu texto como uma string vazia. (iOS 9)
Conhecer esse fato / bug pode ajudar algumas pessoas a simplificar seus layouts, possivelmente até o da pergunta original, então achei que o postaria.
fonte
Estou surpreso que não exista uma abordagem mais elegante fornecida pelo
UIKit
para esse comportamento desejado. Parece uma coisa muito comum querer poder fazer.Desde que conectamos as restrições
IBOutlets
e definimos suas constantes para0
que pareçam indecentes (e causouNSLayoutConstraint
avisos quando a visualização tinha subvisões), decidi criar uma extensão que fornece uma abordagem simples e com estado de ocultar / mostrar umUIView
que tenha restrições de layout automáticoApenas oculta a vista e remove as restrições externas. Quando você mostra a exibição novamente, ela adiciona as restrições novamente. A única ressalva é que você precisará especificar restrições flexíveis de failover para as vistas circundantes.
Editar Esta resposta está direcionada ao iOS 8.4 e abaixo. No iOS 9, basta usar a
UIStackView
abordagem.fonte
A melhor prática é que, quando tudo tiver as restrições de layout corretas, adicione uma altura ou restrição, dependendo de como você deseja que as vistas ao redor movam e conectem a restrição a uma
IBOutlet
propriedade.Verifique se suas propriedades estão
strong
no código, basta definir a constante como 0 e ativá- la, ocultar o conteúdo ou desativá- lo para mostrar o conteúdo. Isso é melhor do que mexer com o valor constante e restaurá-lo. Não esqueça de ligar
layoutIfNeeded
depois.Se o conteúdo a ser oculto estiver agrupado, a melhor prática é colocar tudo em uma visualização e adicionar restrições a essa visualização.
Depois de configurar, você pode testá-lo no IntefaceBuilder, definindo sua restrição como 0 e depois voltando ao valor original. Não se esqueça de verificar outras prioridades de restrições, para que, quando ocultas, não haja conflito algum. outra maneira de testá-lo é colocá-lo em 0 e definir a prioridade como 0, mas não esqueça de restaurá-lo para a prioridade mais alta novamente.
fonte
Eu construo uma categoria para atualizar restrições facilmente:
Resposta aqui: Ocultar o UIView do autolayout: Como obter o NSLayoutConstraint existente para atualizar este
fonte
Meu método preferido é muito semelhante ao sugerido por Jorge Arimany.
Eu prefiro criar várias restrições. Primeiro, crie suas restrições para quando o segundo rótulo estiver visível. Crie uma saída para a restrição entre o botão e o segundo rótulo (se você estiver usando objc, verifique se ele é forte). Essa restrição determina a altura entre o botão e o segundo rótulo quando estiver visível.
Em seguida, crie outra restrição que especifique a altura entre o botão e o rótulo superior quando o segundo botão estiver oculto. Crie uma tomada com a segunda restrição e verifique se essa tomada possui um forte ponteiro. Em seguida, desmarque a
installed
caixa de seleção no construtor de interface e verifique se a prioridade da primeira restrição é menor que a segunda prioridade de restrições.Finalmente, quando você oculta o segundo rótulo, alterne o
.isActive
propriedade dessas restrições e chamesetNeedsDisplay()
E é isso, sem números mágicos, sem matemática, se você tiver várias restrições para ativar e desativar, você pode até usar coleções de tomadas para mantê-las organizadas por estado. (O AKA mantém todas as restrições ocultas em um OutletCollection e as não ocultas em outro e apenas itera sobre cada coleção, alternando seu status .isActive).
Eu sei que Ryan Romanchuk disse que não queria usar várias restrições, mas acho que isso não é microgerenciamento e é mais simples que criar dinamicamente visualizações e restrições programaticamente (que é o que eu acho que ele queria evitar se eu estou lendo a pergunta corretamente).
Eu criei um exemplo simples, espero que seja útil ...
fonte
Também fornecerei minha solução para oferecer variedade.) Acho que a criação de uma saída para a largura / altura de cada item mais o espaçamento é ridícula e explode o código, os possíveis erros e o número de complicações.
Meu método remove todas as visualizações (no meu caso, instâncias UIImageView), seleciona quais precisam ser adicionadas novamente e, em um loop, adiciona novamente cada uma e cria novas restrições. É realmente muito simples, por favor, siga adiante. Aqui está o meu código rápido e sujo para fazer isso:
Recebo um layout e espaçamento limpos e consistentes. Meu código usa Maçonaria, eu recomendo: https://github.com/SnapKit/Masonry
fonte
Experimente o BoxView , ele torna o layout dinâmico conciso e legível.
No seu caso, é:
fonte