Cacao: ¿Cuál es la diferencia entre el marco y los límites?

587

UIViewy todas sus subclases tienen las propiedades framey bounds. ¿Cual es la diferencia?

mk12
fuente
1
Marco de comprensión macoscope.com/blog/understanding-frame
onmyway133
para mí la explicación más concisa está aquí: videos.raywenderlich.com/courses/99-scroll-view-school/lessons/…
Vlad
1
No hay nada conciso sobre un video de 2:30. Puedo leer una o dos oraciones mucho más rápido que las 2:30.
dsjoerg
Para mí, es mucho más claro si pienso de esta manera: Marco = Límites y posición
Serg

Respuestas:

902

Los límites de una UIView es el rectángulo , expresado como una ubicación (x, y) y un tamaño (ancho, alto) relativo a su propio sistema de coordenadas (0,0).

El marco de una UIView es el rectángulo , expresado como una ubicación (x, y) y un tamaño (ancho, alto) relativo a la supervista que contiene.

Entonces, imagine una vista que tiene un tamaño de 100x100 (ancho x alto) posicionada en 25,25 (x, y) de su supervista. El siguiente código imprime los límites y el marco de esta vista:

// This method is in the view controller of the superview
- (void)viewDidLoad {
    [super viewDidLoad];

    NSLog(@"bounds.origin.x: %f", label.bounds.origin.x);
    NSLog(@"bounds.origin.y: %f", label.bounds.origin.y);
    NSLog(@"bounds.size.width: %f", label.bounds.size.width);
    NSLog(@"bounds.size.height: %f", label.bounds.size.height);

    NSLog(@"frame.origin.x: %f", label.frame.origin.x);
    NSLog(@"frame.origin.y: %f", label.frame.origin.y);
    NSLog(@"frame.size.width: %f", label.frame.size.width);
    NSLog(@"frame.size.height: %f", label.frame.size.height);
}

Y la salida de este código es:

bounds.origin.x: 0
bounds.origin.y: 0
bounds.size.width: 100
bounds.size.height: 100

frame.origin.x: 25
frame.origin.y: 25
frame.size.width: 100
frame.size.height: 100

Por lo tanto, podemos ver que en ambos casos, el ancho y el alto de la vista son los mismos, independientemente de si estamos mirando los límites o el marco. Lo que es diferente es el posicionamiento x, y de la vista. En el caso de los límites, las coordenadas xey están en 0,0 ya que estas coordenadas son relativas a la vista en sí. Sin embargo, las coordenadas del marco x e y son relativas a la posición de la vista dentro de la vista principal (que anteriormente dijimos que estaba en 25,25).

También hay una gran presentación que cubre UIViews. Vea las diapositivas 1-20 que no solo explican la diferencia entre marcos y límites, sino que también muestran ejemplos visuales.

shek
fuente
37
Entonces, los límites x, y para siempre serán 0,0 ya que es la ubicación del objeto en ... sí mismo. ¿O podría dar un escenario donde no sería?
mk12
55
Hasta donde yo sé, el origen de los límites siempre será 0,0
shek
77
En realidad, theborders.origin puede ser algo más que 0,0. Use setBoundsOrigin: para mover / traducir el origen. Para obtener más información, consulte "Ver geometría" en la "Ver guía de programación para cacao".
Meltemi el
127
UIScrollView es un ejemplo donde los límites pueden cambiar para mostrar diferentes regiones de contenido dentro de una vista.
Brad Larson
92
Un pequeño consejo: el uso de NSStringFromCGRect puede ahorrar algo de tiempo para registrar las rectificaciones.
berilio
647

Respuesta corta

marco = ubicación y tamaño de una vista utilizando el sistema de coordenadas de la vista principal

  • Importante para: colocar la vista en el padre

límites = ubicación y tamaño de una vista usando su propio sistema de coordenadas

  • Importante para: colocar el contenido o las subvistas de la vista dentro de sí mismo

Respuesta detallada

