UICollectionView Самостійне розмір комірок з автоматичним розташуванням


207

Я намагаюся отримати розмір самості, UICollectionViewCellsпрацюючи з автоматичним макетом, але я не можу отримати комірки за розміром самих по вмісту. У мене виникають проблеми з розумінням того, як оновлюється розмір комірки зі вмісту того, що знаходиться всередині contentView комірки.

Ось налаштування, яку я спробував:

  • Користувальницький UICollectionViewCellз UITextViewу своєму contentView.
  • Прокрутка для UITextViewвимкнено.
  • Горизонтальним обмеженням contentView є: "H: | [_textView (320)]", тобто UITextViewзакріплений зліва від комірки з явною шириною 320.
  • Вертикальне обмеження contentView: "V: | -0 - [_ textView]", тобто UITextViewзакріплене у верхній частині комірки.
  • UITextViewМає обмеження висоти установки на постійна якої UITextViewзвіти будуть відповідати тексту.

Ось як це виглядає з фоном комірки, встановленим червоним кольором, а UITextViewфоном - Синім: фон комірки червоний, фон UITextView синій

Я поставив проект , який я грав з на GitHub тут .


Ви реалізуєте метод sizeForItemAtIndexPath?
Даніель Галаско

@DanielGalasko Я ні. Я розумію, що нова функція розміщення комірок в iOS 8 вже не вимагає від вас цього робити.
rawbee

не впевнений, звідки ви отримали цю інформацію, але вам все одно потрібно, подивіться на уривок WWDC asciiwwdc.com/2014/sesions/226
Даніель Галаско,

але знову ж таки, це залежить від вашого випадку використання, переконайтеся, що ви реалізуєте
оцінкуItemSize

1
2019 - Дуже важливо , щоб піти подивитися на це: stackoverflow.com/a/58108897/294884
Fattie

Відповіді:


309

Оновлено для 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 ароматах; Auto-Layout або призначене для користувача перевизначення preferredLayoutAttributesFittingAttributes.

Створіть та налаштуйте комірки за допомогою автоматичного макета

Я не буду докладно розповідати про це, оскільки існує геніальний повідомлення про налаштування обмежень для комірки. Просто будьте обережні, що 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виграє битву за динамічне розмір ІМХО.

Коваджі

Майте на увазі, що якщо ви використовуєте комірки прототипу для обчислення оціненогоItemSize - це порушиться, якщо ваш XIB використовує класи розмірів . Причиною цього є те, що коли ви завантажуєте свою клітинку з XIB, її клас розмірів буде налаштований Undefined. Це буде порушено лише на iOS 8 і вище, оскільки на iOS 7 клас розмірів завантажуватиметься на основі пристрою (iPad = Звичайний-будь-який, iPhone = Компактний-Будь-який). Ви можете або встановити очікуванийItemSize, не завантажуючи XIB, або можете завантажити клітинку з XIB, додати її до collectionView (це встановить traitCollection), виконати макет і потім видалити його з нагляду. Крім того, ви також можете змусити вашу клітинку замінити traitCollectionгеттер і повернути відповідні риси. Тобі вирішувати.

Дайте мені знати, якщо я щось пропустив, сподіваюся, що я допоміг і вдало кодуйте



2
Я намагаюся реалізувати те ж саме з макетом потоку, проте коли я повертаю його newFrame з оновленим розміром, макет не здається перерахувати y позиції, які все ще базуються на висоті в оціночному розмірі. Чого не вистачає?
anna

@anna ви називаєте invalidateLayout на макеті?
Даніель Галаско

1
Ах, знайшов різницю, ти встановив оціночну висоту досить високою (400), тоді як я робив мінімум (44). Це допомогло. Але здається, що contentSize все ще не налаштовується правильно, доки ви не прокрутите весь цей шлях, тому все ще не дуже корисний. До речі, я змінив UITextView на UILabel, така ж поведінка.
Анна

17
Це, мабуть, принципово порушено в iOS 9 GM. Налаштування estimatedItemSizeпризводить до збоїв - начебто є величезна помилка в тому, як автоматична компоновка намагається обробити UICollectionViewCell.
Уес

