Cadre, limites et centre UIView

320

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

Si je comprends bien, framepeut ê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.

centerPeut également ê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, boundsest relatif à la vue elle-même. Il modifie la zone de dessin pour la vue.

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

Lorenzo B
la source
1
Cette discussion a déjà été résolue ici. donc les pls le regardent ici ou pourraient voir la documentation pour les développeurs donnée ici developer.apple.com/library/ios/#documentation/uikit/reference/… stackoverflow.com/questions/1210047/… slideshare.net/onoaonoa/cs193p-lecture-5 -view-animation
Splendide

Réponses:

577

Étant donné que la question que j'ai posée a été vue à plusieurs reprises, j'en fournirai une réponse détaillée. N'hésitez pas à le modifier si vous souhaitez ajouter un contenu plus correct.

Tout d'abord un récapitulatif de la question: cadre, limites et centre et leurs relations.

Le cadre La vue frame( CGRect) est la position de son rectangle dans le superviewsystème de coordonnées de. Par défaut, il commence en haut à gauche.

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

Le centre A centerest CGPointexprimé en termes de superviewsystème de coordonnées et détermine la position du point central exact de la vue.

Tirées de la position UIView +, voici les relations (elles ne fonctionnent pas dans le code car ce sont des équations informelles) entre les 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 œil à l'image suivante tirée du tiroir de cuisine basé sur le cours Stanford CS193p . Les crédits vont à @Rhubarb .

Cadre, limites et centre

L'utilisation de framevous 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 viewfichier auquel vous vous référez habituellement bounds. Un exemple typique pourrait être de dessiner dans une viewsous-vue en tant qu'encart de la première. Pour dessiner la sous-vue, vous devez connaître la vue boundsd'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 boundsvue. Par exemple, si vous modifiez le bounds size, les framemodifications (et vice versa). Le changement se produit autour centerde 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, bounds originvous modifiez le originsystème de coordonnées interne. Par défaut, le originest à (0.0, 0.0)(coin supérieur gauche). Par exemple, si vous modifiez le originpourview1 vous pouvez voir (commentez le code précédent si vous le souhaitez) que maintenant le coin supérieur gauche pour view2touche view1celui. La motivation est assez simple. Vous dites view1que son coin supérieur gauche est maintenant à la position , (20.0, 20.0)mais depuis view2de » frame originles départs à partir (20.0, 20.0), ils coïncident.

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

Le originreprésente la viewposition de l 'au sein de son superviewmais décrit la position dubounds centre.

Enfin, boundset originne sont pas des concepts liés. Les deux permettent de dériver la framevue (voir les équations précédentes).

Étude de cas de View1

Voici ce qui se passe lors de l'utilisation de l'extrait de code 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.

entrez la description de l'image ici

C'est ce qui se passe à la place si je change les [self view]limites 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.

entrez la description de l'image ici

Ici tu dis à [self view] que son coin supérieur gauche est maintenant à la position (30.0, 20.0) mais puisque view1l'origine du cadre commence à (30.0, 20.0), ils coïncideront.

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

À propos clipsToBounds (source doc Apple)

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

En d'autres termes, si une vue frameest (0, 0, 100, 100)et sa sous-vue (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.

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