Para ayudarme a recordar el marco , pienso en un marco en una pared . El marco de la imagen es como el borde de una vista. Puedo colgar la foto en cualquier lugar que quiera en la pared. Del mismo modo, puedo poner una vista en cualquier lugar que desee dentro de una vista principal (también llamada supervista). La vista principal es como el muro. El origen del sistema de coordenadas en iOS es la esquina superior izquierda. Podemos poner nuestra vista en el origen de la supervista configurando las coordenadas xy del marco de vista en (0, 0), que es como colgar nuestra imagen en la esquina superior izquierda de la pared. Para moverlo hacia la derecha, aumente x, para moverlo hacia abajo aumente y.

Para ayudarme a recordar los límites , pienso en una cancha de baloncesto donde a veces el baloncesto queda fuera de límites . Estás botando la pelota por toda la cancha de baloncesto, pero realmente no te importa dónde está la cancha. Podría ser en un gimnasio, o afuera en una escuela secundaria, o en frente de su casa. No importa. Solo quieres jugar baloncesto. Del mismo modo, el sistema de coordenadas para los límites de una vista solo se preocupa por la vista misma. No sabe nada acerca de dónde se encuentra la vista en la vista principal. El origen de los límites (punto (0, 0) por defecto) es la esquina superior izquierda de la vista. Cualquier subvista que tenga esta vista se presenta en relación con este punto. Es como llevar el baloncesto a la esquina delantera izquierda de la cancha.

Ahora la confusión surge cuando intentas comparar marcos y límites. Sin embargo, en realidad no es tan malo como parece al principio. Usemos algunas imágenes para ayudarnos a entender.

Frame vs Bounds

En la primera imagen a la izquierda tenemos una vista que se encuentra en la parte superior izquierda de su vista principal. El rectángulo amarillo representa el marco de la vista. A la derecha vemos la vista nuevamente, pero esta vez la vista principal no se muestra. Esto se debe a que los límites no conocen la vista principal. El rectángulo verde representa los límites de la vista. El punto rojo en ambas imágenes representa el origen del marco o los límites.

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

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

ingrese la descripción de la imagen aquí

Entonces, el marco y los límites eran exactamente los mismos en esa imagen. Veamos un ejemplo en el que son diferentes.

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

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

ingrese la descripción de la imagen aquí

Entonces puede ver que al cambiar las coordenadas xy del marco se mueve en la vista principal. Pero el contenido de la vista en sí todavía se ve exactamente igual. Los límites no tienen idea de que algo es diferente.

Hasta ahora, el ancho y la altura del marco y los límites han sido exactamente los mismos. Sin embargo, eso no siempre es cierto. Mira lo que sucede si giramos la vista 20 grados en sentido horario. (La rotación se realiza mediante transformaciones. Consulte la documentación y estos ejemplos de vista y capa para obtener más información).

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

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

ingrese la descripción de la imagen aquí

Puedes ver que los límites siguen siendo los mismos. ¡Todavía no saben que ha sucedido nada! Sin embargo, los valores del cuadro han cambiado.

Ahora es un poco más fácil ver la diferencia entre marco y límites, ¿no? El artículo Probablemente no comprende los marcos y límites define un marco de vista como

... el cuadro delimitador más pequeño de esa vista con respecto a su sistema de coordenadas primario, incluidas las transformaciones aplicadas a esa vista.

Es importante tener en cuenta que si transforma una vista, el marco queda indefinido. Entonces, en realidad, el marco amarillo que dibujé alrededor de los límites verdes rotados en la imagen de arriba nunca existe. Eso significa que si gira, escala o realiza alguna otra transformación, ya no debería usar los valores del marco. Sin embargo, aún puede usar los valores de límites. Los documentos de Apple advierten:

Importante: Si la transformpropiedad de una vista no contiene la transformación de identidad, el marco de esa vista es indefinido y también lo son los resultados de sus comportamientos de autorización.

Más bien desafortunado sobre el autoresizing ... Sin embargo, hay algo que puede hacer.

