Question Cocoa: Quelle est la différence entre le cadre et les limites?


UIView et ses sous-classes ont toutes les propriétés frame et bounds. Quelle est la différence?


495
2017-07-31 00:07


origine


Réponses:


le bornes d'un UIView est le rectangle, exprimée par un emplacement (x, y) et une taille (largeur, hauteur) par rapport à son propre système de coordonnées (0,0).

le Cadre d'un UIView est le rectangle, exprimée sous la forme d'un emplacement (x, y) et d'une taille (largeur, hauteur) par rapport à la superview qu'elle contient.

Imaginez donc une vue de taille 100x100 (largeur x hauteur) positionnée à 25,25 (x, y) de sa superview. Le code suivant imprime les limites et le cadre de cette vue:

// 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);
}

Et la sortie de ce code est:

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

Donc, nous pouvons voir que dans les deux cas, la largeur et la hauteur de la vue sont les mêmes, que nous regardions les limites ou le cadre. Ce qui est différent est le positionnement x, y de la vue. Dans le cas des bornes, les coordonnées x et y sont à 0,0 car ces coordonnées sont relatives à la vue elle-même. Cependant, les coordonnées x et y du cadre sont relatives à la position de la vue dans la vue parent (ce que nous avons dit précédemment était à 25,25).

Il y a aussi belle présentation cela couvre UIViews. Voir les diapositives 1-20 qui expliquent non seulement la différence entre les images et les limites, mais montrent aussi des exemples visuels.


796
2017-07-31 00:43



Réponse courte

Cadre = l'emplacement et la taille d'une vue à l'aide du système de coordonnées de la vue parent

  • Important pour: placer la vue dans le parent

bornes = l'emplacement et la taille d'une vue en utilisant son propre système de coordonnées

  • Important pour: placer le contenu ou les sous-vues de la vue en elle-même

Réponse détaillée

