Решение проблемы с передачей данных из коллекции в ячейку

uicollectionviewcell
uitableview
ios
uicollectionview

#1

Всем доброго времени суток!
Для кого эта статья?

Люди у которых коллекция передает только несколько первых данных, а потом циклично их использует.

При создании своего приложения, столкнулся с трудностью передачи информации, c использованием Reusable ячеек коллекции.

Приложение содержит коллекцию ячеек, внутри которых отображаются таблицы с информацией.

Т.к. у нас на одном экране 2 объекта с Reusable ячейками, появляется трудность передачи информации.

Начало будет кратким:

  1. Создаем проект
  2. Для удобства выбираем View as: Iphone 8+ (я выбрал потому что он на руках и подобрал под него размеры ячейки)
  3. На главном контроллере создаем коллекцию
  4. В size inspector коллекции : Cell size(334:543), Min Spacing(10:80), Sections insets (40:40:40:40)

  1. Появится вот такая ячейка

  1. Добавляем таблицу из библиотеки и растягиваем ее

6.1. перетаскиваем (создаем) ячейку таблицы, задаем indentifier “Cell”, style - Basic.

С Сторибордом закончили, переходим к коду

  1. ViewController в конечном итоге он будет выглядеть так:

     import UIKit
    
     class ViewController: UIViewController {
     
     //Массив для передачи данных в таблицу
     let arr = [["table1","table1","table1","table1","table1","table1"],
                ["table2","table2","table2","table2","table2","table2"],
                ["table3","table3","table3","table3","table3","table3"],
                ["table4","table4","table4","table4","table4","table4"],
                ["table5","table5","table5","table5","table5","table5"],
                ["table6","table6","table6","table6","table6","table6"]]
     
     @IBOutlet weak var collectionView: UICollectionView!
     
     override func viewDidLoad() {
         super.viewDidLoad()
         //Подписываемся под делегат и датаСорс
         collectionView.delegate = self //если будете кастомить сами
         collectionView.dataSource = self
         
         //убираем индикаторы положения в коллекции от скрола
         collectionView.showsVerticalScrollIndicator = false
         collectionView.showsHorizontalScrollIndicator = false
         
         //Включаем страничную прокрутку
         collectionView.isPagingEnabled = true
         
         //Специальная переменная ниже опишу для чего она нужна
         collectionView.isPrefetchingEnabled = false
         
       }
    }
    
     //подписываемся под делегат коллекции
     extension ViewController: UICollectionViewDelegate {
         //кастомим если нужно
    
     }
    
     //и под датаСорс
     extension ViewController: UICollectionViewDataSource{
    
         func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
                 return arr.count
         }
    
         func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
         
         let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! CollectionViewCell
         
         //массив с данными, которые нам нужно передать, присваиваем в специально созданный массив из класса ячейки
         cell.dataArr = self.arr[indexPath.row]
         
         //обновляем таблицу (на всякий случай)
         cell.TableView.reloadData()
         
         return cell
         }
     }
    
  2. Создаем Cocoa touch class и подписываем его под UICollectionViewCell. Сам код с коментами:

    Тут могут быть проблемы с присваиванием таблицы к классу ячейки. Выбираем в ассистент эдиторе не авто определение, а работам ручками. Так же при работе в коде, возникает проблема, что Свифт не дает подписать делегат и датасорс таблицы к ячейке. для этого сначала пишем TableView.delegate = self, TableView.dataSource = self через as!, и только потом подписываем расширения.

     import UIKit
    
     class CollectionViewCell: UICollectionViewCell {
     
     @IBOutlet weak var TableView: UITableView!
     
     //в этот массив будут передаваться по одному массиву каждый раз когда мы перелистываем ячейку
     var dataArr = [String]()
     
     override func awakeFromNib() {
         super.awakeFromNib()
         // подписываем делегат и датаСорс
         TableView.delegate = self
         TableView.dataSource = self
         
         DispatchQueue.main.async {
             //наводим красоту по желанию
             self.TableView.layer.cornerRadius = 13.0
             self.TableView.layer.borderWidth = 5
             self.TableView.layer.shadowColor = UIColor.darkGray.cgColor
             self.TableView.layer.shadowOpacity = 0.5
             self.TableView.layer.shadowOpacity = 10.0
             self.TableView.layer.shadowOffset = .zero
             self.TableView.layer.shadowPath = UIBezierPath(rect: self.TableView.bounds).cgPath
             self.TableView.layer.shouldRasterize = true
             }
         }
     }
    
     //делегат таблицы
     extension CollectionViewCell: UITableViewDelegate {
    
     //задаем высоту ячейки таблицы
     func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
         return tableView.frame.height / CGFloat(dataArr.count)
         }
     }
    
     //Датасорс таблицы
     extension CollectionViewCell: UITableViewDataSource {
     
     func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
         return dataArr.count
     }
     
     func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
         
         let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
    
         //присваиваем полученый массив к каждому лейблу ячейки таблицы
          cell.textLabel?.text = "I'm \(dataArr[indexPath.row])"
         
         return cell
         }
     }
    
  3. Запускаем.

написано без применения каких либо паттернов. простой пример, не терзайте в коментах об говноКоде… сами переделывайте если не нравится.

Объясняю зачем я создал этот топик.

При создании, вроде бы, такого легкого экрана, возникли проблемы:

  • Как создать страничную коллекцию типа iCarousel - ответ collectionView.isPagingEnabled = true (если нужно чтобы ячейки изменялись при этом в размере - гуглите кастомизацию flowLayot коллекции и добавляйте в проект, много на эту тему есть статей на en-нете

  • Из-за того, что я ячейки пересоздаваемые, на самом деле на экране их 3(видимая, слева, справа, дабы заранее подгрузить данные без грузов), а не одна, и при перелистывании больше 3х ячеек не передавали данные… Если свайпать ячейку вправо серединная ячейка не исчезает, как таковая, она же и выходит слева. по этому коллекция передавала сразу 3 массива по indexPath.row. Решение этой проблемы пришло откуда не ждал.

    Свойство collectionView.isPrefetchingEnabled = false - изменяет метод поведения коллекции, а точнее подгрузки ячеек. после выставления false коллекция начинает загружать ячейку только при начале свайпа. Этим самым изменяет и передачу данных из коллекции в ячейку. Теперь данные передаются один за другим.

Но нужно помнить что асинхронность так же важна и для улучшения работоспособности Вашего приложения Apple советует (или требует, я так и не понял) использовать

Всем спасибо. Особенно спасибо Qonflictorz и swiftBook каналу в тг. Моя первая статья, сильно не ругайте))


Передача инфирмации из коллекции в ячейку. Решено)
Пролистывание расписания