¿Cómo ajusto el punto de anclaje de un CALayer cuando se usa el diseño automático?

161

Nota : Las cosas han avanzado desde que se hizo esta pregunta; Vea aquí para una buena descripción general reciente.


Antes del diseño automático, puede cambiar el punto de anclaje de la capa de una vista sin mover la vista almacenando el marco, configurando el punto de anclaje y restaurando el marco.

En un mundo de diseño automático, ya no establecemos marcos, pero las restricciones no parecen estar a la altura de la tarea de ajustar la posición de una vista a donde queremos que esté. Puede piratear las restricciones para reposicionar su vista, pero en la rotación u otros eventos de cambio de tamaño, estos vuelven a ser inválidos nuevamente.

La siguiente idea brillante no funciona, ya que crea un "Emparejamiento no válido de atributos de diseño (izquierda y ancho)":

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]];

Mi intención aquí era establecer el borde izquierdo layerView, la vista con el punto de anclaje ajustado, a la mitad de su ancho más 20 (la distancia que quiero insertar desde el borde izquierdo de la supervista).

¿Es posible cambiar el punto de anclaje, sin cambiar la ubicación de una vista, en una vista que se presenta con diseño automático? ¿Necesito usar valores codificados y editar la restricción en cada rotación? Espero que no

Necesito cambiar el punto de anclaje para que cuando aplique una transformación a la vista, obtenga el efecto visual correcto.

jrturton
fuente
Dado lo que tiene aquí, parece que terminará con diseños ambiguos de todos modos, incluso si tiene el código anterior funcionando. ¿Cómo layerViewsabe su ancho? ¿Está pegando su lado derecho a otra cosa?
John Estropia
Eso está cubierto en el //Size-related constraints that work fineancho y la altura de la vista de capa se deriva de la supervista.
jrturton

Respuestas:

361

[EDITAR: Advertencia: iOS 8 posiblemente desactualizará o al menos mitigará toda la discusión subsiguiente, lo que ya no puede cometer el error de activar el diseño en el momento en que se aplica una transformación de vista.]

Autolayout vs. Ver transformaciones

La reproducción automática no se reproduce bien con las transformaciones de vista. La razón, por lo que puedo discernir, es que se supone que no debes meterte con el marco de una vista que tiene una transformación (que no sea la transformación de identidad predeterminada), pero eso es exactamente lo que hace la distribución automática. La forma en que funciona el autolayout es que en layoutSubviewsel tiempo de ejecución viene corriendo a través de todas las restricciones y configurando los marcos de todas las vistas en consecuencia.

En otras palabras, las restricciones no son mágicas; son solo una lista de tareas pendientes. layoutSubviewses donde se hace la lista de tareas pendientes. Y lo hace estableciendo marcos.

No puedo evitar considerar esto como un error. Si aplico esta transformación a una vista:

v.transform = CGAffineTransformMakeScale(0.5,0.5);

Espero ver la vista aparecer con su centro en el mismo lugar que antes y a la mitad del tamaño. Pero dependiendo de sus limitaciones, eso puede no ser lo que veo en absoluto.

[En realidad, hay una segunda sorpresa aquí: aplicar una transformación a una vista activa el diseño de inmediato. Esto me parece ser otro error. O tal vez es el corazón del primer error. Lo que esperaría es poder escapar con una transformación al menos hasta el tiempo de diseño, por ejemplo, el dispositivo gira, al igual que puedo salir con una animación de cuadro hasta el tiempo de diseño. Pero, de hecho, el tiempo de diseño es inmediato, lo que parece incorrecto.]

Solución 1: sin restricciones

Una solución actual es, si voy a aplicar una transformación semipermanente a una vista (y no simplemente moverla temporalmente de alguna manera), para eliminar todas las restricciones que la afectan. Desafortunadamente, esto generalmente hace que la vista desaparezca de la pantalla, ya que la distribución automática todavía tiene lugar, y ahora no hay restricciones para decirnos dónde colocar la vista. Entonces, además de eliminar las restricciones, configuro las vistas translatesAutoresizingMaskIntoConstraintsen SÍ. La vista ahora funciona a la antigua usanza, efectivamente no se ve afectada por la distribución automática. ( Obviamente, se ve afectado por el autolayout, pero las restricciones implícitas de la máscara de autoresizing hacen que su comportamiento sea igual que antes del autolayout).

Solución 2: use solo restricciones apropiadas

Si eso parece un poco drástico, otra solución es establecer las restricciones para que funcionen correctamente con una transformación prevista. Si una vista se dimensiona únicamente por su ancho y altura fijos internos, y se coloca únicamente por su centro, por ejemplo, mi transformación de escala funcionará como esperaba. En este código, elimino las restricciones existentes en una subvista ( otherView) y las reemplazo con esas cuatro restricciones, dándole un ancho y una altura fijos y fijándolos únicamente por su centro. Después de eso, mi transformación 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]];

