ios 8 Swift - 带有嵌入式 CollectionView 的 TableView

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/31582378/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 06:58:30  来源:igfitidea点击:

ios 8 Swift - TableView with embedded CollectionView

iosswiftuitableviewuicollectionview

提问by Robert Plant

I am relatively new to iOS programming and have tried a few things but to no avail.

我对 iOS 编程比较陌生,尝试了一些东西但无济于事。

I would like to put a CollectionViewinside a TableViewCell. I can code each individually, but dont understand how to set and reference each CollectionViewwithin a TableViewCell.

我想在CollectionView里面放一个TableViewCell. 我可以单独编码每个,但不明白如何CollectionViewTableViewCell.

I have found this tutorial, http://ashfurrow.com/blog/putting-a-uicollectionview-in-a-uitableviewcell/which shows how it can be done in Objective-C but I have always struggled with Obj-C.

我找到了这个教程,http://ashfurrow.com/blog/putting-a-uicollectionview-in-a-uitableviewcell/,它展示了如何在 Objective-C 中完成,但我一直在与 Obj-C 斗争。

Does anyone know of a Swift Tutorial or can assist? I am in the process of creating a simple project/code which I will post shortly to try and assist.

有谁知道 Swift 教程或可以提供帮助吗?我正在创建一个简单的项目/代码,我将很快发布以尝试提供帮助。

EDIT 1

编辑 1

I have just found the swift version of the above link. I am working through this now, but seems over complicated, modifying the AppDelegate.

我刚刚找到了上述链接的 swift 版本。我现在正在解决这个问题,但似乎过于复杂,修改 AppDelegate。

https://github.com/DahanHu/DHCollectionTableView

https://github.com/DahanHu/DHCollectionTableView

Many thanks Rob

非常感谢罗布

回答by Matt

Create a usual UITableView and in your UITableViewCell create the UICollectionView. Your collectionView delegate and datasource should conform to that UITableViewCell.

创建一个普通的 UITableView 并在你的 UITableViewCell 中创建 UICollectionView。您的 collectionView 委托和数据源应符合该 UITableViewCell。

Just go through this

只需通过这个

In your ViewController

在你的 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
    }
}

As you can see I've created two different cells, a custom TableViewCell which is returned only when the row index is 3 and a basic UITableViewCell in other indices.

如您所见,我创建了两个不同的单元格,一个自定义 TableViewCell,仅在行索引为 3 时返回,其他索引中的基本 UITableViewCell 才返回。

The custom "TableViewCell" will have our UICollectionView. So Create a UITableViewCell subclass and write down the below code.

自定义“TableViewCell”将拥有我们的 UICollectionView。所以创建一个 UITableViewCell 子类并写下下面的代码。

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
}
}

Hope it helps.

希望能帮助到你。

回答by Vasily Bodnarchuk

Details

细节

  • Xcode 10.2.1 (10E1001), Swift 5
  • Xcode 10.2.1 (10E1001),Swift 5

Full sample

完整样品

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="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>

Results

结果

enter image description hereenter image description here

在此处输入图片说明在此处输入图片说明

回答by kurtanamo

Think about MVC and embedding never programmatically like this above. A subclass of UIView (-> thats what a cell really is) is never the delegate class for a delegate and datasource of an tableView (same for collectionView).

考虑 MVC 和嵌入永远不会像上面这样以编程方式。UIView 的子类(-> 这就是单元格的真正含义)永远不是 tableView 的委托和数据源的委托类(与 collectionView 相同)。

Did you ever create a subclass of tableview to do all the programmatically stuff in this subclass - No! you do it in your viewcontroller - because you create UIView's in Controllers, to "control" them. So the right way you have to do is:

您是否曾经创建过 tableview 的子类来以编程方式执行此子类中的所有内容 - 不!您在视图控制器中执行此操作 - 因为您在控制器中创建 UIView 以“控制”它们。所以你必须做的正确方法是:

Let me give ya a example (on the oldschool "better-understanding" way):

让我举个例子(在老派的“更好的理解”方式上):

  1. Create a ViewController with a tableView and add (addSubview) this collectionView to your UITableViewCell.
  2. You have an ViewController on Storyboard and also a UITableView is embedded
  3. It's also connected with your ViewController class as an Outlet (and also his delegate and datasource)
  4. Instead of adding a CollectionView now on your Custom UITableViewCell, just add a "content-holder" UIView (with constraints). Later you will use this view to add a collectionview as an subview.

  5. Now Create a New UIViewController (New File > ...) with XIB ORdrag and drop a new UIViewController from properties panel in your storyboard and create also a UIViewController class. Don't forget to connect each other. (we will do the first one for better understanding)

  6. The new ViewController just handle like a ViewController with a CollectionView (same like 1. but collectionView)
  7. In this new ViewController with collectionView you handle everyhting like usual, with delegate and datasource and so on...

  8. NOW: On the ViewController (first one) with tableView you instantiate the new ViewController (with collectionView) on every cell (cellForRowAtIndexPath), and add his collectionView as a subview to the current View you created (as on content holder), e.g.:

  1. 创建一个带有 tableView 的 ViewController 并将此 collectionView 添加(addSubview)到您的 UITableViewCell。
  2. 你在 Storyboard 上有一个 ViewController 并且还嵌入了一个 UITableView
  3. 它也作为 Outlet 与您的 ViewController 类相连(还有他的委托和数据源)
  4. 现在不要在您的自定义 UITableViewCell 上添加 CollectionView,只需添加一个“内容持有者”UIView(带约束)。稍后您将使用此视图添加一个集合视图作为子视图。

  5. 现在使用 XIB 创建一个新的 UIViewController (New File > ...)或者从故事板的属性面板中拖放一个新的 UIViewController 并创建一个 UIViewController 类。不要忘记相互连接。(为了更好的理解,我们会做第一个)

  6. 新的 ViewController 就像一个带有 CollectionView 的 ViewController(与 1 一样,但 collectionView)
  7. 在这个带有 collectionView 的新 ViewController 中,您可以像往常一样处理所有内容,包括委托和数据源等等......

  8. 现在:在带有 tableView 的 ViewController(第一个)上,您在每个单元格(cellForRowAtIndexPath)上实例化新的 ViewController(带有 collectionView),并将他的 collectionView 作为子视图添加到您创建的当前视图(如在内容持有者上),例如:

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

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

What you can also do (and maybe the newer and better way, never tried myself but I'm sure it will work very well, is: UIContainerView).

您还可以做的(也许是更新更好的方法,我自己从未尝试过,但我相信它会很好用,是:UIContainerView)。

Thats it! some tips for you:

就是这样!给你的一些提示:

  • be careful on adding a new subview in cellForRowAtIndexPath, check always if the contentHolderView has already a

    myViewControllerWithCollectionView.collectionView

  • 在 cellForRowAtIndexPath 添加新子视图时要小心,始终检查 contentHolderView 是否已经有

    myViewControllerWithCollectionView.collectionView

to get Actions back from the collectionView, to your current View add a custom protocol (delegate) to your view, for more information. Never set delegate and datasource from your collectionView to your main tableViewController, just let handle everything on the right viewcontroller und push information to any other viewcontroller if needed.

要从 collectionView 取回 Actions,向您当前的 View 添加自定义协议(委托)到您的视图,以获取更多信息。永远不要将您的 collectionView 中的委托和数据源设置到您的主 tableViewController,只需让处理正确视图控制器上的所有内容,并在需要时将信息推送到任何其他视图控制器。