Pour m'aider à me souvenir Cadre, Je pense à un cadre photo sur un mur. Le cadre photo est comme la bordure d'une vue. Je peux accrocher l'image partout où je veux sur le mur. De la même manière, je peux mettre une vue n'importe où je veux dans une vue parent (aussi appelée une vue d'ensemble). La vue des parents est comme le mur. L'origine du système de coordonnées dans iOS est le coin supérieur gauche. Nous pouvons placer notre vue à l'origine de la vue en réglant les coordonnées x-y du cadre de vue sur (0, 0), ce qui est comme si nous accrochions notre image dans le coin supérieur gauche du mur. Pour le déplacer vers la droite, augmentez x, pour le diminuer, augmentez y.

Pour m'aider à me souvenir bornes, Je pense à un terrain de basket où parfois le basketball est assommé. Vous dribble la balle partout sur le terrain de basketball, mais vous ne vous souciez pas vraiment de la cour elle-même. Ce pourrait être dans un gymnase, ou à l'extérieur dans une école secondaire, ou devant votre maison. Cela n'a pas d'importance. Vous voulez juste jouer au basket. De la même manière, le système de coordonnées pour les limites d'une vue se soucie uniquement de la vue elle-même. Il ne sait rien sur l'emplacement de la vue dans la vue parente. L'origine des bornes (le point (0, 0) par défaut) est le coin supérieur gauche de la vue. Toutes les sous-vues de cette vue sont présentées par rapport à ce point. C'est comme prendre le ballon de basket dans le coin avant gauche du terrain.

Maintenant, la confusion vient quand vous essayez de comparer le cadre et les limites. En réalité, ce n'est pas aussi grave qu'il n'y paraît au premier abord. Utilisons quelques images pour nous aider à comprendre.

Cadre vs limites

Dans la première image à gauche, nous avons une vue située en haut à gauche de la vue parente. Le rectangle jaune représente le cadre de la vue. À droite, nous voyons à nouveau la vue, mais cette fois, la vue parente n'est pas affichée. C'est parce que les limites ne connaissent pas la vue parent. Le rectangle vert représente les limites de la vue. le point rouge dans les deux images représente le origine du cadre ou des limites.

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

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

enter image description here

Le cadre et les limites étaient donc exactement les mêmes dans cette image. Regardons un exemple où ils sont différents.

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

Vous pouvez donc voir que la modification des coordonnées x-y du cadre le déplace dans la vue parente. Mais le contenu de la vue elle-même est toujours identique. Les bornes n'ont aucune idée que quelque chose est différent.

Jusqu'à présent, la largeur et la hauteur du cadre et des limites ont été exactement les mêmes. Ce n'est pas toujours vrai, cependant. Regardez ce qui se passe si nous faisons pivoter la vue de 20 degrés dans le sens des aiguilles d'une montre. (La rotation est effectuée à l'aide de transformations. Documentation et ceux-ci vue et exemples de couches pour plus d'informations.)

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

Vous pouvez voir que les limites sont toujours les mêmes. Ils ne savent toujours pas que quelque chose s'est passé! Les valeurs de cadre ont toutes changé, cependant.

Maintenant, il est un peu plus facile de voir la différence entre le cadre et les bornes, n'est-ce pas? L'article Vous ne comprenez probablement pas les cadres et les limites définit un cadre de vue comme

... la plus petite boîte de délimitation de cette vue par rapport à ses parents   système de coordonnées, y compris les transformations appliquées à cette vue.

Il est important de noter que si vous transformez une vue, le cadre devient indéfini. Donc, en fait, le cadre jaune que j'ai dessiné autour des limites vertes tournées dans l'image ci-dessus n'existe jamais réellement. Cela signifie que si vous faites une rotation, une mise à l'échelle ou une autre transformation, vous ne devez plus utiliser les valeurs d'image. Vous pouvez toujours utiliser les valeurs des limites, cependant. Les docs d'Apple préviennent:

Important: Si une vue est transform propriété ne contient pas la transformation d'identité, le cadre de cette vue est indéfini et sont donc la   résultats de ses comportements autoresizing.

Plutôt malheureux à propos de la réorganisation automatique… Il y a quelque chose que vous pouvez faire, cependant.

Les documents Apple indiquent:

En modifiant le transform propriété de votre vue, tout   les transformations sont effectuées par rapport au point central de la   vue.

Donc, si vous avez besoin de déplacer une vue dans le parent après une transformation, vous pouvez le faire en changeant le view.center coordonnées. Comme frame, center utilise le système de coordonnées de la vue parent.

Ok, débarrassons-nous de notre rotation et concentrons-nous sur les limites. Jusqu'à présent, l'origine des limites est toujours restée à (0, 0). Cela ne doit pas, cependant. Que faire si notre vue a une grande sous-vue trop grande pour être affichée en une seule fois? Nous allons en faire un UIImageViewavec une grande image. Voici encore notre deuxième image ci-dessus, mais cette fois nous pouvons voir à quoi ressemblerait le contenu entier de la sous-vue de notre vue.

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

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

enter image description here

Seul le coin supérieur gauche de l'image peut tenir dans les limites de la vue. Regardez maintenant ce qui se passe si nous changeons les coordonnées d'origine des limites.

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

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

enter image description here

Le cadre n'a pas été déplacé dans la vue d'ensemble, mais le contenu à l'intérieur du cadre a été modifié car l'origine du rectangle des limites commence dans une autre partie de la vue. C'est l'idée derrière un UIScrollView et ses sous-classes (par exemple, un UITableView). Voir Comprendre UIScrollView pour plus d'explications.

Quand utiliser le cadre et quand utiliser les limites

Depuis frame relie l'emplacement d'une vue dans sa vue parent, vous l'utilisez lorsque vous faites changements extérieurs, comme changer sa largeur ou trouver la distance entre la vue et le haut de sa vue parent.

Utilisez le bounds quand vous faites changements internes, comme dessiner des choses ou organiser des sous-vues dans la vue. Utilisez également les limites pour obtenir la taille de la vue si vous avez effectué une transfomation dessus.

Articles pour plus de recherche:

Apple docs

Questions liées à StackOverflow

Autres ressources

Pratique toi

En plus de lire les articles ci-dessus, cela m'aide beaucoup à créer une application de test. Vous pourriez vouloir essayer de faire quelque chose de similaire. (J'ai l'idée de ce cours vidéo mais malheureusement ce n'est pas gratuit.)

