Como ajusto o ponto de ancoragem de um CALayer, quando o Auto Layout está sendo usado?


161

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.


Dado exatamente o que você tem aqui, parece que você acabará com layouts ambíguos, mesmo que o código acima funcione. Como layerViewsabe sua largura? Está colocando o lado direito em outra coisa?
John Estropia

Isso é coberto pela //Size-related constraints that work fine- a largura e a altura da vista da camada são derivadas da superview.
Jrturton 18/10

Respostas:


361

[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 layoutSubviewstempo 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:

v.transform = CGAffineTransformMakeScale(0.5,0.5);

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 translatesAutoresizingMaskIntoConstraintscomo 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:

NSMutableArray* cons = [NSMutableArray array];
for (NSLayoutConstraint* con in self.view.constraints)
    if (con.firstItem == self.otherView || con.secondItem == self.otherView)
        [cons addObject:con];

[self.view removeConstraints:cons];
[self.otherView removeConstraints:self.otherView.constraints];
[self.view addConstraint:
 [NSLayoutConstraint constraintWithItem:self.otherView attribute:NSLayoutAttributeCenterX relatedBy:0 toItem:self.view attribute:NSLayoutAttributeLeft multiplier:1 constant:self.otherView.center.x]];
[self.view addConstraint:
 [NSLayoutConstraint constraintWithItem:self.otherView attribute:NSLayoutAttributeCenterY relatedBy:0 toItem:self.view attribute:NSLayoutAttributeTop multiplier:1 constant:self.otherView.center.y]];
[self.otherView addConstraint:
 [NSLayoutConstraint constraintWithItem:self.otherView attribute:NSLayoutAttributeWidth relatedBy:0 toItem:nil attribute:0 multiplier:1 constant:self.otherView.bounds.size.width]];
[self.otherView addConstraint:
 [NSLayoutConstraint constraintWithItem:self.otherView attribute:NSLayoutAttributeHeight relatedBy:0 toItem:nil attribute:0 multiplier:1 constant:self.otherView.bounds.size.height]];

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:

insira a descrição da imagem aqui

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:

self.otherView.transform = CGAffineTransformScale(self.otherView.transform, 0.5, 0.5);
self.otherView.transform = CGAffineTransformRotate(self.otherView.transform, M_PI/8.0);

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:

[UIView animateWithDuration:0.3 delay:0
                    options:UIViewAnimationOptionAutoreverse
                 animations:^{
    v.transform = CGAffineTransformMakeScale(1.1, 1.1);
} completion:^(BOOL finished) {
    v.transform = CGAffineTransformIdentity;
}];

Mesmo que no final não tenha havido alteração no tamanho da visualização, basta definir o transformlayout 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:

CABasicAnimation* ba = [CABasicAnimation animationWithKeyPath:@"transform"];
ba.autoreverses = YES;
ba.duration = 0.3;
ba.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(1.1, 1.1, 1)];
[v.layer addAnimation:ba forKey:nil];

3
A transformação não é o que está me causando o problema, apenas definindo o novo ponto de ancoragem. Vou tentar remover as restrições e experimentar a máscara de redimensionamento automático.
jrturton

1
Eu não entendo algo aqui. Solution4: Quando uma transformação é aplicada uma camada de suporte que transformam afetar autolayout também, porque a propriedade da vista transformar está se referindo a transformação da camada de suporte
Luca Bartoletti

1
@Andy pelo contrário, no iOS 8 o problema é completamente desaparecido e minha resposta inteira é desnecessário
mate

1
@ Sunkas Eu respondi no seu problema.
Ben Sinclair

2
No iOS 8, camada transformar também disposição gatilhos auto
carmelos

41

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

#import <UIKit/UIKit.h>

@interface BugFixContainerView : UIView
@property(nonatomic,strong) IBOutlet UIImageView *knobImageView;
@end

e o arquivo m aplica o código especial assim:

#import "BugFixContainerView.h"

@implementation BugFixContainerView
- (void)layoutSubviews
{
    static CGPoint fixCenter = {0};
    [super layoutSubviews];
    if (CGPointEqualToPoint(fixCenter, CGPointZero)) {
        fixCenter = [self.knobImageView center];
    } else {
        self.knobImageView.center = fixCenter;
    }
}
@end

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.


