11
votes

Cellules auto-dimensionnelles et contrôles de taille dynamique pour iOS

Définition problématique

J'essaie de construire un contrôle personnalisé qui se comportera de la même manière à Uilabel . Je devrais pouvoir placer un tel contrôle à l'intérieur d'une cellule de table auto-dimensionnée et il devrait:

  • enveloppez le contenu (comme Uilabel avec NumberOflines = 0 fait)
  • étend automatiquement la taille de la hauteur de la cellule auto-taille
  • gérer une rotation d'appareil
  • ne nécessite aucun code spécial dans UitablecellView ou ViewControll pour implémenter cette fonctionnalité (Uilabel n'exige aucun code spécial pour cela).

    recherche

    La première chose que j'ai faite est très simple. J'ai décidé d'observer comment fonctionne Uilabel. J'ai suivi:

    • a créé une table avec des cellules auto-dimensionnelles
    • a créé une cellule personnalisée, mettez Uilabel (avec Numberflines = 0) dedans
    • Création de contraintes pour vous assurer que Uilabel occupe une cellule entière
    • sous-classes Uilabel et survole un tas de méthodes pour voir comment il se comporte

      J'ai vérifié les choses suivantes

      • exécutez-le dans un portrait (l'étiquette est affichée correctement sur plusieurs lignes) et la hauteur de la cellule est correcte
      • Tournez-le. La largeur de table et la hauteur ont été mises à jour et elles sont correctes aussi.

        J'ai observé que cela se comporte bien. Il ne nécessite aucun code spécial et j'ai vu l'ordre de (certains) appelle quel système fait pour le rendre.

        une solution partielle

        @wingzero a écrit une solution partielle ci-dessous. Cela crée des cellules d'une taille correcte.

        Cependant, sa solution a deux problèmes:

        • Il utilise "self.superview.bounds.Size.width". Cela pourrait être utilisé si votre contrôle occupe une cellule entière. Cependant, si vous avez autre chose dans la cellule qui utilise des contraintes, un tel code ne calculera pas correctement une largeur.

        • Il ne gère pas du tout la rotation. Je suis à peu près sûr que cela ne gère pas d'autres événements de redimensionnement (il y a un tas d'événements de redimensionnement moins courants - comme une barre d'état plus grosse sur un appel téléphonique, etc.).

          Savez-vous comment résoudre ces problèmes pour ce cas? J'ai trouvé un groupe d'articles qui parle de construire plus de commandes personnalisées statiques et d'utiliser des commandes pré-construites dans des cellules auto-dimensionnées.

          Cependant, je n'ai rien trouvé qui a rassemblé une solution pour gérer ces deux.


8 commentaires

N'utilisez pas la mise en page automatique? :)


@Wingzero. Je veux écrire un Compont qui se comporte comme un Uilabel. Il pourrait être placé dans une cellule d'auto-dimensionnement et bien envelopper son contenu (tout en prolongeant la hauteur de cette cellule de la taille de la cellule). C'est à peu près ça. D'AILLEURS. Uilabel IntristicContSeStiser n'est pas complètement faute agnostique. Il utilise préférélayoutoutwidth (qui est à peu près de cadre.Width) pour déterminer comment il devrait être couché.


@Victorronin, oui je pense que vous êtes de la bonne façon et que vous dites que vous avez des difficultés à calculer intriscientConsetsize, ne savez pas comment obtenir la largeur? Ensuite, qu'est-ce que vous êtes à l'intérieur de votre UIView?, vous devez déjà connaître la largeur lorsque vous dépassez la taille intrinsèque.


@Wingzero. Faites-vous référence à une largeur d'un UIView ou de mon contenu. Si vous parlez de la largeur de UIView, c'est un n ° 2 que j'ai décrit. Le cadre aura mal (valeur IB Builder VS Real Runtime Value) tout en appelant à IntrinsicContSIser. Si vous parlez de la largeur de mon contenu, alors oui je le sais. Cependant, comme avec le texte, il pourrait être exprimé à titre d'exemple que 900 PT pour 1 ligne de 300 PT pour 3 lignes de texte. Je dois retourner plusieurs lignes pour laisser ios faire une cellule de hauteur correcte. Et cela nécessite que je connaisse une largeur UIView / cellule.


@Victorronin vrai, cela implique plus de travail. Mais avez-vous essayé de faire la mise en page avec CGRectureDivide / CgrRreCtinet ou leurs équivalents rapides? C'est bien moins de travail que de traiter avec les bords / postes (x, y).


@nielsbot Je n'ai pas essayé. Franchement, c'est mon plan B. Cependant, si je peux le faire avec de l'autoliga, il serait bien plus propre et élégant et permettra de tout encapsuler dans un composant.


Quelles méthodes avez-vous remplacée et vérifiez exactement? Et lequel avez-vous vu appelé?


@WAIn I Nardonnez la mise à jour de l'updateConstraints, intrinsèquesContent et a vu que les deux sont appelés à un rendu initial et lorsqu'une orientation de périphérique a changé.


4 Réponses :


6
votes

Je dois utiliser la section de réponse pour poster mes idées et aller de l'avant, bien que ce ne soit pas votre réponse, car je ne comprends pas parfaitement ce qui vous bloque, car je pense que vous connaissez déjà la taille intrinsèque et c'est tout. Sur la base des commentaires, j'ai essayé de créer une vue avec une propriété de texte et de remplacer le fichier intrinsèque: p>

P> MAXPREFERREDWIDTH n'est pas utilisé totalement, alors ignorez-le: P> xxx pré>

.m Fichier: p> xxx pré>

et un uableviewcell avec xib: p>

fichier d'en-tête: p> XXX PRE>

.M Fichier: P>

@implementation LabelCell

- (void)awakeFromNib {
    [super awakeFromNib];
}

@end


9 commentaires

J'ai besoin d'examiner cette attention avec précaution. Deux commentaires: Il est intéressant d'intéresser que "self.superview.bounds.Size.Width" a travaillé pour vous. J'étais sous l'impression que la taille de la cellule ne figure pas avant plus tard dans le jeu. D'une part, cela pourrait être un raccourci «assez bon». D'autre part, pour Uilabel, vous pouvez configurer des contraintes (il n'occupera donc qu'une partie de la cellule). Cette solution ne sera pas en mesure de le gérer, car elle utilise uniquement la largeur de Superview (cellule).


@Victorronin L'outil est appelé révélateur, pas un outil de conception, mais un outil de débogage de l'interface utilisateur, vous ne regretterez jamais d'acheter.


@Victorronin La cellule la mise en page comme plusieurs fois, je me souviens. Pour le second, qu'est-ce que vous entendez en partie? S'il ne peut pas occuper la pleine hauteur de la cellule, un contenu étreignant / résistance peut aider


@Victorronin l'avez-vous réglé?


Je vous accorderai une prime, en tenant compte que vous êtes la seule personne qui a travaillé sur ce problème. Cependant, la question qui est toujours sans réponse est de savoir comment obtenir une largeur de contrôle basée sur toutes sortes de contraintes pouvant exister dans la cellule (s'il y a d'autres contrôles dans la cellule)


Merci, mais je pense avoir besoin de le gagner :) Voulez-vous dire que si cet uIView est à gauche, et une autre vue à droite, l'UIView n'est donc pas la largeur de la cellule? Si cela est vrai, alors ce que je pense, c'est au moins, nous pouvons itérer toutes les contraintes de la cellule, calculer manuellement quelle largeur est laissée pour cet UIView. De plus, vous pouvez vérifier si ce cadre d'UIVIEW est calculé par le moteur Autolayout en ce moment. Si cela est vrai, alors ce sera assez facile. Si vous avez un projet de démonstration qui est préférable, nous connaissons le problème.


Enfin, je suis revenu pour m'y regarder. Il y a deux problèmes actuellement. Le problème n ° 1 est que nous utilisons "self.superview.bounds.Size.width". Dans le projet réel, la composition cellulaire pourrait être complexe et un contrôle auto-dimensionnement ne peut occuper qu'une partie de la cellule. Vous avez suggéré: «Nous pouvons parcourir toutes les contraintes de la cellule, calculer manuellement quelle largeur». Je ne pense pas que Uilabel fasse cela et sa tâche complexe (à peu près, vous devez construire un moteur de résolution de contrainte sur votre propre VS en utilisant iOS One).


Deuxième problème est que IntrinsicconSIser n'est pas appelé à une rotation de l'appareil. Je me demande comment Uilabel le gère. Je suis sûr que cela n'a pas besoin d'ajouter une manipulation de la rotation dans une entreprise de vue. Penser à voix haute: remplace-t-il une méthode et appelez invalidationnristique?


Réellement. Je n'y ai pas pensé. Je le ferai. Merci.



1
votes

Pour construire sur l'autre réponse de @wingzero, la mise en page est un problème complexe ...

Le maxpreferredwidth est important et concerne préférémaxlayoutwidth de uilabel . Le point de cet attribut est de dire à une étiquette de ne pas simplement être une ligne longue et de préférer envelopper si la largeur correspond à cette valeur. Ainsi, lorsque vous calculez la taille intrinsèque, vous utiliseriez le minimum du préférémaxlayoutwidth (si défini) ou la largeur de la vue en tant que largeur maximale.

Un autre aspect clé est InvalidateIntrinsicContSisez sur que la vue doit appeler elle-même chaque fois que quelque chose change et signifie une nouvelle disposition est requise.

Uilabel ne gère pas la rotation - il ne le sait pas. Il incombe au contrôleur d'affichage de détecter et de manipuler la rotation, généralement en invalidant la mise en page et en mettant à jour la taille de la vue avant de déclencher une nouvelle opération de mise en page. Les étiquettes (et autres vues) sont juste là pour gérer la disposition résultante. Dans le cadre de la rotation que vous (c'est-à-dire un contrôleur d'affichage) peut modifier le préférémaxlayoutwidth car il est logique de permettre une plus grande largeur dans la disposition de paysage par exemple.


5 commentaires

Plusieurs questions ici. Quelque chose devrait définir préférémaxlayoutwidth. Je peux créer de telles biens dans ma classe. Cependant, il n'est pas clair quand il doit être réglé (spécifiquement pour vous assurer qu'il correspond à une taille calculée par d'autres contraintes). Je conviens que Uilabel ne s'inscrit pas directement pour les changements d'orientation du périphérique. Cependant, il remplace certaines méthodes UIView pour voir que la mise en page a changé. Vous pouvez mettre une étiquette dans UitailView et vous remarquerez que vous n'avez pas besoin de code supplémentaire pour gérer la rotation.


@Wingzero Le contrôle personnalisé ne remplace pas ces méthodes et, par conséquent, ne voyez pas que la mise en page a changé (à la suite du changement d'orientation du périphérique)


La rotation ne sera traitée qu'avec un code supplémentaire zéro si vous utilisez un contrôleur de vue de la table - car il contient le code redimensionner la table et exécuter une nouvelle disposition (ou spécifiquement, calcul de la hauteur de table) ... Vous devez définir le < Code> préférémaxlayoutwidth lorsque vous concevez le contenu de la cellule.


Je crois que l'utableview (VS UitablesController) gère également la rotation. ce qui signifie qu'il exécute une nouvelle mise en page. Donc, si vous avez générique UIViewController. Placez l'utableview en elle et mettez des cellules avec des uilabels, alors cela fonctionnera également sans aucun code spécial.


Il est possible que la vue de table elle-même invalide sa mise en page lorsque son cadre change



-2
votes

 Entrez la description de l'image ici

cherchez-vous quelque chose comme ça ^^? La cellule a des hauteurs dynamiques pour faciliter le contenu de l'Uilabel, et il n'y a aucun code pour calculer la taille / la largeur / la hauteur, que ce soit - juste quelques contraintes.

Essentiellement, l'étiquette à gauche a une marge supérieure, inférieure et avancée à la cellule et la marge de fuite à l'étiquette latérale droite, qui a une marge de fuite à la cellule. Vous avez juste besoin d'une étiquette? Ignorer l'étiquette latérale droite puis et configurer l'étiquette latérale gauche avec une contrainte de fuite à la cellule.

Et si vous avez besoin que l'étiquette soit multi-lignes, configurez cela. Définissez le (code> Numberfylines à 2, 3 ou 0, à vous de vous.

Vous n'avez pas besoin de calculer la hauteur de la cellule de vue de la table, la mise en page automatique calculera pour vous; Mais vous devez le faire savoir que, en lui disant d'utiliser Auto Dimension: Self.tableview.rowheightight = UitailViewAutomaticDimension ou de retourner dans TableView: hauteurfrofortordexpath: . Et vous pouvez également indiquer une vue sur la table une estimation «rugueuse» dans TableView: estimationHeightFornorTindexpath: pour une meilleure performance.

toujours ne fonctionne pas? Définissez la priorité de résistance de la compression de contenu - Vertical à 1000 / requis pour l'Uilabel en question, de sorte que le contenu de l'étiquette fera de son mieux pour «résister à la compression» et que la configuration sera entièrement reconnue.

et ça tourne? Essayez d'observer la rotation (il y a des notifications de changement d'orientation), puis mettez à jour la mise en page ( setNeedsLayout ).

toujours ne fonctionne pas? Plus lit ici: Utilisation de la mise en page automatique dans UitailView pour les mises en page dynamique des cellules et des hauteurs de rangées variables


4 commentaires

Vous parlez de faire une auto-dimensionnement des cellules et de les utiliser dans la table. Je cherche un moyen de créer un composant personnalisé avec une hauteur dynamique qui fonctionne correctement dans une cellule d'auto-dimensionnement.


@Victorronin afin que vous disiez que la cellule a déjà une logique auto-dimensionnée et vous souhaitez avoir un composant dans la cellule avec une taille correcte? Si tel est le cas, vous pouvez toujours avoir un UIView avec les contraintes de haut / bas / inférieure / avancée / de fin de la cellule afin que le composant soit redimensionné en conséquence. Désolé si ce n'est pas ce que tu voulais.


Je pense que vous devez lire ma question avec précaution et vérifier d'autres réponses. La cellule est déjà auto-dimensionnement (vous pouvez en lire ici - développeur.apple.com/library/ios/documentation/usereExpérience / ... Cellules de dimensionnement demande des vues qui sont à l'intérieur d'eux quelle taille ils devraient être (et cela est très différent de celui-ci. Disposition lorsqu'il est par des contraintes une cellule forcera une vue d'une taille spécifique). En conséquence, il s'agit d'un travail de contrôle personnalisé pour déterminer quelle taille doit être.


Ce que vous avez dit est définitivement vrai, et c'est aussi ce que j'ai dit - les composants à l'intérieur de la cellule doivent indiquer à la cellule quelle taille il devrait être, et les composants qui sont basés sur 1) calculant leur taille de contenu intrinsèque, ou 2) en installant contraintes entre eux et contre la cellule. D'autres réponses ici tentent de calculer la taille intrinsèque, mais je ne pense pas que cela soit la façon de le faire. Si vous avez dit 5 vues à l'intérieur d'une cellule et que vous avez correctement configuré les contraintes, la cellule observera les contraintes et la taille de manière dynamique elle-même (en particulier sa hauteur).



3
votes

Voici une solution qui répond à vos exigences et est également ibdesignable, de sorte que cela aperçu de la vie en direct dans l'interface Builder. Cette classe mettra une série de carrés (le nombre total est égal à la propriété ibinspectable ). Par défaut, il va simplement les poser tout en une longue ligne. Mais si vous définissez la propriété wrap ibinspectable sur sur , il enveloppera les carrés et augmente sa hauteur en fonction de sa largeur contrainte (comme un Uilabel avec des numéros de remplissage == 0). Dans une cellule de vue de la table auto-dimensionnée, cela permettra de pousser le haut et le bas pour accueillir la taille intrinsèque enveloppée de la vue personnalisée.

Le code: xxx

Dans mon exemple d'application, j'ai défini la vue sur la table pour déroquer une cellule contenant cette vision personnalisée pour chaque ligne et définir la propriété comptez de la vue personnalisée sur 20 * la ligne de IndexPath. La vue personnalisée est contrainte à 50% de la largeur de la cellule. Sa largeur changera automatiquement lors de la déplacement entre le paysage et le portrait. Étant donné que chaque cellule de table successive enveloppe une chaîne de carrés plus longue et plus longue, chaque cellule est automatiquement dimensionnée pour être plus grande et plus grande.

Lorsque vous courez, il ressemble à ceci (Comprend une démonstration de rotation):

 vue personnalisée dans les cellules de table auto-dimensionnement


3 commentaires

Daniel. Ceci est incroyable. Merci beaucoup. Je cherchais ça depuis longtemps. Permettez-moi de prendre la prime pour vous. Si cela ne vous dérange pas, pouvez-vous faire deux choses: ajoutez quelques commentaires à travers le code (je pourrai le comprendre à partir d'un exemple de travail, mais cela aidera tout le monde qui l'examinera). En outre, il y a un petit bug. Première fois où vous le démarrez, il montre un mauvais nombre de carrés par cellule (et seulement après la rotation, il commence à indiquer le bon).


D'AILLEURS. La rendre contestable est vraiment belle touche :)


C'est un impressionnant, bien que un peu compliqué, mais je pense que la partie principale est toujours intrinsèque () concept