Los documentos de Apple indican:

Al modificar la transformpropiedad de su vista, todas las transformaciones se realizan en relación con el punto central de la vista.

Entonces, si necesita mover una vista en el padre después de que se haya realizado una transformación, puede hacerlo cambiando las view.centercoordenadas. Al igual que frame, centerutiliza el sistema de coordenadas de la vista principal.

Ok, eliminemos nuestra rotación y centrémonos en los límites. Hasta ahora, el origen de los límites siempre se ha mantenido en (0, 0). Sin embargo, no tiene que hacerlo. ¿Qué pasa si nuestra vista tiene una gran subvista que es demasiado grande para mostrarla de una vez? Lo haremos UIImageViewcon una imagen grande. Aquí está nuestra segunda imagen desde arriba nuevamente, pero esta vez podemos ver cómo se vería todo el contenido de la subvista de nuestra vista.

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

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

ingrese la descripción de la imagen aquí

Solo la esquina superior izquierda de la imagen puede caber dentro de los límites de la vista. Ahora mira lo que sucede si cambiamos las coordenadas de origen de los límites.

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

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

ingrese la descripción de la imagen aquí

El marco no se ha movido en la supervista, pero el contenido dentro del marco ha cambiado porque el origen del rectángulo de límites comienza en una parte diferente de la vista. Esta es toda la idea detrás UIScrollViewde ay sus subclases (por ejemplo, a UITableView). Consulte Comprensión de UIScrollView para obtener más explicaciones.

Cuándo usar frame y cuándo usar límites

Como framerelaciona la ubicación de una vista en su vista principal, la usa cuando realiza cambios externos , como cambiar su ancho o encontrar la distancia entre la vista y la parte superior de su vista principal.

Use boundscuando realice cambios internos , como dibujar cosas u organizar subvistas dentro de la vista. También use los límites para obtener el tamaño de la vista si ha realizado alguna transfusión en ella.

Artículos para futuras investigaciones:

Documentos de Apple

Preguntas relacionadas sobre StackOverflow

Otros recursos

Practica tu mismo

Además de leer los artículos anteriores, me ayuda mucho hacer una aplicación de prueba. Es posible que desee intentar hacer algo similar. (Se me ocurrió la idea de este video curso pero desafortunadamente no es gratis).

ingrese la descripción de la imagen aquí

Aquí está el código para su referencia:

import UIKit

class ViewController: UIViewController {


    @IBOutlet weak var myView: UIView!

    // Labels
    @IBOutlet weak var frameX: UILabel!
    @IBOutlet weak var frameY: UILabel!
    @IBOutlet weak var frameWidth: UILabel!
    @IBOutlet weak var frameHeight: UILabel!
    @IBOutlet weak var boundsX: UILabel!
    @IBOutlet weak var boundsY: UILabel!
    @IBOutlet weak var boundsWidth: UILabel!
    @IBOutlet weak var boundsHeight: UILabel!
    @IBOutlet weak var centerX: UILabel!
    @IBOutlet weak var centerY: UILabel!
    @IBOutlet weak var rotation: UILabel!

    // Sliders
    @IBOutlet weak var frameXSlider: UISlider!
    @IBOutlet weak var frameYSlider: UISlider!
    @IBOutlet weak var frameWidthSlider: UISlider!
    @IBOutlet weak var frameHeightSlider: UISlider!
    @IBOutlet weak var boundsXSlider: UISlider!
    @IBOutlet weak var boundsYSlider: UISlider!
    @IBOutlet weak var boundsWidthSlider: UISlider!
    @IBOutlet weak var boundsHeightSlider: UISlider!
    @IBOutlet weak var centerXSlider: UISlider!
    @IBOutlet weak var centerYSlider: UISlider!
    @IBOutlet weak var rotationSlider: UISlider!