5
Obrigado por postar isso. Desde que escrevi minha resposta, definitivamente entendi o valor da substituição 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.
mate

Você está certo, mas acho que isso não está realmente relacionado ao problema em si. Como eles forneceram o código acima, estou feliz com isso!
2141313

4
É triste e absurdo que a própria equipe de auto-pagamento recomende a criação de uma subclasse de contêiner personalizada apenas para solucionar como o auto-pagamento quebra o comportamento duradouro e intuitivo no sistema de exibição.
algal

8

Eu acho uma maneira simples. E funciona no iOS 8 e iOS 9.

Como ajustar o anchorPoint ao usar o layout baseado em quadros:

let oldFrame = layerView.frame
layerView.layer.anchorPoint = newAnchorPoint
layerView.frame = oldFrame

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

layerView.layer.anchorPoint = CGPoint(1, 0.5)
let centerXConstraint = .....
centerXConstraint.constant = centerXConstraint.constant + layerView.bounds.size.width/2

Copo da melhor cerveja para você, meu amigo. Esta é uma solução tão boa: D
Błażej

3

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.

/**
  Set the anchorPoint of view without changing is perceived position.

 @param view view whose anchorPoint we will mutate
 @param anchorPoint new anchorPoint of the view in unit coords (e.g., {0.5,1.0})
 @param xConstraint an NSLayoutConstraint whose constant property adjust's view x.center
 @param yConstraint an NSLayoutConstraint whose constant property adjust's view y.center

  As multiple constraints can contribute to determining a view's center, the user of this
 function must specify which constraint they want modified in order to compensate for the
 modification in anchorPoint
 */
void SetViewAnchorPointMotionlesslyUpdatingConstraints(UIView * view,CGPoint anchorPoint,
                                                       NSLayoutConstraint * xConstraint,
                                                       NSLayoutConstraint * yConstraint)
{
  // assert: old and new anchorPoint are in view's unit coords
  CGPoint const oldAnchorPoint = view.layer.anchorPoint;
  CGPoint const newAnchorPoint = anchorPoint;

  // Calculate anchorPoints in view's absolute coords
  CGPoint const oldPoint = CGPointMake(view.bounds.size.width * oldAnchorPoint.x,
                                 view.bounds.size.height * oldAnchorPoint.y);
  CGPoint const newPoint = CGPointMake(view.bounds.size.width * newAnchorPoint.x,
                                 view.bounds.size.height * newAnchorPoint.y);

  // Calculate the delta between the anchorPoints
  CGPoint const delta = CGPointMake(newPoint.x-oldPoint.x, newPoint.y-oldPoint.y);

  // get the x & y constraints constants which were contributing to the current
  // view's position, and whose constant properties we will tweak to adjust its position
  CGFloat const oldXConstraintConstant = xConstraint.constant;
  CGFloat const oldYConstraintConstant = yConstraint.constant;

  // calculate new values for the x & y constraints, from the delta in anchorPoint
  // when autolayout recalculates the layout from the modified constraints,
  // it will set a new view.center that compensates for the affect of the anchorPoint
  CGFloat const newXConstraintConstant = oldXConstraintConstant + delta.x;
  CGFloat const newYConstraintConstant = oldYConstraintConstant + delta.y;

  view.layer.anchorPoint = newAnchorPoint;
  xConstraint.constant = newXConstraintConstant;
  yConstraint.constant = newYConstraintConstant;
  [view setNeedsLayout];
}

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.


Infelizmente, estou usando transformações. A definição da posição é boa porque o método é chamado toda vez que a exibição é definida, para que não seja derrotada. Estou pensando em substituir o layoutRect pode ser uma solução melhor (mencionada em uma de suas respostas anteriores), mas ainda não a tentei.
Jrturton #

A propósito, aqui está um equipamento de teste que estou usando. Seria ótimo se alguém descobrisse isso: github.com/algal/AutolayoutAnchorPointTestRig
algal

Essa deve ser a resposta aceita. A resposta de @ matt nem é sobre ponto de ancoragem.
Iulian Onofrei 13/03/19

