ios swift uitableview uicollectionview

ios 8 Swift-TableView con CollectionView integrado



uitableview uicollectionview (3)

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

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

He encontrado 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 rápido o puede ayudar? Estoy en el proceso de crear un proyecto / código simple que publicaré en breve para intentar ayudar.

EDITAR 1

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

https://github.com/DahanHu/DHCollectionTableView

Muchas gracias Rob


Detalles

  • Xcode 10.2.1 (10E1001), Swift 5

Muestra completa

import UIKit class ViewController: UIViewController { @IBOutlet weak var tableView: UITableView! fileprivate var tableViewCellCoordinator: [Int: IndexPath] = [:] 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.selectionStyle = .none cell.collectionView.delegate = self cell.collectionView.dataSource = self let tag = tableViewCellCoordinator.count cell.collectionView.tag = tag tableViewCellCoordinator[tag] = indexPath 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 var text = "" if let indexPathOfCellInTableView = tableViewCellCoordinator[collectionView.tag] { text = "/(indexPathOfCellInTableView)" } cell.label.text = text + " /(indexPath)" return cell } } // UICollectionViewDelegate extension ViewController: UICollectionViewDelegate { func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { print("selected collectionViewCell with indexPath: /(indexPath) in tableViewCell with indexPath: /(tableViewCellCoordinator[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"?> <document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11762" systemVersion="16D32" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="BYZ-38-t0r"> <device id="retina4_7" orientation="portrait"> <adaptation id="fullscreen"/> </device> <dependencies> <deployment identifier="iOS"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/> <capability name="Constraints to layout margins" minToolsVersion="6.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> </dependencies> <scenes> <!--View Controller--> <scene sceneID="tne-QT-ifu"> <objects> <viewController id="BYZ-38-t0r" customClass="ViewController" customModule="_31582378" customModuleProvider="target" sceneMemberID="viewController"> <layoutGuides> <viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/> <viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/> </layoutGuides> <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC"> <rect key="frame" x="0.0" y="0.0" width="375" height="667"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <subviews> <tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="200" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="pS5-CW-ipl"> <rect key="frame" x="0.0" y="20" width="375" height="647"/> <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/> <prototypes> <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="CollectionViewTableViewCell" id="bMP-Ac-C8D" customClass="CollectionViewTableViewCell" customModule="_31582378" customModuleProvider="target"> <rect key="frame" x="0.0" y="28" width="375" height="200"/> <autoresizingMask key="autoresizingMask"/> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="bMP-Ac-C8D" id="mcy-FO-bcc"> <rect key="frame" x="0.0" y="0.0" width="375" height="199"/> <autoresizingMask key="autoresizingMask"/> <subviews> <collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="prototypes" translatesAutoresizingMaskIntoConstraints="NO" id="yY4-ue-1HX"> <rect key="frame" x="8" y="8" width="359" height="183"/> <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/> <collectionViewFlowLayout key="collectionViewLayout" scrollDirection="horizontal" minimumLineSpacing="10" minimumInteritemSpacing="10" id="pPl-9q-MGc"> <size key="itemSize" width="180" height="180"/> <size key="headerReferenceSize" width="0.0" height="0.0"/> <size key="footerReferenceSize" width="0.0" height="0.0"/> <inset key="sectionInset" minX="10" minY="0.0" maxX="10" maxY="0.0"/> </collectionViewFlowLayout> <cells> <collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="CollectionViewCell" id="g9z-R1-8XJ" customClass="CollectionViewCell" customModule="_31582378" customModuleProvider="target"> <rect key="frame" x="10" y="2" width="180" height="180"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center"> <rect key="frame" x="0.0" y="0.0" width="180" height="180"/> <autoresizingMask key="autoresizingMask"/> <subviews> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="rHM-Xn-vBW"> <rect key="frame" x="69" y="80" width="42" height="21"/> <fontDescription key="fontDescription" type="system" pointSize="17"/> <color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/> <nil key="highlightedColor"/> </label> </subviews> </view> <color key="backgroundColor" red="0.28627450980000002" green="0.56470588239999997" blue="0.8862745098" alpha="1" colorSpace="calibratedRGB"/> <constraints> <constraint firstItem="rHM-Xn-vBW" firstAttribute="centerX" secondItem="g9z-R1-8XJ" secondAttribute="centerX" id="AXf-f9-ruf"/> <constraint firstItem="rHM-Xn-vBW" firstAttribute="centerY" secondItem="g9z-R1-8XJ" secondAttribute="centerY" id="gw4-Iv-7ML"/> </constraints> <size key="customSize" width="180" height="180"/> <connections> <outlet property="label" destination="rHM-Xn-vBW" id="9SL-Kv-ZtD"/> </connections> </collectionViewCell> </cells> </collectionView> </subviews> <constraints> <constraint firstItem="yY4-ue-1HX" firstAttribute="bottom" secondItem="mcy-FO-bcc" secondAttribute="bottomMargin" id="04L-lF-Idy"/> <constraint firstItem="yY4-ue-1HX" firstAttribute="leading" secondItem="mcy-FO-bcc" secondAttribute="leadingMargin" id="Fjd-8j-qvK"/> <constraint firstItem="yY4-ue-1HX" firstAttribute="trailing" secondItem="mcy-FO-bcc" secondAttribute="trailingMargin" id="PUa-ze-U5s"/> <constraint firstItem="yY4-ue-1HX" firstAttribute="top" secondItem="mcy-FO-bcc" secondAttribute="topMargin" id="XX6-d1-Vgx"/> </constraints> </tableViewCellContentView> <connections> <outlet property="collectionView" destination="yY4-ue-1HX" id="tLL-Om-JIX"/> <outlet property="collectionViewFlowLayout" destination="pPl-9q-MGc" id="Ftw-AT-QvP"/> </connections> </tableViewCell> </prototypes> </tableView> </subviews> <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <constraints> <constraint firstItem="pS5-CW-ipl" firstAttribute="leading" secondItem="8bC-Xf-vdC" secondAttribute="leading" id="3vT-w2-JGU"/> <constraint firstItem="pS5-CW-ipl" firstAttribute="top" secondItem="y3c-jy-aDJ" secondAttribute="bottom" id="eS2-Y5-fxg"/> <constraint firstItem="pS5-CW-ipl" firstAttribute="bottom" secondItem="wfy-db-euE" secondAttribute="top" id="hFA-oB-bWJ"/> <constraint firstAttribute="trailing" secondItem="pS5-CW-ipl" secondAttribute="trailing" id="yin-cp-cAP"/> </constraints> </view> <connections> <outlet property="tableView" destination="pS5-CW-ipl" id="Gfe-HE-Ub6"/> </connections> </viewController> <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/> </objects> <point key="canvasLocation" x="136.80000000000001" y="137.18140929535232"/> </scene> </scenes> </document>

