Marco, límites y centro de UIView

Me gustaría saber cómo usar estas propiedades de la manera correcta.

Según entiendo, frame se puede usar desde el contenedor de la vista que estoy creando. Establece la posición de vista relativa a la vista de contenedor. También establece el tamaño de esa vista.

También se puede usar el center desde el contenedor de la vista que estoy creando. Esta propiedad cambia la posición de la vista con respecto a su contenedor.

Finalmente, los bounds son relativos a la vista misma. Cambia el área dibujable para la vista.

¿Puedes dar más información sobre la relación entre el frame y los bounds ? ¿Qué pasa con las propiedades clipsToBounds y masksToBounds ?

Dado que la pregunta que hice se ha visto muchas veces, proporcionaré una respuesta detallada al respecto. Siéntase libre de modificarlo si quiere agregar más contenido correcto.

Primero un resumen de la pregunta: marco, límites y centro y sus relaciones.

Marco El marco de una vista ( CGRect ) es la posición de su rectángulo en el sistema de coordenadas de la superview . Por defecto, comienza en la parte superior izquierda.

Límites Los límites de una vista ( CGRect ) expresan un rectángulo de vista en su propio sistema de coordenadas.

Centro Un center es un CGPoint expresado en términos del sistema de coordenadas de la superview y determina la posición del punto central exacto de la vista.

Tomado de la posición UIView +, estas son las relaciones (no funcionan en el código ya que son ecuaciones informales) entre las propiedades anteriores:

  • frame.origin = center - (bounds.size / 2.0)

  • center = frame.origin + (bounds.size / 2.0)

  • frame.size = bounds.size

NOTA: estas relaciones no se aplican si las vistas se rotan. Para obtener más información, le sugiero que eche un vistazo a la siguiente imagen tomada de The Kitchen Drawer basada en el curso CS193p de Stanford . Los créditos van a @Rhubarb .

Marco, límites y centro

Usar el frame permite cambiar de posición y / o cambiar el tamaño de una vista dentro de su superview . Por lo general, puede usarse desde una superview , por ejemplo, cuando crea una subvista específica. Por ejemplo:

 // view1 will be positioned at x = 30, y = 20 starting the top left corner of [self view] // [self view] could be the view managed by a UIViewController UIView* view1 = [[UIView alloc] initWithFrame:CGRectMake(30.0f, 20.0f, 400.0f, 400.0f)]; view1.backgroundColor = [UIColor redColor]; [[self view] addSubview:view1]; 

Cuando necesita las coordenadas para dibujar dentro de una view , generalmente se refiere a los bounds . Un ejemplo típico podría ser dibujar dentro de una view una subvista como un recuadro de la primera. Dibujar la subvista requiere conocer los bounds de la supervista. Por ejemplo:

 UIView* view1 = [[UIView alloc] initWithFrame:CGRectMake(50.0f, 50.0f, 400.0f, 400.0f)]; view1.backgroundColor = [UIColor redColor]; UIView* view2 = [[UIView alloc] initWithFrame:CGRectInset(view1.bounds, 20.0f, 20.0f)]; view2.backgroundColor = [UIColor yellowColor]; [view1 addSubview:view2]; 

Se producen diferentes comportamientos cuando se cambian los bounds de una vista. Por ejemplo, si cambia el size los bounds , el frame cambia (y viceversa). El cambio ocurre alrededor del center de la vista. Use el siguiente código y vea lo que sucede:

 NSLog(@"Old Frame %@", NSStringFromCGRect(view2.frame)); NSLog(@"Old Center %@", NSStringFromCGPoint(view2.center)); CGRect frame = view2.bounds; frame.size.height += 20.0f; frame.size.width += 20.0f; view2.bounds = frame; NSLog(@"New Frame %@", NSStringFromCGRect(view2.frame)); NSLog(@"New Center %@", NSStringFromCGPoint(view2.center)); 