3
Натрапивши на подібну проблему, чи хтось знайшов роботу?
Тоні

47

В iOS10 є нова константа, яка називається UICollectionViewFlowLayout.automaticSize(раніше UICollectionViewFlowLayoutAutomaticSize), тож замість цього:

self.flowLayout.estimatedItemSize = CGSize(width: 100, height: 100)

Ви можете скористатися цим:

self.flowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize

Він має кращі показники, особливо коли клітини у вашому представленні колекції мають постійний розмах

Доступ до макета потоку:

override func viewDidLoad() {
   super.viewDidLoad()

   if let flowLayout = collectionView?.collectionViewLayout as? UICollectionViewFlowLayout {
      flowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
   }
}

Swift 5 Оновлено:

override func viewDidLoad() {
   super.viewDidLoad()

   if let flowLayout = collectionView?.collectionViewLayout as? UICollectionViewFlowLayout {
      flowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
    }
}

8
Де ти це встановив? в viewdidload? Як ви отримуєте ручку на розкладці?
UKDataGeek

1
Мені вдалося реалізувати це - але воно має дивну поведінку, що змушує його центрувати клітини, якщо є одна клітинка. Ось переповнення стека питання про це - має мене в глухий кут: stackoverflow.com/questions/43266924 / ...
UKDataGeek

1
Ви можете отримати доступ до макета потоку через collectionViews collectionViewLayoutі просто перевірити, чи є він типуUICollectionViewFlowLayout
d4Rk

4
це робить мої клітини дуже маленькими, марними
user924

44

Кілька ключових змін у відповіді Даніела Галаско виправили всі мої проблеми. На жаль, у мене недостатньо репутації, щоб коментувати безпосередньо (поки що).

На кроці 1, використовуючи автоматичний макет, просто додайте єдиний батьківський UIView до комірки. ВСЕ, що знаходиться всередині клітини, повинно бути підглядом батьків. Це відповіло на всі мої проблеми. Хоча Xcode додає це для UITableViewCells автоматично, він не (але повинен) для UICollectionViewCells. Згідно з документами :

Щоб налаштувати зовнішній вигляд вашої комірки, додайте представлення, необхідні для подання вмісту елемента даних, як підвідси, до подання у властивості contentView. Не додайте безпосередньо підгляди до самої комірки.

Потім повністю пропустіть крок 3. Це не потрібно.


1
Цікаво; це спеціально для Xibs, але, тим не менше, дякую, спробую заплутатися з проектом Github і побачити, чи зможу я відтворити. Можливо, поділіть відповідь на Xib vs programmatic
Даніель Галаско,

Дуже корисний . Дякую!
ProblemSlover

2
iOS 9, Xcode 7. Осередки прототипуються в табло, встановлюють спеціальний підклас. Спробувавши створити властивість під назвою contentView, Xcode скаржиться на те, що вона конфліктує з наявною властивістю. Спробувавши додати підпогляди до цього self.contentViewта встановити обмеження для цього, потім програма виходить з ладу.
Nicolas Miari

@NicolasMiari Я не знаю, як це зробити програмно, моє рішення насправді просто додати єдиний uiview в XIB, де все йде всередині нього. Вам не потрібно створювати власність чи щось.
Метт Коала

Я зіткнувся з тією ж проблемою, що і @NicolasMiari. Коли я встановив оціненийContentSize для комірок, прототипованих на аркуші розкадровки з обмеженнями авторозміщення на iOS 9 / Xcode 7, програма виходить з ладу за винятком поганого доступу та без корисної стек-тракції
wasabi

34

В iOS 10+ це дуже простий двоступеневий процес.

  1. Переконайтесь, що весь вміст вашої комірки розміщується в одному UIView (або всередині нащадка UIView, як UIStackView, що значно спрощує автоматичний розклад). Як і при динамічному зміні розміру UITableViewCells, для всієї ієрархії представлення потрібно мати обмеження, налаштовані від самого зовнішнього контейнера до внутрішнього виду. Це включає обмеження між UICollectionViewCell та негайним переглядом дитини

  2. Інструктуйте розклад потоку вашого UICollectionView автоматично розміру

    yourFlowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize

