ios 8 Swift – TableView con CollectionView incrustado

Soy relativamente nuevo en la progtwigción de iOS y he intentado algunas cosas pero fue en vano.

Me gustaría poner un CollectionView dentro de TableViewCell . Puedo codificar cada uno de forma individual, pero no entiendo cómo establecer y hacer referencia a cada CollectionView dentro de un TableViewCell .

Encontré este tutorial, http://ashfurrow.com/blog/putting-a-uicollectionview-in-a-uitableviewcell/ , que muestra cómo se puede hacer en Objective-C, pero siempre he tenido problemas con Obj-C.

¿Alguien sabe de un tutorial Swift o puede ayudar? Estoy en el proceso de crear un proyecto / código simple que publicaré en breve para intentar ayudar.

EDIT 1

Acabo de encontrar la versión rápida del enlace de arriba. Estoy trabajando en esto ahora, pero parece demasiado complicado, modificando AppDelegate.

https://github.com/DahanHu/DHCollectionTableView

Muchas gracias Rob

Cree una UITableView habitual y en su UITableViewCell cree UICollectionView. Su delegado y origen de datos de collectionView deben cumplir con esa UITableViewCell.

Solo revisa esto

En su ViewController

 // Global Variable var tableView: UITableView! override func viewDidLoad() { super.viewDidLoad() tableView = UITableView(frame: self.view.bounds) tableView.delegate = self tableView.dataSource = self self.view.addSubview(tableView) tableView.registerClass(TableViewCell.self, forCellReuseIdentifier: "TableViewCell") tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "NormalCell") } func numberOfSectionsInTableView(tableView: UITableView) -> Int { return 1 } func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 5 } func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { if indexPath.row == 3 { var cell: TableViewCell = tableView.dequeueReusableCellWithIdentifier("TableViewCell", forIndexPath: indexPath) as! TableViewCell cell.backgroundColor = UIColor.groupTableViewBackgroundColor() return cell } else { var cell: UITableViewCell = tableView.dequeueReusableCellWithIdentifier("NormalCell", forIndexPath: indexPath) as! UITableViewCell cell.textLabel?.text = "cell: \(indexPath.row)" return cell } } 

Como puede ver, he creado dos celdas diferentes, una TableViewCell personalizada que se devuelve solo cuando el índice de fila es 3 y una UITableViewCell básica en otros índices.

La “TableViewCell” personalizada tendrá nuestro UICollectionView. Cree una subclase UITableViewCell y anote el código siguiente.

 import UIKit class TableViewCell: UITableViewCell, UICollectionViewDataSource, UICollectionViewDelegate { var collectionView: UICollectionView! override init(style: UITableViewCellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) let layout = UICollectionViewFlowLayout() layout.scrollDirection = UICollectionViewScrollDirection.Horizontal collectionView = UICollectionView(frame: self.bounds, collectionViewLayout: layout) collectionView.delegate = self collectionView.dataSource = self collectionView.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: "CollectionViewCell") collectionView.backgroundColor = UIColor.clearColor() self.addSubview(collectionView) } required init(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } // MARK: UICollectionViewDataSource func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int { return 1 } func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return 10 } func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { let cell: UICollectionViewCell = collectionView.dequeueReusableCellWithReuseIdentifier("CollectionViewCell", forIndexPath: indexPath) as! UICollectionViewCell if indexPath.row%2 == 0 { cell.backgroundColor = UIColor.redColor() } else { cell.backgroundColor = UIColor.yellowColor() } return cell } } 

Espero eso ayude.

Detalles

xCode 8.2.1, swift 3

Comienzo rápido

ViewController

 import UIKit class ViewController: UIViewController { @IBOutlet weak var tableView: UITableView! override func viewDidLoad() { super.viewDidLoad() tableView.dataSource = self tableView.tableFooterView = UIView() } } extension ViewController: UITableViewDataSource { func numberOfSections(in tableView: UITableView) -> Int { return 1 } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 1 } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "CollectionViewTableViewCell") as! CollectionViewTableViewCell return cell } } 

CollectionViewTableViewCell

 import UIKit class CollectionViewTableViewCell: UITableViewCell { @IBOutlet weak var collectionView: UICollectionView! @IBOutlet weak var collectionViewFlowLayout: UICollectionViewFlowLayout! override func awakeFromNib() { super.awakeFromNib() collectionView.dataSource = self collectionView.delegate = self } } extension CollectionViewTableViewCell: UICollectionViewDataSource { func numberOfSections(in collectionView: UICollectionView) -> Int { return 1 } func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return 10 } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCell", for: indexPath) as! CollectionViewCell cell.label.text = "\(indexPath)" return cell } } extension CollectionViewTableViewCell: UICollectionViewDelegate { func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { print("collectionViewCell selected \(indexPath)") } } 

CollectionViewCell

 import UIKit class CollectionViewCell: UICollectionViewCell { @IBOutlet weak var label: UILabel! } 

Main.storyboard

 < ?xml version="1.0" encoding="UTF-8"?>                                                                                                     