Además, si cambia el origin bounds , cambia el origin de su sistema de coordenadas interno. Por defecto, el origin está en (0.0, 0.0) (esquina superior izquierda). Por ejemplo, si cambia el origin de view1 , puede ver (comentar el código anterior si lo desea) que ahora la esquina superior izquierda de view2 toca la view1 . La motivación es bastante simple. Usted dice view1 que su esquina superior izquierda ahora está en la posición (20.0, 20.0) pero dado frame origin frame view2 comienza desde (20.0, 20.0) , coincidirán.

 CGRect frame = view1.bounds; frame.origin.x += 20.0f; frame.origin.y += 20.0f; view1.bounds = frame; 

El origin representa la posición de la view dentro de su superview pero describe la posición del centro de bounds .

Finalmente, los bounds y el origin no son conceptos relacionados. Ambos permiten derivar el frame de una vista (Ver ecuaciones anteriores).

Estudio de caso de View1

Esto es lo que sucede cuando se usa el siguiente fragmento.

 UIView* view1 = [[UIView alloc] initWithFrame:CGRectMake(30.0f, 20.0f, 400.0f, 400.0f)]; view1.backgroundColor = [UIColor redColor]; [[self view] addSubview:view1]; NSLog(@"view1's frame is: %@", NSStringFromCGRect([view1 frame])); NSLog(@"view1's bounds is: %@", NSStringFromCGRect([view1 bounds])); NSLog(@"view1's center is: %@", NSStringFromCGPoint([view1 center])); 

La imagen relativa.

enter image description here

En cambio, esto sucede si cambio los límites de [self view] como el siguiente.

 // previous code here... CGRect rect = [[self view] bounds]; rect.origin.x += 30.0f; rect.origin.y += 20.0f; [[self view] setBounds:rect]; 

La imagen relativa.

enter image description here

Aquí le dice a [self view] que su esquina superior izquierda ahora está en la posición (30.0, 20.0) pero dado que el origen del marco de view1 comienza desde (30.0, 20.0), coincidirán.

Referencias adicionales (para actualizar con otras referencias si lo desea)

  • Geometría UIView
  • Marcos y límites UIView

Acerca de clipsToBounds (fuente Apple doc)

Establecer este valor en YES hace que las subvistas se recorten a los límites del receptor. Si se establece en NO, las subvistas cuyas ttwigs se extienden más allá de los límites visibles del receptor no se recortan. El valor predeterminado es no.

En otras palabras, si el frame una vista es (0, 0, 100, 100) y su subvista es (90, 90, 30, 30) , verá solo una parte de esa subvista. Este último no excederá los límites de la vista principal.

masksToBounds es equivalente a clipsToBounds . En lugar de un UIView , esta propiedad se aplica a un CALayer . Debajo del capó, clipsToBounds llama a masksToBounds . Para obtener más referencias, eche un vistazo a ¿Cómo es la relación entre UIView’s clipsToBounds y CALayer’s masksToBounds? .

Esta pregunta ya tiene una buena respuesta, pero quiero complementarla con algunas imágenes más. Mi respuesta completa está aquí.

Para ayudarme a recordar el cuadro , pienso en un cuadro en una pared . Al igual que una imagen se puede mover a cualquier lugar de la pared, el sistema de coordenadas del marco de una vista es la supervista. (muro = supervista, marco = vista)

Para ayudarme a recordar los límites , pienso en los límites de una cancha de baloncesto . La pelota de baloncesto está en algún lugar dentro de la cancha al igual que el sistema de coordenadas de los límites de la vista se encuentra dentro de la vista en sí. (corte = ver, baloncesto / jugadores = contenido dentro de la vista)

Al igual que el marco, view.center también está en las coordenadas de la supervista.

Frame vs Bounds – Ejemplo 1

El rectángulo amarillo representa el marco de la vista. El rectángulo verde representa los límites de la vista. El punto rojo en ambas imágenes representa el origen del marco o límites dentro de sus sistemas de coordenadas.

 Frame origin = (0, 0) width = 80 height = 130 Bounds origin = (0, 0) width = 80 height = 130 

enter image description here


Ejemplo 2

 Frame origin = (40, 60) // That is, x=40 and y=60 width = 80 height = 130 Bounds origin = (0, 0) width = 80 height = 130 

