¿Cómo hacer transforma en un CALayer?

Antes de escribir esta pregunta, tengo

  • Tuve experiencia con Affine transforma para vistas
  • lea la documentation de Transforms en la Guía de progtwigción de Quartz 2D
  • visto este detallado tutorial de CALayer
  • descargado y ejecutado el proyecto LayerPlayer de Github

Sin embargo, todavía tengo problemas para entender cómo hacer transformaciones básicas en una capa . Encontrar explicaciones y ejemplos simples para traducir, rotar y escalar ha sido difícil.

Hoy finalmente decidí sentarme, hacer un proyecto de testing y resolverlos. Mi respuesta es abajo.

Notas:

  • Solo hago Swift, pero si alguien más quiere agregar el código Objective-C, sé mi invitado.
  • En este punto, solo me preocupa comprender las transformaciones en 2D.

Lo esencial

Hay varias transformaciones diferentes que puede hacer en una capa, pero las básicas son

  • traducir (mover)
  • escala
  • girar

introduzca la descripción de la imagen aquí

Para hacer transformaciones en un CALayer , establece la propiedad de transform la capa en un tipo CATransform3D . Por ejemplo, para traducir una capa, haría algo como esto:

 myLayer.transform = CATransform3DMakeTranslation(20, 30, 0) 

La palabra Make se usa en el nombre para crear la transformación inicial: CATransform3D Hacer traducción. Las transformaciones subsiguientes que se aplican omiten el Make . Ver, por ejemplo, esta rotation seguida de una traducción:

 let rotation = CATransform3DMakeRotation(CGFloat.pi * 30.0 / 180.0, 20, 20, 0) myLayer.transform = CATransform3DTranslate(rotation, 20, 30, 0) 

Ahora que tenemos la base de cómo hacer una transformación, veamos algunos ejemplos de cómo hacer cada uno. Primero, sin embargo, mostraré cómo configuré el proyecto en caso de que quiera jugar con él también.

Preparar

