Nota : As coisas mudaram desde que essa pergunta foi feita; veja aqui uma boa visão geral recente.
Antes do layout automático, era possível alterar o ponto de ancoragem da camada de uma vista sem movê-la armazenando a moldura, definindo o ponto de ancoragem e restaurando a moldura.
Em um mundo de layout automático, não definimos mais quadros, mas as restrições não parecem ser a tarefa de ajustar a posição de uma vista de volta para onde queremos. Você pode hackear as restrições para reposicionar sua exibição, mas, na rotação ou em outros eventos de redimensionamento, elas se tornam inválidas novamente.
A seguinte ideia brilhante não funciona, pois cria um "Emparelhamento inválido de atributos de layout (esquerda e largura)":
layerView.layer.anchorPoint = CGPointMake(1.0, 0.5);
// Some other size-related constraints here which all work fine...
[self.view addConstraint:
[NSLayoutConstraint constraintWithItem:layerView
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:layerView
attribute:NSLayoutAttributeWidth
multiplier:0.5
constant:20.0]];
Minha intenção aqui era definir a borda esquerda de layerView
, a vista com o ponto de ancoragem ajustado, para metade de sua largura mais 20 (a distância que eu quero inserir da borda esquerda da superview).
É possível alterar o ponto de ancoragem, sem alterar o local de uma vista, em uma vista que é definida com o layout automático? Preciso usar valores codificados e editar a restrição em cada rotação? Eu espero que não.
Preciso alterar o ponto de ancoragem para que, quando aplico uma transformação à vista, obtenho o efeito visual correto.
fonte
layerView
sabe sua largura? Está colocando o lado direito em outra coisa?//Size-related constraints that work fine
- a largura e a altura da vista da camada são derivadas da superview.Respostas:
[EDIT: Aviso: toda a discussão que se segue será possivelmente obsoleta ou pelo menos fortemente mitigada pelo iOS 8, que pode não mais cometer o erro de acionar o layout no momento em que uma transformação de exibição é aplicada.]
Autolayout vs. Exibir transformações
O autolayout não funciona bem com as transformações de exibição. A razão, pelo que pude discernir, é que você não deve mexer com o quadro de uma exibição que possui uma transformação (que não seja a transformação de identidade padrão) - mas é exatamente isso que a execução automática faz. A maneira como o autolayout funciona é que, no
layoutSubviews
tempo de execução, percorre todas as restrições e define os quadros de todas as visualizações de acordo.Em outras palavras, as restrições não são mágicas; eles são apenas uma lista de tarefas.
layoutSubviews
é onde a lista de tarefas é concluída. E faz isso definindo quadros.Não posso deixar de considerar isso como um bug. Se eu aplicar essa transformação a uma exibição:
Espero ver a vista aparecer com o centro no mesmo lugar que antes e com a metade do tamanho. Mas, dependendo de suas restrições, pode não ser o que eu vejo.
[Na verdade, há uma segunda surpresa aqui: aplicar uma transformação a uma exibição aciona o layout imediatamente. Isso me parece outro bug. Ou talvez seja o coração do primeiro bug. O que eu esperaria é ser capaz de realizar uma transformação pelo menos até o tempo de layout, por exemplo, o dispositivo é girado - assim como eu posso executar uma animação de quadro até o tempo de layout. Mas, na verdade, o tempo de layout é imediato, o que parece errado.]
Solução 1: sem restrições
Uma solução atual é, se vou aplicar uma transformação semipermanente a uma visualização (e não apenas mexer temporariamente de alguma forma), para remover todas as restrições que a afetam. Infelizmente, isso geralmente faz com que a exibição desapareça da tela, pois o autolayout ainda ocorre e agora não há restrições para nos dizer onde colocar a exibição. Portanto, além de remover as restrições, defino as visualizações
translatesAutoresizingMaskIntoConstraints
como YES. Agora, a exibição funciona da maneira antiga, efetivamente não afetada pela execução automática. (Ela é afetada por autolayout, obviamente, mas as restrições máscara redimensionamento automático implícita causar o seu comportamento ser como era antes autolayout.)Solução 2: use apenas restrições apropriadas
Se isso parecer um pouco drástico, outra solução é definir as restrições para funcionarem corretamente com a transformação pretendida. Se uma vista é dimensionada exclusivamente por sua largura e altura fixas internas e posicionada apenas por seu centro, por exemplo, minha transformação de escala funcionará conforme o esperado. Neste código, removo as restrições existentes em uma subview (
otherView
) e as substituo por essas quatro restrições, fornecendo largura e altura fixas e fixando-as apenas pelo centro. Depois disso, minha transformação de escala funciona:O resultado é que, se você não tiver restrições que afetem o quadro de uma vista, o autolayout não tocará no quadro da vista - que é exatamente o que você procura quando uma transformação está envolvida.
Solução 3: Use uma subvisão
O problema com ambas as soluções acima é que perdemos os benefícios de restrições para posicionar nossa visão. Então, aqui está uma solução que resolve isso. Comece com uma visão invisível, cuja função é apenas atuar como host, e use restrições para posicioná-la. Dentro disso, coloque a visão real como uma subvisão. Use restrições para posicionar a subvisão na visualização do host, mas limite essas restrições a restrições que não serão revertidas quando aplicarmos uma transformação.
Aqui está uma ilustração:
A exibição em branco é a exibição do host; você deve fingir que é transparente e, portanto, invisível. A visualização em vermelho é sua subvisão, posicionada fixando seu centro no centro da visualização do host. Agora podemos escalar e girar a vista vermelha em torno de seu centro sem nenhum problema, e de fato a ilustração mostra que fizemos isso:
Enquanto isso, as restrições na exibição do host mantêm-no no lugar certo à medida que giramos o dispositivo.
Solução 4: use transformações de camada
Em vez de visualizar transformações, use transformações de camada, que não acionam o layout e, portanto, não causam conflito imediato com restrições.
Por exemplo, esta simples animação de exibição "pulsar" pode ser interrompida no layout automático:
Mesmo que no final não tenha havido alteração no tamanho da visualização, basta definir o
transform
layout de suas causas, e restrições podem fazer a visualização saltar. (Isso parece um bug ou o quê?) Mas se fizermos o mesmo com a Core Animation (usando um CABasicAnimation e aplicando a animação à camada da exibição), o layout não acontecerá e funcionará bem:fonte
Eu tinha um Isuue semelhante e acabei de ouvir Back da equipe de Autolayout da Apple. Eles sugerem usar a abordagem de exibição de contêiner que mate sugere, mas eles criam uma subclasse de UIView para substituir layoutSubviews e aplicam o layout personalizado Code lá - funciona como um encanto
O arquivo de cabeçalho se parece com isso para que você possa vincular sua subvisão de escolha diretamente no Interface Builder
e o arquivo m aplica o código especial assim:
Como você pode ver, ele agarra o ponto central da visualização quando é chamado pela primeira vez e reutiliza essa posição em outras chamadas para colocar a visualização em conformidade. Isso substitui o Código de Autolayout nesse sentido, que ocorre após [super layoutSubviews]; que contém o código de pagamento automático.
Dessa forma, não há mais a necessidade de evitar o Autolayout, mas você pode criar seu próprio autolayout quando os Comportamentos padrão não forem mais adequados. É claro que você pode aplicar coisas muito mais complicadas do que o que está nesse exemplo, mas isso era tudo que eu precisava, pois meu aplicativo pode usar apenas o modo retrato.
fonte
layoutSubviews
. Gostaria de acrescentar, porém, que a Apple aqui está apenas fazendo você fazer o que eu acho que eles deveriam ter feito (na implementação do Autolayout) o tempo todo.Eu acho uma maneira simples. E funciona no iOS 8 e iOS 9.
Como ajustar o anchorPoint ao usar o layout baseado em quadros:
Ao ajustar a âncora da vista com o layout automático, você faz a mesma coisa, mas de maneira restritiva. Quando o anchorPoint mudar de (0,5, 0,5) para (1, 0,5), o layerView se moverá para a esquerda com uma distância pela metade do comprimento da largura da vista, portanto, é necessário compensar isso.
Eu não entendo a restrição na pergunta. Portanto, suponha que você adicione uma restrição centerX em relação ao superView centerX com uma constante: layerView.centerX = superView.centerX + constant
fonte
Se você estiver usando o layout automático, não vejo como a definição manual da posição servirá a longo prazo, porque, eventualmente, o layout automático ultrapassará o valor da posição que você definiu ao calcular seu próprio layout.
Em vez disso, é necessário modificar as próprias restrições de layout para compensar as alterações produzidas pela configuração do anchorPoint. A função a seguir faz isso para visualizações não transformadas.
Admito que isso provavelmente não é tudo o que você esperava, pois geralmente o único motivo pelo qual você deseja modificar o anchorPoint é definir uma transformação. Isso exigiria uma função mais complexa que atualize as restrições de layout para refletir todas as alterações de quadro que poderiam ser causadas pela própria propriedade de transformação. Isso é complicado porque as transformações podem fazer muito no quadro. Uma transformação de escala ou rotação aumentaria o quadro, portanto, seria necessário atualizar as restrições de largura ou altura, etc.
Se você estiver usando apenas a transformação para uma animação temporária, o que está acima pode ser suficiente, pois não acredito que o layout automático impeça a animação em andamento de apresentar imagens que representam violações puramente transitórias das restrições.
fonte
tl: dr: Você pode criar uma saída para uma das restrições, para que possa ser removida e adicionada novamente.
Criei um novo projeto e adicionei uma vista com um tamanho fixo no centro. As restrições são mostradas na imagem abaixo.
Em seguida, adicionei uma saída para a vista que irá girar e para a restrição de alinhamento do centro x.
Mais tarde
viewDidAppear
, calculo o novo ponto de ancoragemEm seguida, removo a restrição para a qual tenho uma saída, crio uma nova que está deslocada e a adiciono novamente. Depois disso, digo à vista com a restrição alterada que ele precisa atualizar as restrições.
Finalmente, apenas adiciono a animação de rotação à vista de rotação.
A camada rotativa parece estar centralizada (o que deveria) mesmo ao girar o dispositivo ou fazer com que ele atualize as restrições. A nova restrição e o ponto de ancoragem alterado cancelam-se visualmente.
fonte
Minha solução atual é ajustar manualmente a posição da camada
viewDidLayoutSubviews
. Esse código também pode ser usadolayoutSubviews
para uma subclasse de exibição, mas no meu caso, minha exibição é uma exibição de nível superior dentro de um controlador de exibição, portanto, isso significava que eu não precisava criar uma subclasse UIView.Parece muito esforço, então outras respostas são bem-vindas.
fonte
Inspirando a resposta de meu mate, decidi tentar uma abordagem diferente. Uma exibição de contêiner, com restrições aplicadas adequadamente, pode ser usada. A vista com o ponto de ancoragem modificado pode ser colocada dentro da vista do contêiner, usando máscaras de redimensionamento automático e configurações explícitas de quadros, como nos velhos e maus tempos.
É um prazer, para a minha situação de qualquer maneira. As visualizações são configuradas aqui em viewDidLoad:
Não importa que os quadros para a visualização em vermelho sejam zero neste momento, devido à máscara de redimensionamento automático na visualização em verde.
Eu adicionei uma transformação de rotação em um método de ação, e este foi o resultado:
Pareceu se perder durante a rotação do dispositivo, então eu adicionei isso ao método viewDidLayoutSubviews:
fonte
view.layer
paz e fazer todo o seu trabalho em uma subcamada deview.layer
. Em outras palavras, a vista seria apenas um host, e todo o desenho e subcamada se transforma e assim por diante seria um nível inferior, não afetado por restrições.Eu acho que você está derrotando o objetivo do autolayout com esse método. Você mencionou que a largura e a borda direita dependem da super visão; então, por que não adicionar restrições ao longo dessa linha de pensamento?
Perca o paradigma anchorPoint / transform e tente:
A
NSLayoutAttributeRight
restrição significa exatamente comoanchorPoint = CGPointMake(1.0, 0.5)
, e aNSLayoutAttributeWidth
restrição é aproximadamente equivalente ao do código anteriorNSLayoutAttributeLeft
.fonte
anchorPoint
, meu argumento é que você não deve usá-lo para medições. O sistema de autolayout do UIView deve ser independente das transformações do CALayer. Então UIView: layout, CALayer: aparência / animaçõesviewDidLayoutSubviews
deve corrigir isso;position
sempre vai junto comanchorPoint
. Minha resposta mostra apenas como definir a restrição para a transformação de identidade.Esta pergunta e respostas me inspiraram a resolver meus próprios problemas com o Autolayout e o redimensionamento, mas com visualizações de rolagem. Eu criei um exemplo da minha solução no github:
https://github.com/hansdesmedt/AutoLayout-scrollview-scale
Este é um exemplo de um UIScrollView com paginação personalizada completamente feita no AutoLayout e é escalável (CATransform3DMakeScale) com pressão longa e toque para aumentar o zoom. Compatível com iOS 6 e 7.
fonte
É um tópico importante e eu não li todos os comentários, mas estava enfrentando o mesmo problema.
Eu tinha uma visão do XIB com autolayout. E eu queria atualizar sua propriedade de transformação. A incorporação da exibição em uma exibição de contêiner não resolve meu problema, porque o layout automático estava agindo de maneira estranha na exibição de contêiner. É por isso que acabei de adicionar a segunda exibição de contêiner para conter a exibição de contêiner que contém minha exibição e estava aplicando transformações nela.
fonte
tl; dr Digamos que você alterou o ponto de ancoragem para (0, 0). O ponto de ancoragem está agora no canto superior esquerdo. Sempre que vir a palavra central no layout automático, pense no canto superior esquerdo .
Quando você ajusta seu anchorPoint, apenas altera a semântica do AutoLayout. O layout automático não interfere no seu anchorPoint nem vice-versa. Se você não entender isso, terá um mau momento .
Exemplo:
Figura A. Nenhuma modificação no ponto de ancoragem
Figura B. Ponto de ancoragem alterado para o canto superior esquerdo
As figuras A e B são exatamente iguais. Nada mudou. Apenas a definição de que centro se refere mudou.
fonte