    // Slider actions
    @IBAction func frameXSliderChanged(sender: AnyObject) {
        myView.frame.origin.x = CGFloat(frameXSlider.value)
        updateLabels()
    }
    @IBAction func frameYSliderChanged(sender: AnyObject) {
        myView.frame.origin.y = CGFloat(frameYSlider.value)
        updateLabels()
    }
    @IBAction func frameWidthSliderChanged(sender: AnyObject) {
        myView.frame.size.width = CGFloat(frameWidthSlider.value)
        updateLabels()
    }
    @IBAction func frameHeightSliderChanged(sender: AnyObject) {
        myView.frame.size.height = CGFloat(frameHeightSlider.value)
        updateLabels()
    }
    @IBAction func boundsXSliderChanged(sender: AnyObject) {
        myView.bounds.origin.x = CGFloat(boundsXSlider.value)
        updateLabels()
    }
    @IBAction func boundsYSliderChanged(sender: AnyObject) {
        myView.bounds.origin.y = CGFloat(boundsYSlider.value)
        updateLabels()
    }
    @IBAction func boundsWidthSliderChanged(sender: AnyObject) {
        myView.bounds.size.width = CGFloat(boundsWidthSlider.value)
        updateLabels()
    }
    @IBAction func boundsHeightSliderChanged(sender: AnyObject) {
        myView.bounds.size.height = CGFloat(boundsHeightSlider.value)
        updateLabels()
    }
    @IBAction func centerXSliderChanged(sender: AnyObject) {
        myView.center.x = CGFloat(centerXSlider.value)
        updateLabels()
    }
    @IBAction func centerYSliderChanged(sender: AnyObject) {
        myView.center.y = CGFloat(centerYSlider.value)
        updateLabels()
    }
    @IBAction func rotationSliderChanged(sender: AnyObject) {
        let rotation = CGAffineTransform(rotationAngle: CGFloat(rotationSlider.value))
        myView.transform = rotation
        updateLabels()
    }

    private func updateLabels() {

        frameX.text = "frame x = \(Int(myView.frame.origin.x))"
        frameY.text = "frame y = \(Int(myView.frame.origin.y))"
        frameWidth.text = "frame width = \(Int(myView.frame.width))"
        frameHeight.text = "frame height = \(Int(myView.frame.height))"
        boundsX.text = "bounds x = \(Int(myView.bounds.origin.x))"
        boundsY.text = "bounds y = \(Int(myView.bounds.origin.y))"
        boundsWidth.text = "bounds width = \(Int(myView.bounds.width))"
        boundsHeight.text = "bounds height = \(Int(myView.bounds.height))"
        centerX.text = "center x = \(Int(myView.center.x))"
        centerY.text = "center y = \(Int(myView.center.y))"
        rotation.text = "rotation = \((rotationSlider.value))"

    }

}
Suragch
fuente
Parece que el ancho y la altura del límite son redundantes y una propiedad de "origen" hubiera sido suficiente (como lo fue con Flash).
Ian Warburton el
use límites para "como dibujar cosas u organizar subvistas dentro de la vista". ¿quiere decir que si tengo un viewController que tiene dos botones, debo colocar los botones usando los 'límites' de la vista del viewController? Siempre he usado el marco de la vista ...
Honey
@Honey, estoy un poco oxidado en mis habilidades con iOS ya que he estado trabajando en Android durante el último año. Creo que la vista raíz de un controlador de vista llena la pantalla, por lo que su marco y sus límites deberían ser los mismos.
Suragch
55
Creado github.com/maniramezan/FrameVsBounds.git Repo basa en la aplicación de ejemplo dado aquí para ayudar a entender mejor en boundscontra framede un punto de vista.
manman
1
@IanWarburton Cuando se gira la vista, el ancho y la altura del límite no coinciden con los del marco, por lo que no son redundantes.
Edward Brey hace
43

intenta ejecutar el siguiente código

- (void)viewDidLoad {
    [super viewDidLoad];
    UIWindow *w = [[UIApplication sharedApplication] keyWindow];
    UIView *v = [w.subviews objectAtIndex:0];

    NSLog(@"%@", NSStringFromCGRect(v.frame));
    NSLog(@"%@", NSStringFromCGRect(v.bounds));
}