3

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.

As restrições para o menor exemplo em que pude pensar.

Em seguida, adicionei uma saída para a vista que irá girar e para a restrição de alinhamento do centro x.

@property (weak, nonatomic) IBOutlet UIView *rotatingView;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *xAlignmentContstraint;

Mais tarde viewDidAppear, calculo o novo ponto de ancoragem

UIView *view = self.rotatingView;

CGPoint rotationPoint = // The point I'm rotating around... (only X differs)
CGPoint anchorPoint = CGPointMake((rotationPoint.x-CGRectGetMinX(view.frame))/CGRectGetWidth(view.frame),
                                  (rotationPoint.y-CGRectGetMinY(view.frame))/CGRectGetHeight(view.bounds));

CGFloat xCenterDifference = rotationPoint.x-CGRectGetMidX(view.frame);

view.layer.anchorPoint = anchorPoint;

Em 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.

[self.view removeConstraint:self.xAlignmentContstraint];
self.xAlignmentContstraint = [NSLayoutConstraint constraintWithItem:self.rotatingView
                                                          attribute:NSLayoutAttributeCenterX
                                                          relatedBy:NSLayoutRelationEqual
                                                             toItem:self.view
                                                          attribute:NSLayoutAttributeCenterX
                                                         multiplier:1.0
                                                           constant:xDiff];
[self.view addConstraint:self.xAlignmentContstraint];
[self.view needsUpdateConstraints];

Finalmente, apenas adiciono a animação de rotação à vista de rotação.

CABasicAnimation *rotate = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
rotate.toValue = @(-M_PI_2);
rotate.autoreverses = YES;
rotate.repeatCount = INFINITY;
rotate.duration = 1.0;
rotate.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; 

[view.layer addAnimation:rotate forKey:@"myRotationAnimation"];

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.


1
Isso funcionaria para minha situação específica, não parece que uma solução geral agradável seja possível.
Jrturton #

1

Minha solução atual é ajustar manualmente a posição da camada viewDidLayoutSubviews. Esse código também pode ser usado layoutSubviewspara 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.

-(void)viewDidLayoutSubviews
{
    for (UIView *view in self.view.subviews)
    {
        CGPoint anchorPoint = view.layer.anchorPoint;
        // We're only interested in views with a non-standard anchor point
        if (!CGPointEqualToPoint(CGPointMake(0.5, 0.5),anchorPoint))
        {
            CGFloat xDifference = anchorPoint.x - 0.5;
            CGFloat yDifference = anchorPoint.y - 0.5;
            CGPoint currentPosition = view.layer.position;

            // Use transforms if we can, otherwise manually calculate the frame change
            // Assuming a transform is in use since we are changing the anchor point. 
            if (CATransform3DIsAffine(view.layer.transform))
            {
                CGAffineTransform current = CATransform3DGetAffineTransform(view.layer.transform);
                CGAffineTransform invert = CGAffineTransformInvert(current);
                currentPosition = CGPointApplyAffineTransform(currentPosition, invert);
                currentPosition.x += (view.bounds.size.width * xDifference);
                currentPosition.y += (view.bounds.size.height * yDifference);
                currentPosition = CGPointApplyAffineTransform(currentPosition, current);
            }
            else
            {
                CGFloat transformXRatio = view.bounds.size.width / view.frame.size.width;

                if (xDifference < 0)
                    transformXRatio = 1.0/transformXRatio;

                CGFloat transformYRatio = view.bounds.size.height / view.frame.size.height;
                if (yDifference < 0)
                    transformYRatio = 1.0/transformYRatio;

                currentPosition.x += (view.bounds.size.width * xDifference) * transformXRatio;
                currentPosition.y += (view.bounds.size.height * yDifference) * transformYRatio;
            }
            view.layer.position = currentPosition;
        }

    }
}

