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

Resuelto mk12 asked hace 15 años • 14 respuestas

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

mk12 avatar Jul 31 '09 07:07 mk12
Aceptado

Los límites de una UIView son el rectángulo , expresado como una ubicación (x,y) y un tamaño (ancho,alto) en relación con 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) en relación con la supervista que contiene.

Entonces, imagine una vista que tiene un tamaño de 100x100 (ancho x alto) ubicada 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

Entonces, 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 misma. Sin embargo, las coordenadas xey del marco 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 a 20, que no solo explican la diferencia entre marcos y límites, sino que también muestran ejemplos visuales.

shek avatar Jul 31 '2009 00:07 shek

Respuesta corta

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

  • Importante para: colocar la vista en el padre

límites = la ubicación y el 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í misma

Respuesta detallada

Para ayudarme a recordar el marco , pienso en el marco de un cuadro en la pared . El marco del cuadro es como el borde de una vista. Puedo colgar el cuadro en cualquier lugar que quiera en la pared. De la misma manera, puedo colocar una vista en cualquier lugar que quiera dentro de una vista principal (también llamada supervista). La vista principal es como la pared. El origen del sistema de coordenadas en iOS está arriba a la izquierda. Podemos poner nuestra vista en el origen de la supervista estableciendo 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 la pelota sale fuera de los límites . Estás driblando la pelota por toda la cancha de baloncesto, pero realmente no te importa dónde está la cancha. Podría ser en un gimnasio, afuera en una escuela secundaria o frente a tu casa. No importa. Sólo quieres jugar baloncesto. De la misma manera, el sistema de coordenadas de los límites de una vista sólo 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 establece en relación con este punto. Es como llevar la pelota de baloncesto a la esquina delantera izquierda de la cancha.

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

Marco versus límites

En la primera imagen de 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 volvemos a ver la vista, pero esta vez no se muestra la vista principal. 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 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 cambiar las coordenadas xy del marco lo mueve en la vista principal. Pero el contenido de la vista en sí sigue siendo exactamente el mismo. Los límites no tienen idea de que algo es diferente.

Hasta ahora, el ancho y alto tanto del marco como de los límites han sido exactamente los mismos. Sin embargo, eso no siempre es cierto. Mira lo que pasa si giramos la vista 20 grados en el sentido de las agujas del reloj. (La rotación se realiza mediante transformaciones. Consulte la documentación y estos ejemplos de vistas y capas 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 pasado algo! Sin embargo, todos los valores del marco han cambiado.

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

...el cuadro delimitador más pequeño de esa vista con respecto a su sistema de coordenadas principal, incluida cualquier transformación aplicada a esa vista.

Es importante tener en cuenta que si transforma una vista, el marco deja de estar definido. 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 rotas, escalas o haces alguna otra transformación, entonces no deberías usar más los valores del marco. Sin embargo, aún puedes usar los valores de los límites. Los documentos de Apple advierten:

Importante: si la propiedad de una vista transformno contiene la transformación de identidad, el marco de esa vista no está definido y tampoco lo están los resultados de sus comportamientos de tamaño automático.

Es bastante desafortunado lo del tamaño automático... Sin embargo, hay algo que puedes hacer.

Los documentos de Apple afirman:

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 realizar una transformación, puede hacerlo cambiando las view.centercoordenadas. Me gusta frame, centerutiliza el sistema de coordenadas de la vista principal.

Bien, deshagámonos de 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). Aunque no es necesario. ¿Qué pasa si nuestra vista tiene una subvista grande que es demasiado grande para mostrarla toda a la vez? Lo haremos UIImageViewcon una imagen grande. Aquí está nuevamente nuestra segunda imagen desde arriba, 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í

Sólo 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 la idea detrás de a UIScrollViewy sus subclases (por ejemplo, a UITableView). Consulte Comprensión de UIScrollView para obtener más explicaciones.

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

Dado que 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.

Úselo boundscuando esté realizando cambios internos , como dibujar cosas u organizar subvistas dentro de la vista. Utilice también los límites para obtener el tamaño de la vista si ha realizado alguna transformación en ella.

Artículos para futuras investigaciones:

documentos de Apple

  • Ver geometría
  • Puntos de vista
  • Arquitectura de vistas y ventanas

Preguntas relacionadas con StackOverflow

  • Marco, límites y centro de UIView
  • Marco, límites, centro, origen de UIView, ¿cuándo usar qué?
  • Tamaño de marco/ventana "incorrecto" después de la reorientación en iPhone

Otros recursos

  • Probablemente no entiendas los marcos y los límites
  • Fundamentos de iOS: marcos, límites y CGGeometry
  • CS193p Conferencia 5: Vistas, dibujo, animación

Practica tú mismo

In addition to reading the above articles, it helps me a lot to make a test app. You might want to try to do something similar. (I got the idea from this video course but unfortunately it isn't free.)

ingrese la descripción de la imagen aquí

Here is the code for your reference:

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 avatar Mar 07 '2015 17:03 Suragch