enter image description here

Voici le code pour votre référence:

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))"

    }

}

488
2018-03-07 17:18



essayez d'exécuter le code ci-dessous

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

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

la sortie de ce code est:

l'orientation du boîtier est Portrait

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

l'orientation du périphérique de cas est Paysage

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

de toute évidence, vous pouvez voir la différence entre le cadre et les limites


43
2018-01-04 01:44



le Cadre est le rectangle qui définit UIView avec par rapport à son aperçu.

le limites rect est la gamme de valeurs qui définissent cette Le système de coordonnées de NSView. 

c'est-à-dire que tout ce qui est dans ce rectangle s'affichera réellement dans l'UIView.


13
2018-05-23 12:19



Cadre est l'origine (coin supérieur gauche) et la taille de la vue dans le système de coordonnées de sa super-vue, cela signifie que vous traduisez la vue dans sa super-vue en modifiant l'origine de l'image, bornes d'autre part est la taille et l'origine dans son propre système de coordonnées, donc par défaut l'origine des bornes est (0,0).

La plupart du temps, le cadre et les limites sont congruents, mais si vous avez une vue du cadre ((140,65), (200,250)) et des limites ((0,0), (200,250)) par exemple et la vue a été inclinée pour qu'il se trouve dans son coin inférieur droit, les limites seront toujours ((0,0), (200,250)), mais le cadre ne l'est pas.

enter image description here

le cadre sera le plus petit rectangle qui encapsule / entoure la vue, de sorte que le cadre (comme sur la photo) sera ((140,65), (320,320)).

une autre différence est par exemple si vous avez un superView dont les limites sont ((0,0), (200,200)) et ce superView a une sous-vue dont la trame est ((20,20), (100,100)) et vous avez modifié les limites superView à ((20,20), (200,200)), alors la trame de sous-image sera encore ((20,20), (100,100)) mais décalée de (20,20) parce que son système de coordonnées de présentation a été décalé de (20, 20).

J'espère que cela aide quelqu'un.


9
2018-04-08 14:20



Cadre son relatif à son SuperView alors que Bounds par rapport à son NSView.

Exemple: X = 40, Y = 60.Aussi contient 3 vues. Ce diagramme vous montre une idée claire.

FRAME

BOUNDS


2
2017-12-17 07:31



Les réponses ci-dessus ont très bien expliqué la différence entre les limites et les cadres.

Limites: A vues Taille et emplacement selon son propre système de coordonnées.

Frame: taille et emplacement communs par rapport à son SuperView.

Alors il y a confusion que dans le cas de Bounds le X, Y sera toujours "0". Ce n'est pas vrai. Cela peut également être compris dans UIScrollView et UICollectionView.

Lorsque les bornes 'x, y ne sont pas 0.
Supposons que nous ayons un UIScrollView. Nous avons mis en œuvre la pagination. UIScrollView a 3 pages et la largeur de son ContentSize est trois fois la largeur de l'écran (supposons que ScreenWidth soit 320). La hauteur est constante (supposons 200).

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

Ajoutez trois UIImageViews en tant que sous-vues et observez attentivement les X valeur du cadre

        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. Page 0: Lorsque le ScrollView est à 0 Page, les limites seront (x: 0, y: 0, largeur: 320, hauteur: 200)

  2. Page 1 : Faites défiler et passez à la page 1.
    Maintenant, les limites seront (x: 320, y: 0, largeur: 320, hauteur: 200) Rappelez-vous que nous avons dit en ce qui concerne son propre système de coordonnées. Alors maintenant, la "partie visible" de notre ScrollView a son "x" en 320. Regardez le cadre de imageView1.

  3. Page 2 : Faites défiler et passez à la page 2 Limites: (x: 640, y: 0, largeur: 320, hauteur: 200) Encore une fois jeter un oeil à l'image de imageView2

C'est le cas de UICollectionView. La façon la plus simple de regarder collectionView est de le faire défiler et d'imprimer / consigner ses limites et vous aurez l'idée.


0
2018-06-07 18:16