web-dev-qa-db-fra.com

Utilisation appropriée de intrinsicContentSize et sizeThatFits: sur la sous-classe UIView avec autolayout

Je pose cette question (en quelque sorte) simple, juste pour être précis, parce que parfois je crains un mauvais usage que je ferais de nombreuses API de UIView, en particulier en ce qui concerne l'autolayout.

Pour simplifier les choses, prenons un exemple. Supposons que j’ai besoin d’une sous-classe UIView dotée d’une icône d’image et d’une étiquette multiligne. Le comportement que je veux, c'est que la hauteur de ma vue change avec la hauteur de l'étiquette (pour correspondre au texte à l'intérieur). De plus, je la pose avec le générateur Interface, de sorte que j'ai quelque chose comme:

simple view image

avec certaines contraintes qui donnent une largeur et une hauteur fixes à la vue image et une largeur et une position fixes (par rapport à la vue image) à l'étiquette:

simple view image, constraints shown

Maintenant, si je mets du texte sur l’étiquette, je veux que la vue soit redimensionnée en hauteur pour s’ajuster correctement, ou reste à la même hauteur qu’elle a dans le xib. Avant l'autolayout, j'aurais toujours fait quelque chose comme ça:

Dans le fichier de sous-classe CustoView, j'aurais remplacé sizeThatFits: ainsi:

- (CGSize) sizeThatFits:(CGSize)size{

    //this stands for whichever method I would have used
    //to calculate the height needed to display the text based on the font
    CGSize labelSize = [self.titleLabel intrinsicContentSize];

    //check if we're bigger than what's in ib, otherwise resize
    CGFloat newHeight = (labelSize.height <= 21) ? 51: labelSize.height+20;

    size.height = newHeight;

    return size;

}

Et que j'aurais appelé quelque chose comme:

myView.titleLabel.text = @"a big text to display that should be more than a line";
[myView sizeToFit];

Maintenant, en ce qui concerne les contraintes, je sais que les systèmes autolayout appellent intrinsicContentSize sur les éléments de l’arborescence de la vue pour connaître leur taille et effectuer leurs calculs. Par conséquent, je dois remplacer intrinsicContentSize dans ma sous-vue exactement les mêmes choses qu'il retourne dans le sizeThatFits: La méthode précédemment montrée, à l'exception du fait que précédemment, lorsque j'appelais sizeToFit, ma vue était correctement redimensionnée, mais maintenant, avec autolayout, en combinaison avec un xib, cela ne se produira pas.

Bien sûr, j’appellerai peut-être sizeToFit chaque fois que je modifierai du texte dans ma sous-classe, avec un intrinsicContentSize remplacé qui renvoie exactement la même taille que sizeThatFits:, mais je ne pense pas que ce soit la bonne façon de procéder.

Je pensais à redéfinir needsUpdateConstraints et updateConstraints, mais cela n’a toujours pas beaucoup de sens car la largeur et la hauteur de ma vue sont déduites et traduites à partir du masque autoresizing du xib.

Alors, quelle est, selon vous, la manière la plus propre et la plus correcte de faire exactement ce que je montre ici et de soutenir la diffusion automatique?

60
dev_mush

Je ne pense pas que vous ayez besoin de définir un contenu intrinsèque.

Voici deux raisons de penser que:

  1. Lorsque la documentation sur la mise en page automatique aborde intrinsicContentSize, elle se réfère aux "vues en feuille" telles que les boutons ou les libellés où une taille peut être calculée uniquement en fonction de leur contenu. L'idée est que ce sont les feuilles dans l'arborescence de la hiérarchie, pas des branches, car elles ne sont pas composées d'autres vues.

  2. IntrinsicContentSize n'est pas vraiment un concept "fondamental" dans Auto Layout. Les concepts fondamentaux ne sont que des contraintes et les attributs liés par des contraintes. La taille intrinsèque du contenu, les priorités de contenu et les priorités de résistance à la compression ne sont en réalité que des commodités à utiliser pour générer des contraintes internes concernant la taille. La taille finale est simplement le résultat de ces contraintes qui interagissent avec toutes les autres contraintes de la manière habituelle.

Et alors? Ainsi, si votre "vue personnalisée" n'est en réalité qu'un assemblage de quelques autres vues, vous n'avez pas besoin de définir un contenu intrinsèque. Vous pouvez simplement définir les contraintes qui créent la mise en page souhaitée et ces contraintes produiront également la taille souhaitée.

Dans le cas particulier que vous décrivez, je définirais une contrainte d'espace minimum> = 0 de l'étiquette à la vue d'ensemble, une autre de l'image à la vue d'ensemble, puis une contrainte priorité faible hauteur zéro pour la vue dans son ensemble. La contrainte de priorité faible essaiera de réduire la taille de l’Assemblée, tandis que les autres contraintes l’empêcheront de réduire autant que possible le découpage de ses sous-vues.

Si vous ne définissez jamais explicitement intrinsicContentSize, comment voyez-vous la taille résultant de ces contraintes? Une solution consiste à forcer la mise en page, puis à observer les résultats.

Une autre méthode consiste à utiliser systemLayoutSizeFittingSize: (Et dans iOS8, le peu annoncé systemLayoutSizeFittingSize:withHorizontalFittingPriority:verticalFittingPriority:). C'est un cousin plus proche de sizeThatFits: Que intrinsicContentSize. C'est ce que le système utilisera pour calculer la taille appropriée de votre vue, en prenant en compte toutes les contraintes qu'il contient, y compris les contraintes de taille de contenu intrinsèque ainsi que toutes les autres.

Malheureusement, si vous avez une étiquette multiligne, vous aurez probablement aussi besoin de configurer preferredMaxLayoutWidth pour obtenir un bon résultat, mais c'est une autre histoire ...

71
algal