web-dev-qa-db-fra.com

Le cadre de sous-vue est incorrect lors de la création de UICollectionViewCell

Le problème

J'ai créé un UICollectionViewController avec un UICollectionViewCell personnalisé. La cellule personnalisée contient une grande UIView rectangulaire (nommée colorView) et une UILabel (nommée nameLabel).

Lorsque la collection est remplie pour la première fois avec ses cellules et que j'imprime colorView.frame, les cadres imprimés ont des valeurs incorrectes. Je sais qu'ils sont incorrects, car les cadres colorView sont plus grands que le cadre de cellule eux-mêmes, même si colorView est dessiné correctement.

Cependant, si je fais suffisamment défiler la collectionView pour déclencher une réutilisation d'une cellule précédemment créée, le colorView.frame a maintenant des valeurs correctes! J'ai besoin des bons cadres parce que je veux appliquer des coins arrondis à la couche colorView et j'ai besoin de la bonne taille coloView pour ce faire. Au fait, au cas où vous vous poseriez la question, colorView.bounds a également la même valeur de taille incorrecte que colorView.frame.

La question

Pourquoi les cadres sont-ils incorrects lors de la création des cellules?

Et maintenant du code

Voici mon UICollectionViewCell:

class BugCollectionViewCell: UICollectionViewCell {
    @IBOutlet weak var colorView: UIView!
    @IBOutlet weak var nameLabel: UILabel!
}

et voici l'UICollectionViewController:

import UIKit

let reuseIdentifier = "Cell"
let colors = [UIColor.redColor(), UIColor.blueColor(),
              UIColor.greenColor(), UIColor.purpleColor()]
let labels = ["red", "blue", "green", "purple"]

class BugCollectionViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
    override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
        return 1
    }

    override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return colors.count
    }

    override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as BugCollectionViewCell

        println("ColorView frame: \(cell.colorView.frame) Cell frame: \(cell.frame)")

        cell.colorView.backgroundColor = colors[indexPath.row]
        cell.nameLabel.text = labels[indexPath.row]

        return cell
    }

    func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
        let width = self.collectionView?.frame.width
        let height = self.collectionView?.frame.height
        return CGSizeMake(width!, height!/2)
    }
}

La vue de collection est configurée afin d'afficher deux cellules à la fois, verticalement, chaque cellule contenant un grand rectangle peint avec une couleur et une étiquette avec le nom de la couleur.

Lorsque je lance simplement le code ci-dessus sur le simulateur, j'obtiens le résultat imprimé suivant:

ColorView frame: (0.0,0.0,320.0,568.0) Cell frame: (0.0,0.0,375.0,333.5)
ColorView frame: (0.0,0.0,320.0,568.0) Cell frame: (0.0,343.5,375.0,333.5)

C'est un résultat étrange - colorView.frame a une hauteur de 568 points, tandis que le cadre de la cellule ne mesure que 333,5 points.

Si je fais glisser la collectionView vers le bas et qu'une cellule est réutilisée, le résultat suivant est imprimé:

ColorView frame: (8.0,8.0,359.0,294.0) Cell frame: (0.0,1030.5,375.0,333.5)
ColorView frame: (8.0,8.0,359.0,294.0) Cell frame: (0.0,343.5,375.0,333.5)

Quelque chose, que je ne peux pas comprendre, s'est produit en cours de route qui corrige le cadre de colorView.

Je pense que cela a quelque chose à voir avec le fait que la cellule est chargée à partir de la Nib, donc au lieu d'utiliser l'initialiseur init (frame: frame), le contrôleur utilise l'initialiseur init (coder: aCoder), donc dès que la cellule est créé il est probablement livré avec un cadre par défaut que je ne peux pas modifier de toute façon.

J'apprécierai toute aide qui me permettra de comprendre ce qui se passe!

J'utilise Xcode 6.1.1. avec le SDK iOS 8.1.

36
Felipe Ferri

Vous pouvez obtenir les images finales de votre cellule en remplaçant layoutIfNeeded () dans votre classe Cell personnalisée comme ceci:

override func layoutIfNeeded() {
    super.layoutIfNeeded()
    self.subView.layer.cornerRadius = self.subView.bounds.width / 2
}

puis dans votre méthode de source de données UICollectionView cellForRowAtIndexPath: procédez comme suit:

let cell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath) as! CustomCollectionViewCell
cell.setNeedsLayout()
cell.layoutIfNeeded()
56
Jon Vogel

J'ai eu le même problème avec un UICollectionViewCell en utilisant des contraintes de disposition automatique.

J'ai dû appeler layoutIfNeeded avant de configurer ma sous-vue en fonction de la largeur du cadre des vues.

16
RyanG

A eu ce problème avec le dessin Core Graphics iOS 10, Swift 3.0.1.

Ajoutez cette méthode à la sous-classe UICollectionView:

override func didMoveToSuperview() {
    super.didMoveToSuperview()
    setNeedsLayout()
    layoutIfNeeded()
}

Mon problème était que les formes Core Graphics n'étaient pas calculées correctement, car une layoutSubviews() n'était pas appelée.

8
Vadim Bulavin

Ok, je comprends maintenant que la cellule est créée avant que la mise en page automatique ne définisse ses cadres. C'est la raison pour laquelle au moment de la création, les limites sont fausses. Lorsque les cellules sont réutilisées, les cadres ont déjà été corrigés.

J'avais ce problème lors de la création d'une UIView personnalisée qui plaçait certaines couches et sous-vues dans des coordonnées spécifiques. Lorsque des instances de cette vue UIV ont été créées, le placement des sous-vues était incorrect (car la mise en page automatique n'avait pas encore démarré).

J'ai découvert qu'au lieu de configurer les sous-vues de vue sur init(coder: aCoder) je devais remplacer la méthode layoutSubviews(). Cela s'appelle lorsque la mise en page automatique demande à chaque vue de mettre en page ses propres sous-vues, donc à ce stade, au moins la vue parent a le cadre correct et je peux l'utiliser pour poser correctement les sous-vues.

Probablement, si j'avais utilisé des contraintes sur le code de la vue personnalisée au lieu de m'occuper des tailles et du positionnement des cadres, la mise en page aurait été effectuée correctement et il ne serait pas nécessaire de remplacer layoutSubviews().

8
Felipe Ferri