Para los siguientes ejemplos, UIView una aplicación de vista única y agregué una UIView con un background azul claro al guión gráfico. Conecté la vista al controller de vista con el siguiente código:

 import UIKit class ViewController: UIViewController { var myLayer = CATextLayer() @IBOutlet weak var myView: UIView! override func viewDidLoad() { super.viewDidLoad() // setup the sublayer addSubLayer() // do the transform transformExample() } func addSubLayer() { myLayer.frame = CGRect(x: 0, y: 0, width: 100, height: 40) myLayer.backgroundColor = UIColor.blue.cgColor myLayer.string = "Hello" myView.layer.addSublayer(myLayer) } //******** Replace this function with the examples below ******** func transformExample() { // add transform code here ... } } 

Hay muchos types diferentes de CALayer , pero CATextLayer por utilizar CATextLayer para que las transformaciones sean más claras visualmente.

Traducir

La transformación de traducción mueve la capa. La syntax básica es

 CATransform3DMakeTranslation(tx: CGFloat, ty: CGFloat, tz: CGFloat) 

donde tx es el cambio en las coorderadas x, ty es el cambio en y, y tz es el cambio en z.

Ejemplo

introduzca la descripción de la imagen aquí

En iOS, el origen del sistema de coorderadas se encuentra en la esquina superior izquierda, por lo que si quisiéramos mover la capa 90 puntos hacia la derecha y 50 puntos hacia abajo, haríamos lo siguiente:

 myLayer.transform = CATransform3DMakeTranslation(90, 50, 0) 

Notas

  • Recuerde que puede pegar esto en el método transformExample() en el código del proyecto anterior.
  • Como solo vamos a tratar con dos dimensiones aquí, tz se establece en 0 .
  • La línea roja en la image de arriba va del centro de la location original al centro de la nueva location. Esto se debe a que las transformaciones se realizan en relación con el punto de anclaje y el punto de anclaje de forma pnetworkingeterminada está en el centro de la capa.

Escala

La transformación de la escala estira o aplasta la capa. La syntax básica es

 CATransform3DMakeScale(sx: CGFloat, sy: CGFloat, sz: CGFloat) 

donde sx , sy , y sz son los numbers para escalar (multiplicar) las coorderadas x, y y z, respectivamente.

Ejemplo

introduzca la descripción de la imagen aquí

Si quisiéramos la mitad del ancho y triplicamos la altura, haríamos lo siguiente

 myLayer.transform = CATransform3DMakeScale(0.5, 3.0, 1.0) 

Notas

  • Dado que solo estamos trabajando en dos dimensiones, simplemente multiplicamos las coorderadas z por 1.0 para que no se vean afectadas.
  • El punto rojo en la image de arriba representa el punto de anclaje. Observe cómo se realiza la escala en relación con el punto de anclaje. Es decir, todo está estirado hacia o lejos del punto de anclaje.

Girar

La transformación de rotation rota la capa alnetworkingedor del punto de anclaje (el centro de la capa de forma pnetworkingeterminada). La syntax básica es

 CATransform3DMakeRotation(angle: CGFloat, x: CGFloat, y: CGFloat, z: CGFloat) 

donde el angle es el ángulo en radianes, la capa debe rotarse x , y , z son los ejes alnetworkingedor de los cuales rotar. Establecer un eje en 0 cancela una rotation alnetworkingedor de ese eje particular.

Ejemplo

introduzca la descripción de la imagen aquí

Si quisiéramos rotar una capa en el sentido de las agujas del reloj 30 grados, haríamos lo siguiente:

 let degrees = 30.0 let radians = CGFloat(degrees * Double.pi / 180) myLayer.transform = CATransform3DMakeRotation(radians, 0.0, 0.0, 1.0) 

Notas

  • Dado que estamos trabajando en dos dimensiones, solo queremos que el plano xy se rote alnetworkingedor del eje z. Por lo tanto, establecemos y en 0.0 y establecemos z en 1.0 .
  • Esto giró la capa en el sentido de las agujas del reloj. Podríamos haber girado en el sentido contrario a las agujas del reloj al establecer z a -1.0 .
  • El punto rojo muestra dónde está el punto de anclaje. La rotation se realiza alnetworkingedor del punto de anclaje.

Múltiples transformaciones

Para combinar múltiples transformaciones podríamos usar concatinaciones como esta

 CATransform3DConcat(a: CATransform3D, b: CATransform3D) 

Sin embargo, haremos uno tras otro. La primera transformación usará Make en su nombre. Las siguientes transformaciones no usarán Make , pero tomarán la transformación anterior como un parámetro.

Ejemplo

introduzca la descripción de la imagen aquí

Esta vez combinamos las tres transformaciones anteriores.

 let degrees = 30.0 let radians = CGFloat(degrees * Double.pi / 180) // translate var transform = CATransform3DMakeTranslation(90, 50, 0) // rotate transform = CATransform3DRotate(transform, radians, 0.0, 0.0, 1.0) // scale transform = CATransform3DScale(transform, 0.5, 3.0, 1.0) // apply the transforms myLayer.transform = transform 

Notas

  • El order en que se realizan las transformaciones en materia.
  • Todo se hizo en relación con el punto de anclaje (punto rojo).

Una nota sobre el punto de anclaje y la position

Hicimos todas nuestras transformaciones anteriores sin cambiar el punto de anclaje. Sin embargo, a veces es necesario cambiarlo, como si quisieras rotar alnetworkingedor de algún otro punto además del centro. Sin embargo, esto puede ser un poco complicado.

El punto y la position del ancla están en el mismo lugar. El punto de anclaje se expresa como una unidad del sistema de coorderadas de la capa (el valor pnetworkingeterminado es 0.5, 0.5 ) y la position se expresa en el sistema de coorderadas de la superestructura. Se pueden configurar de esta manera.

 myLayer.anchorPoint = CGPoint(x: 0.0, y: 1.0) myLayer.position = CGPoint(x: 50, y: 50) 

Si solo establece el punto de anclaje sin cambiar la position, el marco cambiará para que la position quede en el lugar correcto. O más precisamente, el recuadro se vuelve a calcular en function del nuevo punto de anclaje y la position anterior. Esto generalmente da resultados inesperados. Los siguientes dos artículos tienen una excelente discusión sobre esto.

  • Acerca de anchorPoint
  • Traducir rotar traducir?

Ver también

  • Borde, esquinas networkingondeadas y sombra en un CALayer
  • Usando un borde con una ruta Bezier para una capa