Мені цікаво, як би загортання вмісту ваших комірок в UIView зробить автоматичну розкладку ефективнішою? Я думав, що це буде краще з більш дрібною ієрархією?
Джеймс Хілд

1
Я не згадав про продуктивність, лише простоту. Самостійні розміри комірок працюватимуть лише в тому випадку, якщо у вас є обмеження, що охоплюють увесь шлях між лівим і правим і між верхнім і нижнім. Досягти цього найпростіше, коли всі погляди загорнуті в один контейнер. Якщо цей контейнер є UIStackView, то легше, якщо це будь-який інший нащадок UIView.
Мармой

Скажіть, будь ласка, як встановити постійну висоту (наприклад, встановити висоту 25, і ширина буде автоматично змінена) для UICollectionViewFlowLayoutAutomaticSize.
Светослав Атанасов

Використовуючи Swift 4.1, Xcode повідомляє, що я UICollectionViewFlowLayout.automaticSizeбув перейменований на UICollectionViewFlowLayoutAutomaticSize.
LinusGeffarth

14
  1. Додати потоковий макет у viewDidLoad ()

    override func viewDidLoad() {
        super.viewDidLoad() 
        if let flowLayout = infoCollection.collectionViewLayout as? UICollectionViewFlowLayout {
            flowLayout.estimatedItemSize = CGSize(width: 1, height:1)
        }
    }
  2. Крім того, встановіть для свого стільника UIView як mainContainer та додайте все необхідні перегляди всередині нього.

  3. Докладнішу інформацію див. У цьому дивовижному, вражаючому навчальному посібнику: UICollectionView з автоматичною розмірною коміркою за допомогою автоматичного вимикання в iOS 9 та 10


11

EDIT 19.11.19: Для iOS 13 просто використовуйте UICollectionViewCompositionalLayout із розрахунковими висотами. Не витрачайте час на розбиття цього зламаного API.

Протягом деякого часу, працюючи з цим, я помітив, що зміна розміру не працює для UITextViews, якщо ви не відключите прокрутку:

let textView = UITextView()
textView.scrollEnabled = false

коли я намагаюся прокручувати, клітинка перегляду колекції не є рівною. Чи є у вас рішення для цього?
Сатіш Кумар Гурунатан

6

contentView таємниця якоря:

В одному химерному випадку це

    contentView.translatesAutoresizingMaskIntoConstraints = false

не працювало б. До ContentView додано чотири явні прив’язки, і це спрацювало.

class AnnoyingCell: UICollectionViewCell {
    
    @IBOutlet var word: UILabel!
    
    override init(frame: CGRect) {
        super.init(frame: frame); common() }
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder); common() }
    
    private func common() {
        contentView.translatesAutoresizingMaskIntoConstraints = false
        
        NSLayoutConstraint.activate([
            contentView.leftAnchor.constraint(equalTo: leftAnchor),
            contentView.rightAnchor.constraint(equalTo: rightAnchor),
            contentView.topAnchor.constraint(equalTo: topAnchor),
            contentView.bottomAnchor.constraint(equalTo: bottomAnchor)
        ])
    }
}

і як завжди

    estimatedItemSize = UICollectionViewFlowLayout.automaticSize

в YourLayout: UICollectionViewFlowLayout

Хто знає? Може допомогти комусь.

Кредит

https://www.vadimbulavin.com/collection-view-cells-self-sizing/

наткнувся на кінчик там - ніколи не бачив його ніде в усіх статтях з 1000-х років про це.


3

Я зробив динамічну висоту осередків у вигляді колекції. Ось git hub repo .

І з’ясуйте, чому переважніLayoutAttributesFittingAttributes називаються не один раз. Власне, його будуть називати щонайменше 3 рази.

Зображення журналу консолі: введіть тут опис зображення

1-й кращийLayoutAttributesFittingAttributes :

(lldb) po layoutAttributes
<UICollectionViewLayoutAttributes: 0x7fa405c290e0> index path: (<NSIndexPath:    0xc000000000000016> 
{length = 2, path = 0 - 0}); frame = (15 12; 384 57.5); 