El resultado es que si no tiene restricciones que afecten el marco de una vista, el autolayout no tocará el marco de la vista, que es justo lo que busca cuando se trata de una transformación.

Solución 3: usar una subvista

El problema con ambas soluciones anteriores es que perdemos los beneficios de las restricciones para posicionar nuestra opinión. Así que aquí hay una solución que resuelve eso. Comience con una vista invisible cuyo trabajo sea únicamente actuar como host, y use restricciones para posicionarlo. Dentro de eso, coloque la vista real como una subvista. Use restricciones para colocar la subvista dentro de la vista de host, pero limite esas restricciones a restricciones que no se defiendan cuando apliquemos una transformación.

Aquí hay una ilustración:

ingrese la descripción de la imagen aquí

La vista en blanco es vista de host; se supone que debes fingir que es transparente y, por lo tanto, invisible. La vista roja es su subvista, posicionada fijando su centro al centro de la vista del host. Ahora podemos escalar y rotar la vista roja alrededor de su centro sin ningún problema, y ​​de hecho la ilustración muestra que lo hemos hecho:

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

Y mientras tanto, las restricciones en la vista del host lo mantienen en el lugar correcto mientras giramos el dispositivo.

Solución 4: utilice transformaciones de capa en su lugar

En lugar de transformaciones de vista, use transformaciones de capa, que no activan el diseño y, por lo tanto, no causan un conflicto inmediato con las restricciones.

Por ejemplo, esta simple animación de vista de "latido" bien puede romperse en autolayout:

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

Aunque al final no hubo cambios en el tamaño de la vista, simplemente configurando el transformdiseño de sus causas, y las restricciones pueden hacer que la vista salte. (¿Se siente como un error o qué?) Pero si hacemos lo mismo con Core Animation (usando una animación CABasic y aplicando la animación a la capa de la vista), el diseño no sucede y funciona bien:

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];
mate
fuente
3
La transformación no es lo que me está causando el problema, solo establece el nuevo punto de anclaje. Sin embargo, intentaré eliminar las restricciones y probar la máscara de tamaño automático.
jrturton 01 de
1
No entiendo algo aquí. Solución 4: cuando se aplica una transformación a una capa de respaldo que afecta la transformación también, porque la propiedad de transformación de la vista se refiere a la transformación de la capa de respaldo
Luca Bartoletti
1
@Andy, por el contrario, en iOS 8, el problema desapareció por completo y toda mi respuesta es innecesaria
mate,
1
@Sunkas respondí en tu problema.
Ben Sinclair
2
En iOS 8, la transformación de capa también activa el diseño automático
CarmeloS
41

Tuve un Isuue similar y acabo de escuchar Back del equipo de Autolayout en Apple. Sugieren usar el Enfoque de vista de contenedor matt sugiere pero crean una subclase de UIView para sobrescribir las subvistas de diseño y aplicar el código de diseño personalizado allí: funciona como un encanto

El archivo de encabezado se ve así para que pueda vincular su subvista de elección directamente desde Interface Builder

#import <UIKit/UIKit.h>

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

y el Archivo m aplica el Código especial así:

#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 puede ver, toma el punto central de la Vista cuando se llama por primera vez y reutiliza esa Posición en otras llamadas para colocar la Vista en consecuencia. Esto sobrescribe el código de Autolayout en ese sentido, que tiene lugar después de [super layoutSubviews]; que contiene el código de autolayout.

De este modo, ya no hay necesidad de evitar Autolayout, pero puede crear su propio autolayout cuando los comportamientos predeterminados ya no sean apropiados. Por supuesto, puede aplicar cosas mucho más complicadas de lo que está en ese ejemplo, pero esto era todo lo que necesitaba, ya que mi aplicación solo puede usar el modo vertical.

sensslen
fuente
55
Gracias por publicar esto. Desde que escribí mi respuesta, definitivamente he llegado a comprender el valor de anular layoutSubviews. Sin embargo, solo agregaría que Apple aquí simplemente te hace hacer lo que creo que deberían haber hecho (en su implementación de Autolayout) todo el tiempo.
mate
Tienes razón, pero supongo que esto no está realmente relacionado con el problema en sí. ¡Ya que proporcionaron el Código anterior, estoy feliz con eso!
sensslen
44
Es triste y absurdo que el equipo de distribución automática recomiende crear una subclase de contenedor personalizada solo para evitar cómo la distribución automática rompe el comportamiento antiguo e intuitivo en el sistema de visualización.
algal
8