La salida de este código es:

la orientación del dispositivo es vertical

{{0, 0}, {768, 1024}}
{{0, 0}, {768, 1024}}

la orientación del dispositivo es horizontal

{{0, 0}, {768, 1024}}
{{0, 0}, {1024, 768}}

obviamente, puedes ver la diferencia entre marco y límites

tristan
fuente
2
esto ya no es cierto (iOS 8)
Julian Król
13

El marco es el rectángulo que define la UIView con respecto a su supervista .

Los límites rect es el rango de valores que definen el sistema de coordenadas de NSView.

es decir, cualquier cosa en este rectángulo se mostrará en la vista UIV.

jorik
fuente
10

el marco es el origen (esquina superior izquierda) y el tamaño de la vista en el sistema de coordenadas de su super vista, esto significa que traduce la vista en su super vista cambiando el origen del marco, los límites , por otro lado, son el tamaño y el origen en su propio sistema de coordenadas, por lo que, de forma predeterminada, el origen de los límites es (0,0).

la mayoría de las veces el marco y los límites son congruentes, pero si tiene una vista del marco ((140,65), (200,250)) y los límites ((0,0), (200,250)) por ejemplo y la vista estaba inclinada para que quede en su esquina inferior derecha, entonces los límites seguirán siendo ((0,0), (200,250)), pero el marco no lo es.

ingrese la descripción de la imagen aquí

el marco será el rectángulo más pequeño que encapsula / rodea la vista, por lo que el marco (como en la foto) será ((140,65), (320,320)).

otra diferencia es, por ejemplo, si tiene un superView cuyos límites son ((0,0), (200,200)) y este superView tiene un subView cuyo marco es ((20,20), (100,100)) y cambió los límites de superView a ((20,20), (200,200)), entonces el marco subView seguirá siendo ((20,20), (100,100)) pero desplazado por (20,20) porque su sistema de coordenadas de supervista fue desplazado por (20, 20)

Espero que esto ayude a alguien.

m.eldehairy
fuente
con respecto al último párrafo: el subView marco es relativo a él superView. cambiar los superViewlímites a cualquier cosa no hará que el subVieworigen coincida con él superView.
staticVoidMan
5

Enmarque su relativo a su SuperView mientras que los límites relativos a su NSView.

Ejemplo: X = 40, Y = 60. También contiene 3 vistas. Este diagrama muestra una idea clara.

MARCO

LÍMITES

RAGHUNATH
fuente
4

Déjame agregar mis 5 centavos.

La vista principal de la vista usa el marco para colocarlo dentro de la vista principal.

La vista misma utiliza los límites para colocar su propio contenido (como lo hace una vista de desplazamiento mientras se desplaza). Ver también clipsToBounds . Los límites también se pueden usar para acercar / alejar el contenido de la vista.

Analogía:
Marco ~
Límites de pantalla de TV ~ Cámara (zoom, mover, rotar)

Serg
fuente
2

Las respuestas anteriores han explicado muy bien la diferencia entre límites y marcos.

Límites: una vista Tamaño y ubicación según su propio sistema de coordenadas.

Marco: un tamaño de vista y ubicación en relación con su SuperView.

Entonces existe la confusión de que en el caso de Bounds, X, Y siempre será "0". Esto no es verdad . Esto también se puede entender en UIScrollView y UICollectionView.

Cuando los límites 'x, y no son 0.
Supongamos que tenemos un UIScrollView. Hemos implementado la paginación. El UIScrollView tiene 3 páginas y el ancho de su ContentSize es tres veces el ancho de la pantalla (suponga que ScreenWidth es 320). La altura es constante (suponga 200).

scrollView.contentSize = CGSize(x:320*3, y : 200)

Agregue tres UIImageViews como subViews y observe de cerca el valor x del cuadro