(lldb) po self.collectionView
<UICollectionView: 0x7fa40606c800; frame = (0 57.6667; 384 0);

LayoutAttributes.frame.size.height є поточним статусом 57.5 .

2-й кращийLayoutAttributesFittingAttributes :

(lldb) po layoutAttributes
<UICollectionViewLayoutAttributes: 0x7fa405c16370> index path: (<NSIndexPath: 0xc000000000000016> 
{length = 2, path = 0 - 0}); frame = (15 12; 384 534.5); 

(lldb) po self.collectionView
<UICollectionView: 0x7fa40606c800; frame = (0 57.6667; 384 0);

Висота кадру комірки змінилася до 534,5, як ми очікували. Але, вигляд колекції все ще дорівнює нульовій висоті.

3-й кращийLayoutAttributesFittingAttributes :

(lldb) po layoutAttributes
<UICollectionViewLayoutAttributes: 0x7fa403d516a0> index path: (<NSIndexPath: 0xc000000000000016> 
{length = 2, path = 0 - 0}); frame = (15 12; 384 534.5); 

(lldb) po self.collectionView
<UICollectionView: 0x7fa40606c800; frame = (0 57.6667; 384 477);

Ви можете бачити, що висота перегляду колекції була змінена з 0 на 477 .

Поведінка схожа на ручку прокрутки:

1. Before self-sizing cell

2. Validated self-sizing cell again after other cells recalculated.

3. Did changed self-sizing cell

На початку я думав, що цей метод викликає лише один раз. Тому я кодував наступне:

CGRect frame = layoutAttributes.frame;
frame.size.height = frame.size.height + self.collectionView.contentSize.height;
UICollectionViewLayoutAttributes* newAttributes = [layoutAttributes copy];
newAttributes.frame = frame;
return newAttributes;

Цей рядок:

frame.size.height = frame.size.height + self.collectionView.contentSize.height;

викличе нескінченний цикл виклику системи та збій програми.

Будь-який розмір зміниться, він буде перевіряти всі віддані клітині 'LayoutAttributesFittingAttributes знову і знову, поки позиції кожної комірки (тобто кадри) більше не змінюються.


2

Окрім наведених вище відповідей,

Просто переконайтеся , що ви встановили estimatedItemSize властивість UICollectionViewFlowLayout деякого розмір і НЕ здійснювати sizeForItem: atIndexPath метод делегата.

Це воно.


1

Якщо ви реалізуєте метод UICollectionViewDelegateFlowLayout:

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

Коли ви телефонуєте collectionview performBatchUpdates:completion:, sizeForItemAtIndexPathзамість цього буде використовуватися висота розміру preferredLayoutAttributesFittingAttributes.

Процес візуалізації performBatchUpdates:completionбуде проходити через метод, preferredLayoutAttributesFittingAttributesале він ігнорує ваші зміни.


Я бачив це неправильне поведінку, але лише тоді, коли орієнтовний розмір дорівнює нулю. Ви впевнені, що встановлюєте орієнтовний розмір?
dgatwood

1

Кому це може допомогти,

У мене був той жахливий збій, якщо він estimatedItemSizeбув встановлений. Навіть якщо я повернув 0 дюймів numberOfItemsInSection. Тому самі комірки та їх автоматична компонування не були причиною збоїв ... КолекціяView просто estimatedItemSizeвийшла з ладу, навіть коли вона порожня, лише тому, що була встановлена ​​для самостійного розміру.

У моєму випадку я реорганізував свій проект - з контролера, що містить collectionView, до collectionViewController, і він працював.

Піди розберися.


1
Спробуйте зателефонувати collectionView.collectionViewLayout.invalidateLayout()після collectionView.reloadData().
chengsam

для ios10 та нижче додайте цей код `override func viewWillLayoutSubviews () {super.viewWillLayoutSubviews () якщо #available (iOS 11.0, *) {} else {mainCollectionView.collectionViewLayout.invalidateLayout ()}}`
Marosdee Uma,

1

Для тих, хто спробував усе без удачі, це єдине, що працювало для мене. Для багаторядкових міток всередині комірки спробуйте додати цю магічну лінію:

label.preferredMaxLayoutWidth = 200

Більше інформації: тут

Ура!


0

Наведений вище приклад не компілюється. Ось виправлена ​​версія (але не перевірена, працює вона чи ні.)

override func preferredLayoutAttributesFittingAttributes(layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes 
{
    let attr: UICollectionViewLayoutAttributes = layoutAttributes.copy() as! UICollectionViewLayoutAttributes

    var newFrame = attr.frame
    self.frame = newFrame

    self.setNeedsLayout()
    self.layoutIfNeeded()

    let desiredHeight: CGFloat = self.contentView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height
    newFrame.size.height = desiredHeight
    attr.frame = newFrame
    return attr
}

0

Оновіть додаткову інформацію:

  • Якщо ви використовуєте flowLayout.estimatedItemSize, запропонуйте використовувати iOS8.3 пізнішої версії. Перед iOS8.3 він вийде з ладу [super layoutAttributesForElementsInRect:rect];. Повідомлення про помилку є

    *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSArrayM insertObject:atIndex:]: object cannot be nil'

  • По-друге, у версії iOS8.x, це flowLayout.estimatedItemSizeпризведе до того, що різні параметри вставки розділу не спрацювали. тобто функція: (UIEdgeInsets)collectionView:layout:insetForSectionAtIndex:.


0

Рішення включає 4 важливі етапи:

  1. Увімкнення динамічного розміру комірок

flowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize

  1. Явно увімкніть автоматичне розміщення та прикріпіть contentViewдо країв комірки, оскільки contentViewзапобігає самостійному розміру, враховуючи, що в ньому не ввімкнено автоматичний макет.
class MultiLineCell: UICollectionViewCell{

    private var textViewHeightContraint: NSLayoutConstraint!

    override init(frame: CGRect) {
        super.init(frame: frame)
        backgroundColor = .cyan
        contentView.translatesAutoresizingMaskIntoConstraints = false
        contentViewWidthAnchor = contentView.widthAnchor.constraint(equalToConstant: 0)

        NSLayoutConstraint.activate([
            contentView.leadingAnchor.constraint(equalTo: leadingAnchor),
            contentView.topAnchor.constraint(equalTo: topAnchor),
            contentView.trailingAnchor.constraint(equalTo: trailingAnchor),
            contentView.bottomAnchor.constraint(equalTo: bottomAnchor),
        ])
    } 
    ...
}
  1. Встановіть contentView.widthAnchor.constraint з, collectionView(:cellForItemAt:)щоб обмежити ширину contentView шириною collectionView.

Це відбувається наступним чином; ми встановимо осередок maxWidthзмінної ширину CollectionView за адресою collectionView(:cellForItemAt:)і в maxWidth«S didSetметоду ми встановимо widthAnchor.constant в MaxWidth.

class ViewController: UIViewController, UICollectionViewDataSource {
    ...

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellId", for: indexPath) as! MultiLineCell
        cell.textView.text = dummyTextMessages[indexPath.row]
        cell.maxWidth = collectionView.frame.width
        return cell
    }

    ...
}
class MultiLineCell: UICollectionViewCell{
    ....

    var maxWidth: CGFloat? {
        didSet {
            guard let maxWidth = maxWidth else {
                return
            }
            contentViewWidthAnchor.constant = maxWidth
            contentViewWidthAnchor.isActive = true
        }
    }

    ....
}

Оскільки ви хочете ввімкнути саморозмір UITextView, він має додатковий крок;

4. Обчисліть та встановіть висотуAnchor.constant для UITextView.

Отже, щоразу, коли встановлюється ширина contentView, ми будемо коригувати висоту UITextView разом didSetз maxWidth.

Всередині UICollectionViewCell:

var maxWidth: CGFloat? {
    didSet {
        guard let maxWidth = maxWidth else {
            return
        }
        contentViewWidthAnchor.constant = maxWidth
        contentViewWidthAnchor.isActive = true

        let sizeToFitIn = CGSize(width: maxWidth, height: CGFloat(MAXFLOAT))
        let newSize = self.textView.sizeThatFits(sizeToFitIn)
        self.textViewHeightContraint.constant = newSize.height
    }
}