Encuentro una manera simple. Y funciona en iOS 8 e iOS 9.

Como ajustar anchorPoint cuando usa un diseño basado en marcos:

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

Cuando ajusta el anclaje de la vista con el diseño automático, hace lo mismo pero de manera restringida. Cuando anchorPoint cambia de (0.5, 0.5) a (1, 0.5), el layerView se moverá hacia la izquierda con una distancia de la mitad del ancho de la vista, por lo que debe compensar esto.

No entiendo la restricción en la pregunta, por lo tanto, suponga que agrega una restricción centerX relativa a superView centerX con una constante: layerView.centerX = superView.centerX + constante

layerView.layer.anchorPoint = CGPoint(1, 0.5)
let centerXConstraint = .....
centerXConstraint.constant = centerXConstraint.constant + layerView.bounds.size.width/2
seedante
fuente
Vaso de la mejor cerveza para ti mi amigo. Esta es una solución muy buena: D
Błażej
3

Si está utilizando el diseño automático, entonces no veo cómo la posición de configuración manual servirá a largo plazo porque, finalmente, el diseño automático reducirá el valor de posición que ha establecido cuando calcula su propio diseño.

Por el contrario, lo que se necesita es modificar las restricciones de diseño para compensar los cambios producidos al establecer el punto de anclaje. La siguiente función hace eso para vistas no 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 esto probablemente no sea todo lo que esperabas, ya que generalmente la única razón por la que deseas modificar el AnchorPoint es establecer una transformación. Eso requeriría una función más compleja que actualice las restricciones de diseño para reflejar todos los cambios de marco que podrían ser causados ​​por la propiedad de transformación en sí. Esto es complicado porque las transformaciones pueden hacer mucho al marco. Una transformación de escala o rotación agrandaría el marco, por lo que necesitaríamos actualizar las restricciones de ancho o alto, etc.

Si solo está utilizando la transformación para una animación temporal, entonces lo anterior puede ser suficiente, ya que no creo que el diseño automático evite que la animación en vuelo presente imágenes que representan violaciones puramente transitorias de las restricciones.

alga
fuente
Estoy usando transformaciones por desgracia. Establecer la posición está bien porque el método se llama cada vez que se presenta la vista, por lo que no se golpea. Estoy pensando que anular layoutRect podría ser una mejor solución (mencionada en una de sus respuestas anteriores) pero aún no la he probado.
jrturton
Por cierto, aquí hay una plataforma de prueba que he estado usando. Sería genial si alguien descubriera esto: github.com/algal/AutolayoutAnchorPointTestRig
algal
Esta debería ser la respuesta aceptada. La respuesta de @ matt ni siquiera se trata del punto de anclaje.
Iulian Onofrei
3

tl: dr: puede crear una salida para una de las restricciones para que se pueda eliminar y volver a agregar.


Creé un nuevo proyecto y agregué una vista con un tamaño fijo en el centro. Las restricciones se muestran en la imagen a continuación.

Las restricciones para el ejemplo más pequeño que se me ocurra.

A continuación, agregué una salida para la vista que va a girar y para la restricción de alineación centro x.

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

Más adelante viewDidAppearcalculo el nuevo punto de anclaje

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;

Luego elimino la restricción para la que tengo una salida, creo una nueva que está compensada y la agrego nuevamente. Después de eso, le digo a la vista con la restricción modificada que necesita actualizar las restricciones.

[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, solo agrego la animación de rotación a la vista giratoria.

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"];

Parece que la capa giratoria permanece centrada (que debería) incluso cuando gira el dispositivo o hace que actualice las restricciones. La nueva restricción y el punto de anclaje modificado se cancelan visualmente.

David Rönnqvist
fuente
1
Esto funcionaría para mi situación específica, no parece que sea posible una buena solución general.
jrturton 01 de
1

Mi solución actual es ajustar manualmente la posición de la capa viewDidLayoutSubviews. Este código también podría usarse layoutSubviewspara una subclase de vista, pero en mi caso mi vista es una vista de nivel superior dentro de un controlador de vista, por lo que esto significa que no tuve que hacer una subclase de UIView.

Parece demasiado esfuerzo, por lo que otras respuestas son bienvenidas.

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

    }
}
jrturton
fuente
1

Inspirada en la respuesta de Matt, decidí probar un enfoque diferente. Se puede utilizar una vista de contenedor, con restricciones aplicadas adecuadamente. La vista con el punto de anclaje modificado se puede colocar dentro de la vista de contenedor, utilizando máscaras de autorización y configuración de marco explícito como en los viejos tiempos.