Resultados


Cree un UITableView habitual y en su UITableViewCell cree el UICollectionView. Su delegado y fuente de datos collectionView deben cumplir con ese UITableViewCell.

Solo pasa por esto

En tu 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.

El "TableViewCell" personalizado tendrá nuestra UICollectionView. Por lo tanto, cree una subclase UITableViewCell y escriba el código a continuación.

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.


Piense en MVC y la incrustación nunca antes mediante programación como esta. Una subclase de UIView (-> eso es realmente una celda) nunca es la clase delegada para un delegado y fuente de datos de un tableView (lo mismo para collectionView).

¿Alguna vez creaste una subclase de tableview para hacer todas las cosas programáticas en esta subclase? ¡No! lo hace en su controlador de vista, porque crea UIView en Controladores, para "controlarlos". Entonces, la forma correcta de hacerlo es:

Déjame darte un ejemplo (en la forma de "mejor comprensión" de la vieja escuela):

  1. Cree un ViewController con tableView y agregue (addSubview) esta collectionView a su UITableViewCell.
  2. Tiene un ViewController en Storyboard y también se integra 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 "con contenido" (con restricciones). Más tarde usará esta vista para agregar una vista de colección como una 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. (haremos el primero para una mejor comprensión)

  6. El nuevo ViewController solo se maneja como un ViewController con un CollectionView (igual que 1. pero collectionView)
  7. En este nuevo ViewController con collectionView manejas 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 soporte de 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, verifique siempre si contentHolderView ya tiene un

    myViewControllerWithCollectionView.collectionView

para recuperar acciones de la vista de colección, agregue un protocolo personalizado (delegado) a su vista para obtener más información. Nunca establezca delegado y fuente de datos de su collectionView a su tableViewController principal, solo deje que maneje todo en el viewcontroller correcto y envíe la información a cualquier otro viewcontroller si es necesario.