let imageView0 = UIImageView.init(frame: CGRect(x:0, y: 0 , width : scrollView.frame.size.width, height : scrollView.frame.size.height))
let imageView1 :  UIImageView.init( frame: CGRect(x:320, y: 0 , width : scrollView.frame.size.width, height : scrollView.frame.size.height))
let imageView2 :  UIImageView.init(frame: CGRect(x:640, y: 0 , width : scrollView.frame.size.width, height : scrollView.frame.size.height))
scrollView.addSubview(imageView0)
scrollView.addSubview(imageView0)
scrollView.addSubview(imageView0)
  1. Página 0: cuando el ScrollView está en la página 0, los límites serán (x: 0, y: 0, ancho: 320, altura: 200)

  2. Página 1: Desplácese y muévase a la Página 1.
    Ahora los límites serán (x: 320, y: 0, ancho: 320, altura: 200) Recuerde que dijimos con respecto a su propio Sistema de coordenadas. Así que ahora la "Parte visible" de nuestro ScrollView tiene su "x" en 320. Mire el marco de imageView1.

  3. Página 2: Desplácese y muévase a Página 2 Límites: (x: 640, y: 0, ancho: 320, altura: 200) Nuevamente, observe el marco de imageView2

Lo mismo para el caso de UICollectionView. La forma más fácil de ver collectionView es desplazarlo e imprimir / registrar sus límites y obtendrá la idea.

Muhammad Nayab
fuente
2

Todas las respuestas anteriores son correctas y esta es mi opinión sobre esto:

Para diferenciar entre el marco y los límites, el desarrollador de CONCEPTOS debe leer:

  1. relativo a la supervista (una vista principal) está contenida dentro de = MARCO
  2. en relación con su propio sistema de coordenadas, determina su ubicación de subvista = BOUNDS

"límites" es confuso porque da la impresión de que las coordenadas son la posición de la vista para la que se establece. Pero estos están en relaciones y ajustados de acuerdo con las constantes de trama.

pantalla del iPhone

Centro de deportes acuáticos
fuente
1

Una ilustración más para mostrar la diferencia entre marco y límites. En este ejemplo:

  • View B es una subvista de View A
  • View B fue trasladado a x:60, y: 20
  • View B fue girado 45 degrees

ingrese la descripción de la imagen aquí

yoAlex5
fuente
0

Marco vs atado

  • Si crea una vista en X: 0, Y: 0, ancho: 400, altura: 400, su marco y límites son los mismos.
  • Si mueve esa vista a X: 400, su marco reflejará ese cambio pero sus límites no. Recuerde, los límites son relativos al espacio propio de la vista y, internamente, nada ha cambiado.
  • Si transforma la vista, por ejemplo, girándola o ampliándola, el marco cambiará para reflejar eso, pero los límites aún no lo harán; en lo que respecta a la vista internamente, no ha cambiado.
  • Si cambia los límites, cambiará el contenido dentro del marco porque el origen del rectángulo de límites comienza en una parte diferente de la vista.
Anit Kumar
fuente
-1

marco = ubicación y tamaño de una vista utilizando el sistema de coordenadas de la vista principal

límites = ubicación y tamaño de una vista usando su propio sistema de coordenadas

Una vista rastrea su tamaño y ubicación utilizando dos rectángulos: un rectángulo de marco y un rectángulo de límites. El rectángulo del marco define la ubicación y el tamaño de la vista en la supervista utilizando el sistema de coordenadas de la supervista. El rectángulo de límites define el sistema de coordenadas interiores que se utiliza al dibujar el contenido de la vista, incluido el origen y la escala. La Figura 2-1 muestra la relación entre el rectángulo del marco, a la izquierda, y el rectángulo de límites, a la derecha ".

En resumen, el marco es la idea de una vista de la supervista, y los límites son la propia idea de la vista. Tener múltiples sistemas de coordenadas, uno para cada vista, es parte de la jerarquía de vistas.

Xab Ion
fuente
¿Dónde está la figura 2-1?
Alexander Mayatsky