UICollectionView (и UITableView) оптимизирует память, создавая пул ячеек, которые используются повторно.Он извлекает количество ячеек, которое необходимо отобразить, из пула с учетом изменения положения прокрутки и / или доступного пространства (путем изменения его размера).Под «показом» я не обязательно подразумеваю видимое.В вашем случае collectionView может не отображаться, если он встроен в UIScrollView и размещен ниже границ экрана.Ячейка отображается, когда collectionView.bounds содержит «cell.frame» (даже частично).Я процитировал «cell.frame», потому что речь идет не о экземпляре ячейки, а о внутреннем механизме UICollectionView для отслеживания фрейма каждой ячейки.
Что вы делаете, так это предоставляете вашему collectionView достаточно места(регулируя его высоту), чтобы отобразить весь контент, который вы хотите отобразить.Так что никакой оптимизации там не делается.
Я могу представить три варианта:
1) Вместо встраивания UICollectionView в UIScrollView лучше использовать только один UICollectionView.Затем сделайте ваш контент с точки зрения ячеек.Это содержание выше вашего collectionView?Сделайте это клеткой (или связкой ячеек).Заставьте его жить в своем собственном разделе со своими вставками и прочим.При реализации этой опции я обычно определяю перечисление с регистрами, являющимися необходимыми типами контента.
enum ContentType {
case header, cell(Any)
}
var data: [ContentType] = [...]
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
switch data[indexPath.row] {
case .header:
return headerDataSource.cell // Some object you can obtain the cell from
case .cell(value):
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath)
cell.fill(value)
return cell
}
}
2) Этот параметр является личным предпочтением с точки зрения дизайна.Я предлагаю вам установить ограничение на количество ячеек, которые отображает ваш collectionView, отключить прокрутку collectionView, установить высоту collectionView равным contentSize.height и добавить ячейку «More», которая отправляет пользователя в UICollectionViewController, который отображает остальную часть содержимого, и использовать преимуществаоптимизации.Так как мы ограничиваем количество отображаемых ячеек, это не повлияет на память, если ограничение не установлено слишком высоким (очевидно).Я реализовал эту опцию один раз, когда мне нужно было отобразить галерею с некоторым содержимым над ней, с кучей других видов ячеек под ней и некоторыми записями обзора внизу (тоже с ячейками), и я не хотел, чтобы пользователь прокручивал,скажем, 1000 галерей, чтобы добраться до отзывов.Также не хотелось, чтобы пользователь снова прокрутил эти 1000 ячеек, чтобы добраться до контента над галереей.Я установил ограничение в 8 ячеек галереи плюс ячейка «Еще», чтобы создать сетку 3х3, которая выглядит красиво и просто.
3) Я думаю, что эта излишняя.Не стоит, когда вы получили вариант 1. Вы можете реализовать scrollViewDidScroll :, рассчитать фрейм встроенного collectionView (в терминах системы координат представления контроллера представления), используя convert (_: to :) и отрегулировать ограничение по высоте для collectionView, чтобы оно было дельтой scrollView.высота фрейма минус collectionView ранее вычисленного minY фрейма и гарантирует, что высота collectionView не превышает высоту фрейма scrollView.Когда это происходит, нужно добавить дополнительную дельту к границам collectionView, чтобы прокрутить ее программно.Есть еще много соображений, которые я не смог решить, и у меня не было никакого интереса сделать это, учитывая, что я могу реализовать вариант 1.
// This code is not working in an acceptable way but I think is a reasonable starting point.
// Also, when bouncing is disabled, a scrollView doesn't call this method when trying to scroll past the edges.
extension ViewController: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
guard !(scrollView is UICollectionView) else {return}
let height = view.bounds.height
let minY = collectionView.convert(collectionView.bounds, to: view).minY
let delta = height - minY
constraint_collection_height.constant = delta
if delta > height {
constraint_collection_height.constant = height
let offset = collectionView.convert(CGPoint.zero, to: scrollView).y
let scroll = scrollView.bounds.minY - offset
let bounds = CGRect(origin: CGPoint(x: 0, y: scroll), size: collectionView.bounds.size)
collectionView.bounds = bounds
}
}
}
Редактировать: - Пример ответа на комментарий.
Вместо встраивания collectionView в ячейку для управления другим типом макета.Нужно управлять новым разделом и определить новый макет там.
enum ContentType {
case header, cell(Any), image(UIImage)
}
var data_section0: [ContentType] = [...]
var data_section1: [ContentType] = [...]
func retrieve_cell(_ collectionView: UICollectionView, _ data: [ContentType], _ row: Int) -> UICollectionViewCell {
switch data[row] {
case .header:
return headerDataSource.cell // Some object you can obtain the cell from
case .cell(value):
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath)
cell.fill(value)
return cell
case .image(let image):
let cell = ...
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
switch indexPath.section {
case 0: return retrieve_cell(collectionView, data_section0, indexPath.row)
case 1: return retrieve_cell(collectionView, data_section1, indexPath.row)
default: fatalError("There' shouldn't be a 3rd section")
}
}
func numberOfSections(in collectionView: UICollectionView) -> Int {return 2}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
switch section {
case 0: return data_section0.count
case 1: return data_section1.count
default: fatalError("There' shouldn't be a 3rd section")
}
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
switch indexPath.section {
case 0: return collectionView.bounds.width
case 1: return collectionView.bounds.width / 2
default: fatalError("There' shouldn't be a 3rd section")
}
}