web-dev-qa-db-fra.com

ios 8 Swift - TableView avec CollectionView intégré

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

16
Robert Plant

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.

23
Matt

Détails

  • Xcode 10.2.1 (10E1001), Swift 5

Échantillon complet

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>

Résultats

enter image description hereenter image description here

15

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):

  1. Créez un ViewController avec une tableView et ajoutez (addSubview) cette collectionView à votre UITableViewCell.
  2. Vous avez un ViewController sur Storyboard et également un UITableView est intégré
  3. Il est également connecté à votre classe ViewController en tant que sortie (et également à son délégué et à sa source de données)
  4. 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.

  5. 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)

  6. Le nouveau ViewController se comporte comme un ViewController avec un CollectionView (identique à 1. mais collectionView)
  7. Dans ce nouveau ViewController avec collectionView, vous gérez tout comme d'habitude, avec délégué et source de données, etc.

  8. 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.

5
kurtanamo