Подтвердить что ты не робот

Недопустимый фрейм Subview при создании UICollectionViewCell

Проблема

Я создал UICollectionViewController с пользовательским UICollectionViewCell. Пользовательская ячейка содержит большой и прямоугольный UIView (с именем colorView) и UILabel (с именем nameLabel).

Когда коллекция сначала заполняется ячейками, и я печатаю colorView.frame, печатные фреймы имеют неправильные значения. Я знаю, что они неправильны, потому что кадры colorView больше, чем сами кадры ячеек, хотя colorView рисуется правильно.

Однако, если я прокрутку коллекцииView достаточно, чтобы вызвать повторное использование ранее созданной ячейки, colorView.frame теперь имеет правильные значения! Мне нужны правильные кадры, потому что я хочу применить закругленные углы к слою colorView, и для этого мне нужен правильный размер coloView. Кстати, если вам интересно, colorView.bounds также имеет такое же неправильное значение размера, как и colorView.frame.

Вопрос

Почему фреймы неправильны при создании ячеек?

И теперь некоторый код

Это мой UICollectionViewCell:

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

и это 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)
    }
}

Представление коллекции настроено для отображения двух ячеек за раз, по вертикали, каждая ячейка, содержащая большой прямоугольник, окрашенный цветом и меткой с именем цвета.

Когда я просто запускаю вышеуказанный код на симуляторе, я получаю следующий результат:

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)

Это странный результат. colorView.frame имеет высоту 568 точек, а ячейка - всего 333,5 балла.

Если я перетаскиваю элемент collectionView вниз и ячейка повторно используется, выводится следующий результат:

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)

Что-то, что я не могу понять, произошло по пути, который исправляет рамку colorView.

Я думаю, что это связано с тем, что ячейка загружается из Nib, поэтому вместо использования инициализатора init (frame: frame) контроллер использует инициализатор init (coder: aCoder), поэтому, как только ячейка создана, вероятно, с некоторым фреймом по умолчанию, который я никак не могу редактировать.

Поймите любую помощь, которая позволит мне понять, что происходит!

Я использую Xcode 6.1.1. с iOS SDK 8.1.

4b9b3361

Ответ 1

Вы можете получить финальные кадры своей ячейки, переопределив layoutIfNeeded() в своем пользовательском классе Cell следующим образом:

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

то в вашем источнике данных UICollectionView Source method cellForRowAtIndexPath: сделайте следующее:

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

Ответ 2

У меня была такая же проблема с UICollectionViewCell с использованием ограничений автоматической компоновки.

Мне пришлось называть layoutIfNeeded до того, как я настроил свой подзаголовок, который основывался на ширине кадров представлений.

Ответ 3

Хорошо, теперь я понимаю, что ячейка создана до того, как автомат определяет его фреймы. Вот почему в момент создания границы ошибочны. Когда ячейки повторно используются, кадры уже исправлены.

У меня возникла эта проблема при создании пользовательского UIView, который помещал некоторые слои и подпункты в определенные координаты. Когда экземпляры этого UIView были созданы, размещение субвью было неправильным (поскольку автомат не начинался).

Я выяснил, что вместо того, чтобы настраивать представления subviews на init(coder: aCoder), мне пришлось переопределить метод layoutSubviews(). Это вызывается, когда автомаркетинг запрашивает у каждого вида макет своих собственных подзонов, поэтому в этот момент, по крайней мере, у родительского представления есть правильный фрейм, и я могу использовать его для правильной подкладки.

Возможно, если бы я использовал ограничения для кода пользовательского вида вместо того, чтобы заниматься карманами и позиционированием, то макет был бы выполнен правильно, и нет необходимости переопределять layoutSubviews().

Ответ 4

Если эта проблема связана с графикой Core Graphics в iOS 10, Swift 3.0.1.

Вот способ, который помог мне:

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

Моя проблема заключалась в том, что формы Core Graphics не были рассчитаны правильно, потому что не был вызван layoutSubviews().