Largeur de cellule dynamique de UICollectionView en fonction de la largeur de l'étiquette

92

J'ai un UICollectionView, qui charge les cellules de la cellule réutilisable, qui contient l'étiquette. Un tableau fournit du contenu pour cette étiquette. Je peux facilement redimensionner la largeur de l'étiquette en fonction de la largeur du contenu avec sizeToFit. Mais je ne peux pas faire de cellule pour adapter l'étiquette.

Voici le code

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    arrayOfStats =  @[@"time:",@"2",@"items:",@"10",@"difficulty:",@"hard",@"category:",@"main"];
}

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:     (NSInteger)section{
    return [arrayOfStats count];
}

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath{

    return CGSizeMake(??????????);
}

- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView{

    return 1;
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{

    Cell *cell = (Cell *) [collectionView dequeueReusableCellWithReuseIdentifier:@"qw" forIndexPath:indexPath];
    cell.myLabel.text = [NSString stringWithFormat:@"%@",[arrayOfStats objectAtIndex:indexPath.item]];
    // make label width depend on text width
    [cell.myLabel sizeToFit];

    //get the width and height of the label (CGSize contains two parameters: width and height)
    CGSize labelSize = cell.myLbale.frame.size;

    NSLog(@"\n width  = %f height = %f", labelSize.width,labelSize.height);

    return cell;
}
pulpe
la source
genre de problème similaire ... stackoverflow.com/questions/24915443/… ???
Fattie

Réponses:

83

En sizeForItemAtIndexPathretour la taille du texte

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath{

    return [(NSString*)[arrayOfStats objectAtIndex:indexPath.row] sizeWithAttributes:NULL];
}
Basheer_CAD
la source
4
Vous ne pouvez pas imaginer comment je vous remercie! Cela fonctionne vraiment. Maintenant, je n'ai plus qu'à résoudre le problème [cell.myLabel sizeToFit], car il n'apparaît dans sa taille réelle qu'après le défilement. Mais je n'ai même pas été proche de votre solution.
pulp
merci pour votre aide mais j'ai encore un problème. Quand je décommente [cell.myLabel sizeToFit] j'ai des mots tronqués et des lettres coupées en bas mais ça devient ok après que je fasse défiler (les mots ont leur taille normale et les lettres sautent un peu). Si je commente et désactive le message [cell.myLabel sizeToFit] (j'ai décidé de jouer avec IB et cela fonctionne très bien), j'ai des mots coupés à la fin et en bas. J'ai fait une capture d'écran goo.gl/HaoqQV Ce n'est pas très net sur les écrans non rétiniens mais vous pouvez voir que les lettres se sont coupées. Votre suggestion sur la façon de résoudre sera vraiment appréciée!
pulp
2
au lieu de sizeToFit, utilisez sizeWithAttributes pour obtenir le CGSize du texte, puis définissez le cadre de l'étiquette avec une nouvelle taille.
Basheer_CAD
merci pour la suggestion mais j'ai toujours myLabel coupé en bas et à la fin. Peut-être que je me trompe avec la mise en œuvre de votre suggestion. Voici mon code
pulp
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{ Cell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"qw" forIndexPath:indexPath]; cell.myLbale.text = [NSString stringWithFormat:@"%@",[arrayOfStats objectAtIndex:indexPath.item]]; CGSize textSize; textSize = [[arrayOfStats objectAtIndex:indexPath.item] sizeWithAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:12.0f]}]; [cell.myLbale sizeThatFits:textSize]; //[cell.myLbale sizeToFit]; return cell; }
pulp
48

Swift 4.2+

Le principe est:

  1. Assurez-vous que la délégation est configurée (par exemple collectionView.delegate = self)

  2. Implémenter UICollectionViewDelegateFlowLayout(il contient la signature de méthode nécessaire).

  3. collectionView...sizeForItemAtMéthode d' appel .

  4. Pas besoin de pont coulé Stringà NSStringà l' appel size(withAttributes:méthode. Swift l' Stringa prêt à l'emploi.

  5. Les attributs sont les mêmes que ceux que vous avez définis (NS)AttributedString, c'est-à-dire la famille de polices, la taille, le poids, etc. Paramètre facultatif.


Exemple de solution:

extension ViewController: UICollectionViewDelegateFlowLayout {
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        return "String".size(withAttributes: nil)
    }
}

Mais vous voudrez probablement spécifier des attributs de chaîne concrets respectifs à votre cellule, par conséquent, le retour final ressemblerait à:

