Обновлено для Swift 5
preferredLayoutAttributesFittingAttributes
переименовать preferredLayoutAttributesFitting
и использовать авторазмер
Обновлено для Swift 4
systemLayoutSizeFittingSize
переименован в systemLayoutSizeFitting
Обновлено для iOS 9
Увидев, что мой GitHub-решение сломалось под iOS 9, у меня наконец-то появилось время полностью исследовать проблему. Сейчас я обновил репо, чтобы включить несколько примеров различных конфигураций для ячеек с самоконтролем. Мой вывод состоит в том, что самоконтролирующие клетки хороши в теории, но на практике беспорядочные. Слово предостережения при продолжении самоконтроля клеток.
TL; DR
Проверьте мой проект GitHub
Саморазмерные ячейки поддерживаются только в макете потока, поэтому убедитесь, что это то, что вы используете.
Есть две вещи, которые вам нужно настроить для работы с саморазмерными ячейками.
1. Установите estimatedItemSize
наUICollectionViewFlowLayout
Макет потока станет динамичным по своей природе после того, как вы установите estimatedItemSize
свойство.
self.flowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
2. Добавить поддержку для определения размеров на вашем подклассе ячейки
Это входит в 2 аромата; Авто-макет или пользовательское переопределение preferredLayoutAttributesFittingAttributes
.
Создание и настройка ячеек с помощью Auto Layout
Я не буду вдаваться в подробности об этом, поскольку есть замечательный пост SO о настройке ограничений для ячейки. Просто будьте осторожны, что Xcode 6 сломал кучу вещей с iOS 7, поэтому, если вы поддерживаете iOS 7, вам нужно будет сделать что-то вроде того, чтобы убедиться, что autoresizingMask установлен в contentView ячейки, а границы contentView установлены как границы ячейки, когда ячейка загружена (т.е. awakeFromNib
).
Вы должны знать, что ваша ячейка должна быть более серьезно ограничена, чем ячейка табличного представления. Например, если вы хотите, чтобы ваша ширина была динамической, тогда ваша ячейка нуждается в ограничении высоты. Аналогично, если вы хотите, чтобы высота была динамической, вам понадобится ограничение ширины для вашей ячейки.
Внедрите preferredLayoutAttributesFittingAttributes
в свою ячейку
Когда эта функция вызывается, ваше представление уже было настроено с содержимым (то cellForItem
есть было вызвано). Предполагая, что ваши ограничения были установлены соответствующим образом, у вас может быть такая реализация:
//forces the system to do one layout pass
var isHeightCalculated: Bool = false
override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
//Exhibit A - We need to cache our calculation to prevent a crash.
if !isHeightCalculated {
setNeedsLayout()
layoutIfNeeded()
let size = contentView.systemLayoutSizeFitting(layoutAttributes.size)
var newFrame = layoutAttributes.frame
newFrame.size.width = CGFloat(ceilf(Float(size.width)))
layoutAttributes.frame = newFrame
isHeightCalculated = true
}
return layoutAttributes
}
ПРИМЕЧАНИЕ В iOS 9 поведение немного изменилось, что может привести к сбоям в вашей реализации, если вы не будете осторожны (подробнее здесь ). При реализации preferredLayoutAttributesFittingAttributes
вам необходимо убедиться, что вы изменяете фрейм атрибутов макета только один раз. Если вы этого не сделаете, макет будет вызывать вашу реализацию неопределенно долго и в конечном итоге вылетит. Одним из решений является кэширование вычисленного размера в вашей ячейке и аннулирование этого в любое время, когда вы повторно используете ячейку или меняете ее содержимое, как я сделал сisHeightCalculated
свойством.
Испытайте свой макет
На этом этапе у вас должны быть «функционирующие» динамические ячейки в вашем collectionView. Я еще не нашел готового решения, достаточного для моих тестов, поэтому не стесняйтесь комментировать, если у вас есть. По-прежнему ощущается, что UITableView
выигрывает битву за динамические размеры ИМХО.
Предостережения
Помните, что если вы используете ячейки-прототипы для вычисленияtimateItemSize - это сломается, если ваша XIB использует классы размеров . Причина этого заключается в том, что при загрузке ячейки из XIB ее класс размера будет настроен с помощью Undefined
. Это будет нарушено только в iOS 8 и выше, поскольку в iOS 7 класс размера будет загружаться в зависимости от устройства (iPad = обычный-любой, iPhone = компактный-любой). Вы можете либо установитьtimateItemSize без загрузки XIB, либо вы можете загрузить ячейку из XIB, добавить ее в collectionView (это установит traitCollection), выполнить макет, а затем удалить его из суперпредставления. В качестве альтернативы вы также можете заставить свою ячейку переопределять traitCollection
геттер и возвращать соответствующие черты. Тебе решать.
Дайте мне знать, если я что-то пропустил, надеюсь, я помог и удачи в кодировании