web-dev-qa-db-fra.com

Cadre UIView, limites et centre

Je voudrais savoir comment utiliser ces propriétés de la bonne manière.

Si je comprends bien, frame peut être utilisé à partir du conteneur de la vue que je crée. Il définit la position de la vue par rapport à la vue du conteneur. Il définit également la taille de cette vue.

De plus, center peut être utilisé à partir du conteneur de la vue que je crée. Cette propriété modifie la position de la vue par rapport à son conteneur.

Enfin, bounds est relatif à la vue elle-même. Cela change la zone de dessin pour la vue.

Pouvez-vous donner plus d’informations sur la relation entre frame et bounds? Qu'en est-il des propriétés clipsToBounds et masksToBounds?

315
Lorenzo B

Comme la question que j’ai posée a été vue à maintes reprises, je vais en donner une réponse détaillée. N'hésitez pas à le modifier si vous souhaitez ajouter plus de contenu correct.

Tout d’abord, récapitulons la question: cadre, bornes et centre et leurs relations.

Cadre La vue frame (CGRect) d'une vue est la position de son rectangle dans le système de coordonnées superview. Par défaut, cela commence en haut à gauche.

Limites La vue bounds (CGRect) d'une vue exprime un rectangle de vue dans son propre système de coordonnées.

Centre Une center est une CGPoint exprimée en fonction du système de coordonnées superview 'et détermine la position de le point central exact de la vue.

Tiré de IView + position ce sont les relations (elles ne fonctionnent pas dans le code car ce sont des équations informelles) entre propriétés précédentes:

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

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

  • frame.size = bounds.size

REMARQUE: Ces relations ne s'appliquent pas si les vues sont pivotées. Pour plus d’informations, je vous suggère de jeter un oeil à l’image suivante tirée de Le tiroir de la cuisine sur la base de Stanford CS193p bien sûr. Les crédits vont à @ Rhubarb .

Frame, bounds and center

L'utilisation de frame vous permet de repositionner et/ou de redimensionner une vue dans sa superview. Peut généralement être utilisé à partir de superview, par exemple, lorsque vous créez une sous-vue spécifique. Par exemple:

// 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];

Lorsque vous avez besoin des coordonnées pour dessiner à l'intérieur d'un view, vous vous référez généralement à bounds. Un exemple typique pourrait être de dessiner dans une view une sous-vue en tant qu'encart de la première. Dessiner la sous-vue nécessite de connaître la bounds de la vue d'ensemble. Par exemple:

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];

Différents comportements se produisent lorsque vous modifiez la bounds d'une vue. Par exemple, si vous modifiez la boundssize, la frame change (et inversement). Le changement se produit autour de la center de la vue. Utilisez le code ci-dessous et voyez ce qui se passe:

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

De plus, si vous changez boundsOrigin, vous changez le Origin de son système de coordonnées interne. Par défaut, la Origin est à (0.0, 0.0) (coin supérieur gauche). Par exemple, si vous modifiez la Origin pour view1, vous pouvez voir (commentez le code précédent si vous le souhaitez) que le coin supérieur gauche de view2 touche le view1 . La motivation est assez simple. Vous dites à view1 que son coin supérieur gauche est maintenant à la position (20.0, 20.0) mais depuis view2 'frameOrigin commence à (20.0, 20.0), ils vont coïncider.

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

Le Origin représente la position du view dans son superview mais décrit la position du centre bounds.

Enfin, bounds et Origin ne sont pas des concepts liés. Les deux permettent de dériver la frame d'une vue (Voir les équations précédentes).

Étude de cas de View1

Voici ce qui se passe lorsqu’on utilise l’extrait suivant.

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]));

L'image relative.

enter image description here

C'est ce qui se passe si je change les limites de [self view] comme suit.

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

L'image relative.

enter image description here

Ici, vous dites à [self view] que son coin supérieur gauche se trouve maintenant à la position (30.0, 20.0) mais que le cadre de view1 Origin commence à partir de (30.0, 20.0), ils coïncident.