extension ViewController: UICollectionViewDelegateFlowLayout {
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        // dataArary is the managing array for your UICollectionView.
        let item = dataArray[indexPath.row]
        let itemSize = item.size(withAttributes: [
            NSAttributedString.Key.font : UIFont.boldSystemFont(ofSize: 14)
        ])
        return itemSize
    }
}

Pourquoi vous NE DEVRIEZ PAS utiliser UILabel pour calculer la taille? Voici la solution suggérée:

let label = UILabel(frame: CGRect.zero)
label.text = textArray[indexPath.item]
label.sizeToFit()

Oui, vous obtenez le même résultat. Cela semble simpliste et peut sembler une solution incontournable. Mais c'est inapproprié parce que: 1) c'est cher, 2) frais généraux et 3) sale.

C'est cher car UILabel est un objet d'interface utilisateur complexe, qui est créé à chaque itération chaque fois que votre cellule est sur le point de s'afficher, même si vous n'en avez pas besoin ici. C'est une solution de surcharge car vous n'avez besoin que d'obtenir la taille d'un texte, mais vous allez jusqu'à créer un objet d'interface utilisateur complet. Et c'est sale pour cette raison.

Hexfire
la source
1
n'oubliez pas de réglercollectionView.delegate == self // or whatever-object-which-do-it
Fitsyu
Très bonne réponse. Même si la taille que j'obtenais était un peu plus petite que nécessaire, sur différentes longueurs de cordes, j'ai décidé de modifier un peu la taille moi-même. L'ajout de 5 points supplémentaires à la largeur a fait l'affaire:CGSize(width: title.size(withAttributes: [NSAttributedString.Key.font : UIFont.boldSystemFont(ofSize: 16)]).width + 5, height: 50)
Starsky
37

J'ai trouvé un petit truc pour swift 4.2

Pour largeur dynamique et hauteur fixe:

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        let label = UILabel(frame: CGRect.zero)
        label.text = textArray[indexPath.item]
        label.sizeToFit()
        return CGSize(width: label.frame.width, height: 32)
    }

Pour hauteur dynamique et largeur fixe:

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
            let label = UILabel(frame: CGRect.zero)
            label.text = textArray[indexPath.item]
            label.sizeToFit()
            return CGSize(width: 120, height: label.frame.height)
        }
Hassan Izhar
la source
8
Soyez prudent en utilisant ceci. Créer et dessiner un nouveau UILabel pour chaque calcul de cellule est très coûteux.
AnthonyR
besoin d'ajouter UICollectionViewDelegateFlowLayout
cristianego
3
Pour répondre au commentaire selon lequel la création d'une étiquette factice coûte cher, vous pouvez peut-être créer une seule étiquette factice au lieu de plusieurs. Tout ce que vous voulez vraiment, c'est la taille du texte des attributs d'étiquette. En fin de compte, c'est essentiellement la même chose que le calcul de la taille du texte via sizeWithAttributes, alors peut-être que c'est la réponse préférée.
Stephen Paul
@Hassan Merci bro, cela a fonctionné pour moi, mais j'ai trouvé un problème de largeur, donc j'ai ajouté le retour CGSize (width: label.frame.width + 50, height: 32). Ensuite, cela a fonctionné, je pense que cette réponse devrait être en tête de liste.
Arshad Shaik
27

Vérifiez ci-dessous le code que vous pourriez donner à CGSize très court.

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath{

    NSString *testString = @"SOME TEXT";
    return [testString sizeWithAttributes:NULL];
}
Raza.najam
la source
@Vineesh TP Merci pour votre réponse, je vais certainement la vérifier et vous le faire savoir!
pulp
+1000 Celui-ci fonctionne définitivement. Votre message et celui de Basheer devraient avoir une coche verte ensemble.
rocky raccoon
Une idée comment faire cela avec Swift 3?
UKDataGeek
1
Merci l'homme, c'est l'heure de la fête !!
Ravi
18

Dans Swift 3

let size = (arrayOfStats[indexPath.row] as NSString).size(attributes: nil)
Johnson
la source
14

Swift 4

let size = (arrayOfStats[indexPath.row] as NSString).size(withAttributes: nil)
Barath
la source
0

// ajouter la vue didload

UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
    [layout setScrollDirection:UICollectionViewScrollDirectionHorizontal];
    layout.estimatedItemSize = CGSizeMake(self.breadScrumbCollectionView.frame.size.width, 30); 
self.breadScrumbCollectionView.collectionViewLayout = layout;
Apurva Gaikwad
la source