Lorenzo Boaro
la source
@Rhubarb Vous avez parfaitement raison. Je le soulignerai dans ma réponse. Merci.
Lorenzo B
Je ne sais pas si j'obtiendrai une réponse, mais pourquoi la sous-vue se déplace-t-elle dans le sens négatif lorsque les origines des limites sont modifiées. Je n'arrive pas à comprendre ce bit. Merci!!
nimgrg
@nimgrg Il semble que la vue se déplace dans le sens négatif mais ce n'est pas le cas. Les coordonnées internes de la vue d'ensemble sont modifiées.
Lorenzo B
pardonnez mes capacités géométriques, si les coordonnées internes de la superview sont changées en origine (30.0, 20.0), le point (0,0) se trouve-t-il à l'intérieur du carré bleu ou à l'extérieur de celui-ci. J'essaie simplement de suivre les changements et de leur donner un sens. Merci d'avoir pris le temps de répondre.
nimgrg
@flexaddicted: Bonjour, la diapositive que vous avez ajoutée dit: "Le milieu de la vue B dans son propre espace de coordonnées est: bounds.size.width / 2 + origin.x" - pourquoi est-ce le cas? Puisque le titre dit dans son propre espace de coordonnées, je dirais que c'est: bounds.size.width / 2 + bounds.origin.x ?? N'est-ce pas ??
134

Cette question a déjà une bonne réponse, mais je veux la compléter avec plus de photos. 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. (mur = superview, cadre = vue)

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

Comme le cadre, view.center est é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

entrez la description de l'image ici


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

entrez la description de l'image ici


Exemple 3

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

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

entrez la description de l'image ici


Exemple 4

C'est la même chose que dans l'exemple 2, sauf que cette fois-ci, tout le contenu de la vue est affiché tel qu'il ressemblerait s'il n'était pas coupé aux limites de la vue.

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

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

entrez la description de l'image ici


Exemple 5

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

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

entrez la description de l'image ici

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

Suragch
la source
2
Merci Suragch. C'est vraiment une image très claire du cadre et de la différence liée. J'apprécie vraiment votre effort.
Mohd Haider
Tout ce que je veux voir dans une réponse courte et concise. Merci!
Matt Chuang
Impressionnant! Pour l'exemple 3: je ne comprends pas. vous avez seulement déplacé un peu l'origine de votre cadre, comment cela change-t-il la rotation de votre image?
Honey
@ asma22, Dans l'exemple 3, j'essayais juste de montrer que lorsque vous faites pivoter une image, le cadre change mais pas les limites. Voir ma réponse plus complète .
Suragch
89

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

entrez la description de l'image ici

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

Erben Mo
la source
14
Une image qui vaut mille mots.
Narendra Kamma
3
La meilleure explication possible
Ratikanta Patra
@Erben Mo: Bonjour, la diapositive que vous avez ajoutée dit: "Le milieu de la vue B dans son propre espace de coordonnées est: bounds.size.width / 2 + origin.x" - pourquoi est-ce le cas? Puisque le titre dit dans son propre espace de coordonnées, je dirais que c'est: bounds.size.width / 2 + bounds.origin.x ?? N'est-ce pas ??
1
Quelle est la source de cette image? Lien?
David
1
@David C'est du cours CS193p de Stanford sur iTunesU trouvé ici: www.stanford.edu/class/cs193p/cgi-bin/drupal/downloads-2013-fall
preynolds
3

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

Le cadre 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 (UIView au centre) et de la transformation.

Donc, fondamentalement, la façon dont les dispositions de couche / vue sont réellement décidées par ces trois propriétés (et anchorPoint), et aucune de ces trois propriétés ne changera aucune autre propriété, comme changer la transformation ne change pas les limites.

coolbeet
la source
2

Il y a de très bonnes réponses avec une explication détaillée de ce post. Je voudrais simplement mentionner qu'il existe une autre explication avec une représentation visuelle de la signification de Frame, Bounds, Center, Transform, Bounds Origin dans la vidéo de la WWDC 2011 Comprendre le rendu UIKit à partir de @ 4: 22 jusqu'à 20:10

Wael Showair
la source
0

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

Supposons que vous naviguiez en ligne, votre navigateur Web est votre framequi décide où et quelle taille afficher la page Web. Le défilement du navigateur est votre bounds.originqui décide quelle partie de la page Web sera affichée. bounds.originest difficile à comprendre. La meilleure façon d'apprendre consiste à créer une application à vue unique, à essayer de modifier ces paramètres et à 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));
NSTNF
la source