Resultado

enter image description here

El ejemplo correcto de MVC

ViewController

 import UIKit class ViewController: UIViewController { @IBOutlet weak var tableView: UITableView! fileprivate var tableViewCellCoordinator: [IndexPath: Int] = [:] override func viewDidLoad() { super.viewDidLoad() tableView.dataSource = self tableView.delegate = self tableView.tableFooterView = UIView() } } // UITableViewDataSource extension ViewController: UITableViewDataSource { func numberOfSections(in tableView: UITableView) -> Int { return 5 } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 10 } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "CollectionViewTableViewCell") as! CollectionViewTableViewCell cell.collectionView.delegate = self cell.collectionView.dataSource = self let tag = 10000*(indexPath.section+1)+indexPath.row cell.collectionView.tag = tag tableViewCellCoordinator[indexPath] = tag return cell } func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { return "section: \(section)" } } extension ViewController: UITableViewDelegate { func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { let cell = cell as! CollectionViewTableViewCell cell.collectionView.reloadData() cell.collectionView.contentOffset = .zero } } // UICollectionViewDataSource extension ViewController: UICollectionViewDataSource { func numberOfSections(in collectionView: UICollectionView) -> Int { return 1 } func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return 10 } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCell", for: indexPath) as! CollectionViewCell return cell } } // UICollectionViewDelegate extension ViewController: UICollectionViewDelegate { func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { let cell = cell as! CollectionViewCell cell.label.text = "\(tableViewCellCoordinator.key(forValue: collectionView.tag)!) \(indexPath)" } func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { print("selected collectionViewCell with indexPath: \(indexPath) in tableViewCell with indexPath: \(tableViewCellCoordinator.key(forValue: collectionView.tag)!)") } } 

CollectionViewTableViewCell

 import UIKit class CollectionViewTableViewCell: UITableViewCell { @IBOutlet weak var collectionView: UICollectionView! @IBOutlet weak var collectionViewFlowLayout: UICollectionViewFlowLayout! } 

CollectionViewCell

 import UIKit class CollectionViewCell: UICollectionViewCell { @IBOutlet weak var label: UILabel! } 

Main.storyboard

 < ?xml version="1.0" encoding="UTF-8"?>                                                                                                     

Resultados

enter image description here enter image description here

Piensa en MVC e incrustación nunca de forma programática como esta arriba. Una subclase de UIView (-> eso es lo que realmente es una celda) nunca es la clase de delegado para un delegado y un origen de datos de un tableView (lo mismo para collectionView).

¿Alguna vez creó una subclase de tabla vista para hacer todo el material programáticamente en esta subclase? ¡No! lo haces en tu viewcontroller porque creas UIView en Controladores para “controlarlos”. Entonces, la forma correcta de hacerlo es:

Déjame darte un ejemplo (en la forma de “entender mejor” de la vieja escuela):

  1. Cree un ViewController con un tableView y añada (addSubview) este collectionView a su UITableViewCell.
  2. Usted tiene un ViewController en Storyboard y también está incorporado un UITableView
  3. También está conectado con su clase ViewController como un Outlet (y también su delegado y fuente de datos)
  4. En lugar de agregar un CollectionView ahora en su UITableViewCell personalizado, simplemente agregue un UIView “titular de contenido” (con restricciones). Más tarde usará esta vista para agregar una vista de colección como subvista.

  5. Ahora cree un nuevo UIViewController (Nuevo archivo> …) con XIB O arrastre y suelte un nuevo UIViewController desde el panel de propiedades en su guión gráfico y cree también una clase UIViewController. No te olvides de conectarte entre ellos. (haremos el primero para una mejor comprensión)

  6. El nuevo ViewController simplemente maneja como un ViewController con un CollectionView (igual que 1. pero collectionView)
  7. En este nuevo ViewController con collectionView maneja todo como de costumbre, con delegado y fuente de datos, etc.

  8. AHORA : en el ViewController (primero) con tableView crea una instancia del nuevo ViewController (con collectionView) en cada celda (cellForRowAtIndexPath), y agrega su collectionView como una subvista a la Vista actual que creó (como en el titular del contenido), por ejemplo:

deje myViewControllerWithCollectionView = MyViewControllerWithCollectionView () myCell.contentHolderView.addSubview (myViewControllerWithCollectionView.collectionView)

Lo que también puede hacer (y tal vez la forma más nueva y mejor, nunca lo intenté pero estoy seguro de que funcionará muy bien, es: UIContainerView).

¡Eso es! algunos consejos para ti:

  • tenga cuidado al agregar una nueva subvista en cellForRowAtIndexPath, compruebe siempre si el contentHolderView ya tiene una

    myViewControllerWithCollectionView.collectionView

para recuperar Acciones de collectionView, a su vista actual, agregue un protocolo personalizado (delegado) a su vista, para obtener más información. Nunca establezca el delegado y el origen de datos de su collectionView en su tableViewViewController principal, solo deje manejar todo en el controlador de vista derecho e inserte información en cualquier otro controlador de vista si es necesario.