Ці кроки отримають бажаний результат. Ось повний суть прогону

Дякуємо Вадиму Булавіну за його чітке та приємне повідомлення в блозі - Розмір осередків перегляду розмірів у кліпах: Покроковий посібник


-2

Я спробував використати, estimatedItemSizeале під час вставлення та видалення комірок з'явилася купа помилок, якщо значення estimatedItemSizeне було рівно висоті комірки. Я зупинив налаштування estimatedItemSizeта реалізував динамічні комірки за допомогою комірки-прототипу. ось як це робиться:

створити цей протокол:

protocol SizeableCollectionViewCell {
    func fittedSize(forConstrainedSize size: CGSize)->CGSize
}

реалізуйте цей протокол у своєму користувальницькому режимі UICollectionViewCell:

class YourCustomCollectionViewCell: UICollectionViewCell, SizeableCollectionViewCell {

    @IBOutlet private var mTitle: UILabel!
    @IBOutlet private var mDescription: UILabel!
    @IBOutlet private var mContentView: UIView!
    @IBOutlet private var mTitleTopConstraint: NSLayoutConstraint!
    @IBOutlet private var mDesciptionBottomConstraint: NSLayoutConstraint!

    func fittedSize(forConstrainedSize size: CGSize)->CGSize {

        let fittedSize: CGSize!

        //if height is greatest value, then it's dynamic, so it must be calculated
        if size.height == CGFLoat.greatestFiniteMagnitude {

            var height: CGFloat = 0

            /*now here's where you want to add all the heights up of your views.
              apple provides a method called sizeThatFits(size:), but it's not 
              implemented by default; except for some concrete subclasses such 
              as UILabel, UIButton, etc. search to see if the classes you use implement 
              it. here's how it would be used:
            */
            height += mTitle.sizeThatFits(size).height
            height += mDescription.sizeThatFits(size).height
            height += mCustomView.sizeThatFits(size).height    //you'll have to implement this in your custom view

            //anything that takes up height in the cell has to be included, including top/bottom margin constraints
            height += mTitleTopConstraint.constant
            height += mDescriptionBottomConstraint.constant

            fittedSize = CGSize(width: size.width, height: height)
        }
        //else width is greatest value, if not, you did something wrong
        else {
            //do the same thing that's done for height but with width, remember to include leading/trailing margins in calculations
        }

        return fittedSize
    }
}

тепер переконайтеся, що ваш контролер відповідає UICollectionViewDelegateFlowLayout, і в ньому є це поле:

class YourViewController: UIViewController, UICollectionViewDelegateFlowLayout {
    private var mCustomCellPrototype = UINib(nibName: <name of the nib file for your custom collectionviewcell>, bundle: nil).instantiate(withOwner: nil, options: nil).first as! SizeableCollectionViewCell
}

він буде використовуватися в якості комірки-прототипу для прив'язки даних, а потім визначення того, як ці дані вплинули на той вимір, який ви хочете бути динамічним

нарешті, UICollectionViewDelegateFlowLayout's collectionView(:layout:sizeForItemAt:)необхідно здійснити:

class YourViewController: UIViewController, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {

    private var mDataSource: [CustomModel]

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath)->CGSize {

        //bind the prototype cell with the data that corresponds to this index path
        mCustomCellPrototype.bind(model: mDataSource[indexPath.row])    //this is the same method you would use to reconfigure the cells that you dequeue in collectionView(:cellForItemAt:). i'm calling it bind

        //define the dimension you want constrained
        let width = UIScreen.main.bounds.size.width - 20    //the width you want your cells to be
        let height = CGFloat.greatestFiniteMagnitude    //height has the greatest finite magnitude, so in this code, that means it will be dynamic
        let constrainedSize = CGSize(width: width, height: height)

        //determine the size the cell will be given this data and return it
        return mCustomCellPrototype.fittedSize(forConstrainedSize: constrainedSize)
    }
}

і це все. Таким чином, повертаючи розмір комірки, collectionView(:layout:sizeForItemAt:)заважаючи мені використовувати estimatedItemSize, а також вставляти та видаляти комірки працює чудово.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.