Références supplémentaires (à mettre à jour avec d'autres références si vous le souhaitez)

À propos de clipsToBounds (source Apple doc)

Si vous définissez cette valeur sur YES, les sous-vues sont tronquées aux limites du récepteur. Si cette option est définie sur NO, les sous-vues dont les cadres s'étendent au-delà des limites visibles du récepteur ne sont pas tronquées. La valeur par défaut est NO.

En d'autres termes, si la frame d'une vue est (0, 0, 100, 100) et que sa sous-vue est (90, 90, 30, 30), vous ne verrez qu'une partie de cette sous-vue. Ce dernier ne dépassera pas les limites de la vue parent.

masksToBounds est équivalent à clipsToBounds. A la place d'un UIView, cette propriété est appliquée à un CALayer. Sous le capot, clipsToBounds appelle masksToBounds. Pour plus de références, jetez un œil à Quelle est la relation entre les clipsToBounds de UIView et les masquesToBounds de CALayer? .

569
Lorenzo B

Cette question a déjà une bonne réponse, mais je veux la compléter avec quelques images supplémentaires. Ma réponse complète est ici.

Pour m'aider à me souvenir du cadre , je pense à un cadre photo sur un mur Tout comme une image peut être déplacée n'importe où sur le mur, le système de coordonnées du cadre d'une vue est la vue d'ensemble. (wall = superview, frame = view)

Pour m'aider à me souvenir des limites , je pense à les limites d'un terrain de basket Le basketball se trouve quelque part dans le court, tout comme le système de coordonnées des limites de la vue est dans la vue elle-même. (court = vue, basket-ball/joueurs = contenu dans la vue)

Comme le cadre, view.center se trouve également dans les coordonnées de la vue d'ensemble.

Frame vs Bounds - Exemple 1

Le rectangle jaune représente le cadre de la vue. Le rectangle vert représente les limites de la vue. Le point rouge dans les deux images représente l'origine du cadre ou des limites dans leurs systèmes de coordonnées.

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

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

enter image description here


Exemple 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


Exemple 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


Exemple 4

Ceci est identique à l'exemple 2, sauf que cette fois, tout le contenu de la vue est affiché, car il ressemblerait s'il n'était pas tronqué dans les limites de la vue.

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

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

enter image description here


Exemple 5

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

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

enter image description here

Encore une fois, voir ici pour ma réponse avec plus de détails.

131
Suragch

J'ai trouvé cette image très utile pour comprendre le cadre, les limites, etc.

enter image description here

Veuillez également noter que frame.size != bounds.size lorsque l'image est pivotée.

88
Erben Mo

Je pense que si vous le pensez du point de vue de CALayer, tout est plus clair.

Frame n'est pas vraiment une propriété distincte de la vue ou du calque, c'est une propriété virtuelle, calculée à partir des limites, de la position (centre de UIView) et de la transformation.

Donc, fondamentalement, la manière dont la disposition des couches/vues est vraiment déterminée par ces trois propriétés (et anchorPoint), et l'une ou l'autre de ces trois propriétés ne changera aucune autre propriété, comme changer de transformation ne modifie pas les limites.

3
coolbeet

Il y a de très bonnes réponses avec des explications détaillées à ce post. Je voudrais simplement mentionner qu’il existe une autre explication avec représentation visuelle de la signification des images Cadre, Limites, Centre, Transformation, Origines dans WWDC 2011 Présentation du rendu UIKit = à partir de 4h22 jusqu'à 20h10

1
Wael Showair

Après avoir lu les réponses ci-dessus, ajoutez ici mes interprétations.

Supposons que la navigation en ligne sur le navigateur Web soit votre frame qui décide où et quelle taille afficher. Le navigateur du navigateur est votre bounds.Origin qui décide quelle partie de la page Web sera affichée. bounds.Origin est difficile à comprendre. La meilleure façon d'apprendre consiste à créer une application à vue unique, d'essayer de modifier ces paramètres et de voir comment les sous-vues changent.

- (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));
0
NSTNF