Je suis relativement nouveau dans la programmation iOS et j'ai essayé quelques choses mais en vain.
Je voudrais mettre un CollectionView
à l'intérieur d'un TableViewCell
. Je peux coder chacun individuellement, mais je ne comprends pas comment définir et référencer chaque CollectionView
dans un TableViewCell
.
J'ai trouvé ce tutoriel, http://ashfurrow.com/blog/putting-a-uicollectionview-in-a-uitableviewcell/ qui montre comment cela peut être fait dans Objective-C mais j'ai toujours lutté avec Obj-C.
Est-ce que quelqu'un connaît un tutoriel Swift ou peut aider? Je suis en train de créer un projet/code simple que je publierai sous peu pour essayer d'aider.
EDIT 1
Je viens de trouver la version Swift du lien ci-dessus. J'y travaille maintenant, mais semble trop compliqué, modifiant l'AppDelegate.
https://github.com/DahanHu/DHCollectionTableView
Merci beaucoup Rob
Créez un UITableView habituel et dans votre UITableViewCell créez l'UICollectionView. Votre délégué collectionView et votre source de données doivent être conformes à cette UITableViewCell.
Passez par là
Dans votre 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
}
}
Comme vous pouvez le voir, j'ai créé deux cellules différentes, une TableViewCell personnalisée qui n'est renvoyée que lorsque l'index de ligne est 3 et une UITableViewCell de base dans d'autres index.
Le "TableViewCell" personnalisé aura notre UICollectionView. Créez donc une sous-classe UITableViewCell et notez le code ci-dessous.
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
}
}
J'espère que ça aide.
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!
}
Tableau principal
<?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="stackoverflow_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="stackoverflow_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="stackoverflow_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>
Pensez à MVC et à l'incorporation jamais par programme comme ci-dessus. Une sous-classe de UIView (-> c'est ce qu'est réellement une cellule) n'est jamais la classe déléguée pour un délégué et la source de données d'une tableView (idem pour collectionView).
Avez-vous déjà créé une sous-classe de tableview pour effectuer toutes les tâches de programmation dans cette sous-classe - Non! vous le faites dans votre viewcontroller - parce que vous créez des UIView dans les contrôleurs, pour les "contrôler". La bonne façon de procéder est donc:
Permettez-moi de vous donner un exemple (sur la manière de "mieux comprendre" à l'ancienne):
Au lieu d'ajouter un CollectionView maintenant sur votre UITableViewCell personnalisé, ajoutez simplement un UIView "contenant du contenu" (avec des contraintes). Plus tard, vous utiliserez cette vue pour ajouter une vue de collection en tant que sous-vue.
Créez maintenant un nouveau UIViewController (nouveau fichier> ...) avec XIB OU glissez-déposez un nouveau UIViewController depuis le panneau des propriétés dans votre storyboard et créez également un UIViewController classe. N'oubliez pas de vous connecter. (nous ferons le premier pour une meilleure compréhension)
Dans ce nouveau ViewController avec collectionView, vous gérez tout comme d'habitude, avec délégué et source de données, etc.
MAINTENANT : Sur le ViewController (premier) avec tableView vous instanciez le nouveau ViewController (avec collectionView) sur chaque cellule (cellForRowAtIndexPath), et ajoutez sa collectionView en tant que sous-vue à la vue actuelle que vous avez créée (comme sur le support de contenu), par exemple:
laissez myViewControllerWithCollectionView = MyViewControllerWithCollectionView () myCell.contentHolderView.addSubview (myViewControllerWithCollectionView.collectionView)
Ce que vous pouvez aussi faire (et peut-être que la manière la plus récente et la meilleure, je n'ai jamais essayé moi-même mais je suis sûr que cela fonctionnera très bien, c'est: UIContainerView).
C'est ça! quelques conseils pour vous:
soyez prudent lors de l'ajout d'une nouvelle sous-vue dans cellForRowAtIndexPath, vérifiez toujours si le contentHolderView a déjà un
myViewControllerWithCollectionView.collectionView
pour récupérer des actions de la collectionView, à votre vue actuelle, ajoutez un protocole personnalisé (délégué) à votre vue, pour plus d'informations. Ne définissez jamais délégué et source de données de votre collectionView vers votre table principale ViewController, laissez simplement tout gérer sur le bon viewcontroller et envoyez les informations à tout autre viewcontroller si nécessaire.