enter image description here


Ejemplo 3

 Frame origin = (20, 52) // These are just rough estimates. width = 118 height = 187 Bounds origin = (0, 0) width = 80 height = 130 

enter image description here


Ejemplo 4

Es lo mismo que en el ejemplo 2, excepto que esta vez todo el contenido de la vista se muestra como se vería si no estuviera recortado a los límites de la vista.

 Frame origin = (40, 60) width = 80 height = 130 Bounds origin = (0, 0) width = 80 height = 130 

enter image description here


Ejemplo 5

 Frame origin = (40, 60) width = 80 height = 130 Bounds origin = (280, 70) width = 80 height = 130 

enter image description here

De nuevo, mira aquí mi respuesta con más detalles.

Encontré esta imagen más útil para entender el marco, los límites, etc.

enter image description here

También tenga en cuenta que frame.size != bounds.size cuando se gira la imagen.

  • La propiedad frame contiene el rectángulo frame, que especifica el tamaño y la ubicación de la vista en el sistema de coordenadas de su superview.
  • La propiedad bounds contiene el rectángulo de límites, que especifica el tamaño de la vista (y su origen de contenido) en el propio sistema de coordenadas local de la vista.
  • La propiedad central contiene el punto central conocido de la vista en el sistema de coordenadas de la supervista.

Creo que si lo piensas desde el punto de CALayer de CALayer , todo está más claro.

Frame no es realmente una propiedad distinta de la vista o capa en absoluto, es una propiedad virtual, calculada desde los límites, posición (centro de UIView ) y transformación.

Entonces, básicamente, cómo estos diseños de capa / vista realmente se deciden por estas tres propiedades (y anchorPoint), y cualquiera de estas tres propiedades no cambiará ninguna otra propiedad, como cambiar la transformación no cambia los límites.

Hay muy buenas respuestas con una explicación detallada de esta publicación. Me gustaría referir que hay otra explicación con representación visual para el significado de Frame, Bounds, Center, Transform, Bounds Origin en WWDC 2011 video Comprensión de UIKit Rendering a partir de @ 4: 22 a 20:10

Después de leer las respuestas anteriores, aquí agrego mis interpretaciones.

Supongamos que navegar en línea, el navegador web es su frame que decide dónde y qué tan grande mostrar la página web. Scroller of browser es su bounds.origin que decide qué parte de la página web se mostrará. bounds.origin es difícil de entender. La mejor forma de aprender es crear una aplicación de vista única, intentando modificar estos parámetros y ver cómo cambian las subvistas.

 - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. UIView *view1 = [[UIView alloc] initWithFrame:CGRectMake(100.0f, 200.0f, 200.0f, 400.0f)]; [view1 setBackgroundColor:[UIColor redColor]]; UIView *view2 = [[UIView alloc] initWithFrame:CGRectInset(view1.bounds, 20.0f, 20.0f)]; [view2 setBackgroundColor:[UIColor yellowColor]]; [view1 addSubview:view2]; [[self view] addSubview:view1]; NSLog(@"Old view1 frame %@, bounds %@, center %@", NSStringFromCGRect(view1.frame), NSStringFromCGRect(view1.bounds), NSStringFromCGPoint(view1.center)); NSLog(@"Old view2 frame %@, bounds %@, center %@", NSStringFromCGRect(view2.frame), NSStringFromCGRect(view2.bounds), NSStringFromCGPoint(view2.center)); // Modify this part. CGRect bounds = view1.bounds; bounds.origin.x += 10.0f; bounds.origin.y += 10.0f; // incase you need width, height //bounds.size.height += 20.0f; //bounds.size.width += 20.0f; view1.bounds = bounds; NSLog(@"New view1 frame %@, bounds %@, center %@", NSStringFromCGRect(view1.frame), NSStringFromCGRect(view1.bounds), NSStringFromCGPoint(view1.center)); NSLog(@"New view2 frame %@, bounds %@, center %@", NSStringFromCGRect(view2.frame), NSStringFromCGRect(view2.bounds), NSStringFromCGPoint(view2.center));