Funciona de maravilla, para mi situación de todos modos. Las vistas se configuran aquí en 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;
}

No importa que los marcos para la vista roja sean cero en este punto, debido a la máscara de tamaño automático en la vista verde.

Agregué una transformación de rotación en un método de acción, y este fue el resultado:

ingrese la descripción de la imagen aquí

Parecía perderse durante la rotación del dispositivo, así que agregué esto al 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];

}
jrturton
fuente
Me pregunto si en su caso no hubiera sido más fácil dejarlo view.layersolo y hacer todo su trabajo en una subcapa de view.layer. En otras palabras, la vista sería solo un host, y todas las transformaciones de dibujo y subcapa, y así sucesivamente, serían un nivel inferior, no afectado por restricciones.
mate el
1
Bien, inspirado en su solución de subvista, ¡lo he agregado a mi ensayo! Gracias por dejarme jugar en tu sandbox ...
mate
0

Creo que estás derrotando el propósito del autolayout con ese método. Usted mencionó que el ancho y el borde derecho dependen de la supervista, entonces, ¿por qué no simplemente agregar restricciones a lo largo de esa línea de pensamiento?

Pierda el paradigma anchorPoint / transform y pruebe:

[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

La NSLayoutAttributeRightrestricción significa exactamente igual anchorPoint = CGPointMake(1.0, 0.5), y la NSLayoutAttributeWidthrestricción es más o menos equivalente a la de su código anterior NSLayoutAttributeLeft.

John Estropia
fuente
Gracias por su respuesta, pero no puedo "perder el punto de anclaje / paradigma de transformación". Necesito aplicar una transformación y ajustar el punto de anclaje para hacer la transformación correcta.
jrturton
Para explicar más, en este caso específico, la vista transformada a veces tendrá que plegarse en la pantalla girando a lo largo del eje Y en su extremo derecho. Por lo tanto, el punto de anclaje debe ser movido. También estoy buscando una solución general.
jrturton
Por supuesto, aún puede mantener el anchorPoint, mi punto es que no debe usarlo para mediciones. El sistema de distribución automática UIView debe ser independiente de las transformaciones CALayer. Entonces UIView: diseño, CALayer: apariencia / animaciones
John Estropia
1
Debería ser, pero no lo es. ¿Realmente has intentado esto? Cambiar el punto de anclaje compensa la posición de la capa después de que el diseño automático haya hecho su trabajo. Su solución no funciona con un punto de anclaje modificado.
jrturton
Sí, puedo usar diferentes puntos de anclaje para mis vistas con restricciones. Su respuesta en viewDidLayoutSubviewsdebería arreglar eso; positionsiempre va de la mano anchorPoint. Mi respuesta simplemente muestra cómo definir la restricción para la transformación de identidad.
John Estropia
0

Esta pregunta y respuestas me inspiraron a resolver mis propios problemas con Autolayout y escalado, pero con vistas de desplazamiento. Creé un ejemplo de mi solución en github:

https://github.com/hansdesmedt/AutoLayout-scrollview-scale

Este es un ejemplo de un UIScrollView con paginación personalizada completamente realizada en AutoLayout y es escalable (CATransform3DMakeScale) con una pulsación larga y toca para hacer zoom. Compatible con iOS 6 y 7.

Hans
fuente
0

Es un gran tema y no he leído todos los comentarios, pero estaba enfrentando el mismo problema.

Tenía una vista desde XIB con autolayout. Y quería actualizar su propiedad de transformación. Incrustar la vista en una vista de contenedor no resuelve mi problema porque la distribución automática estaba actuando de manera extraña en la vista de contenedor. Es por eso que acabo de agregar una segunda vista de contenedor para contener la vista de contenedor que contiene mi vista y estaba aplicando transformaciones en ella.

plamkata__
fuente
0

tl; dr Digamos que cambiaste el punto de anclaje a (0, 0). El punto de anclaje ahora está arriba a la izquierda. Cada vez que vea el centro de palabras en diseño automático, debe pensar en la esquina superior izquierda .

Cuando ajusta su AnchorPoint, simplemente cambia la semántica de AutoLayout. El diseño automático no interferirá con su anchorPoint ni viceversa. Si no comprende esto, lo pasará mal .


Ejemplo:

Figura A. Sin modificaciones en el punto de anclaje

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

Figura B. El punto de anclaje cambió a la esquina superior izquierda

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

La Figura A y la Figura B se ven exactamente iguales. Nada ha cambiado. Solo la definición de a qué centro se refiere cambió.

seo
fuente
Eso está muy bien, pero no responde a la pregunta de qué hacer cuando sus limitaciones no están relacionadas con el centro.
jrturton