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.
fuente
layerView
sabe su ancho? ¿Está pegando su lado derecho a otra cosa?//Size-related constraints that work fine
ancho y la altura de la vista de capa se deriva de la supervista.Respuestas:
[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
layoutSubviews
el 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.
layoutSubviews
es 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:
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
translatesAutoresizingMaskIntoConstraints
en 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: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:
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:
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:
Aunque al final no hubo cambios en el tamaño de la vista, simplemente configurando el
transform
diseñ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:fuente
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
y el Archivo m aplica el Código especial así:
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.
fuente
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.Encuentro una manera simple. Y funciona en iOS 8 e iOS 9.
Como ajustar anchorPoint cuando usa un diseño basado en marcos:
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
fuente
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.
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.
fuente
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.
A continuación, agregué una salida para la vista que va a girar y para la restricción de alineación centro x.
Más adelante
viewDidAppear
calculo el nuevo punto de anclajeLuego 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.
Finalmente, solo agrego la animación de rotación a la vista giratoria.
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.
fuente
Mi solución actual es ajustar manualmente la posición de la capa
viewDidLayoutSubviews
. Este código también podría usarselayoutSubviews
para 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.
fuente
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:
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:
Parecía perderse durante la rotación del dispositivo, así que agregué esto al método viewDidLayoutSubviews:
fuente
view.layer
solo y hacer todo su trabajo en una subcapa deview.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.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:
La
NSLayoutAttributeRight
restricción significa exactamente igualanchorPoint = CGPointMake(1.0, 0.5)
, y laNSLayoutAttributeWidth
restricción es más o menos equivalente a la de su código anteriorNSLayoutAttributeLeft
.fuente
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 / animacionesviewDidLayoutSubviews
debería arreglar eso;position
siempre va de la manoanchorPoint
. Mi respuesta simplemente muestra cómo definir la restricción para la transformación de identidad.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.
fuente
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.
fuente
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
Figura B. El punto de anclaje cambió a la esquina superior izquierda
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ó.
fuente