1

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:

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    UIView *redView = [UIView new];
    redView.translatesAutoresizingMaskIntoConstraints = NO;
    redView.backgroundColor = [UIColor redColor];
    [self.view addSubview:redView];

    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|-[redView]-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(redView)]];
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[redView]-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(redView)]];
    self.redView = redView;

    UIView *greenView = [UIView new];
    greenView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
    greenView.layer.anchorPoint = CGPointMake(1.0, 0.5);
    greenView.frame = redView.bounds;
    greenView.backgroundColor = [UIColor greenColor];
    [redView addSubview:greenView];
    self.greenView = greenView;

    CATransform3D perspective = CATransform3DIdentity;
    perspective.m34 = 0.005;
    self.redView.layer.sublayerTransform = perspective;
}

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:

insira a descrição da imagem aqui

Pareceu se perder durante a rotação do dispositivo, então eu adicionei isso ao método viewDidLayoutSubviews:

-(void)viewDidLayoutSubviews
{
    [super viewDidLayoutSubviews];
    [CATransaction begin];
    [CATransaction setDisableActions:YES];
    CATransform3D transform = self.greenView.layer.transform;
    self.greenView.layer.transform = CATransform3DIdentity;
    self.greenView.frame = self.redView.bounds;
    self.greenView.layer.transform = transform;
    [CATransaction commit];

}

Gostaria de saber se, no seu caso, não teria sido mais fácil simplesmente deixar em view.layerpaz e fazer todo o seu trabalho em uma subcamada de view.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.
matt

1
Ok, inspirado na sua solução de subvisão, adicionei isso ao meu ensaio! Obrigado por me deixar jogar em seu sandbox ...
Matt

0

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:

[self.view addConstraint:
[NSLayoutConstraint constraintWithItem:layerView
                             attribute:NSLayoutAttributeRight
                             relatedBy:NSLayoutRelationEqual 
                                toItem:self.view 
                             attribute:NSLayoutAttributeWidth 
                            multiplier:1.0f
                              constant:-somePadding]];
[self.view addConstraint:
[NSLayoutConstraint constraintWithItem:layerView
                             attribute:NSLayoutAttributeWidth
                             relatedBy:NSLayoutRelationEqual 
                                toItem:someViewWeDependTheWidthOn
                             attribute:NSLayoutAttributeWidth 
                            multiplier:0.5f // because you want it to be half of someViewWeDependTheWidthOn
                              constant:-20.0f]]; // your 20pt offset from the left

A NSLayoutAttributeRightrestrição significa exatamente como anchorPoint = CGPointMake(1.0, 0.5), e a NSLayoutAttributeWidthrestrição é aproximadamente equivalente ao do código anterior NSLayoutAttributeLeft.


Obrigado pela resposta, mas não posso "perder o paradigma do ponto de ancoragem / transformação". Preciso aplicar uma transformação e ajustar o ponto de ancoragem para fazer a transformação correta.
Jrturton 19/10

Para explicar melhor, nesse caso específico, às vezes, a vista transformada precisará dobrar na tela girando ao longo do eixo Y na sua extremidade direita. Portanto, o ponto de ancoragem deve ser movido. Também estou procurando uma solução geral.
Jrturton 19/10/12

Claro que você ainda pode manter o 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ções
John esotropia

1
Deveria ser, mas não é. Você realmente tentou isso? Alterar o ponto de ancoragem desloca a posição da camada depois que o layout automático faz seu trabalho. Sua solução não funciona com um ponto de ancoragem modificado.
Jrturton 19/10/12

Sim, eu posso usar diferentes pontos de ancoragem para minhas visualizações dispostas com restrições. Sua resposta viewDidLayoutSubviewsdeve corrigir isso; positionsempre vai junto com anchorPoint. Minha resposta mostra apenas como definir a restrição para a transformação de identidade.
John esotropia

0

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.


0

É 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.


0

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

#Before changing anchor point to top-left
view.size == superview.size
view.center == superview.center

Figura B. Ponto de ancoragem alterado para o canto superior esquerdo

view.layer.anchorPoint = CGPointMake(0, 0)
view.size == superview.size
view.center == superview.topLeft                <----- L0-0K, center is now top-left

As figuras A e B são exatamente iguais. Nada mudou. Apenas a definição de que centro se refere mudou.


Tudo bem, mas não responde à pergunta sobre o que fazer quando suas restrições não estão relacionadas ao centro.
Jrturton
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.