在UICollectionViewCell中添加UICollectionView

4
我正在使用Swift为我所在的医院构建iOS应用程序。
某种程度上,在特定的功能中,我必须将一个UICollectionView放置在UICollectionViewCell中。我想要实现的是,每个父UICollectionView(垂直滚动)的内容都有几个子级(可以水平滚动),取决于父行。
举个例子,我必须显示医生列表(姓名和照片),然后在他们的姓名和照片下面显示他们的每个实践时间表。每位医生的实践时间表都会有所不同。因此,我必须将它放在UICollectionView中。
我已经尝试了网上找到的几种解决方案,但仍然无法解决问题。
最大的问题是:我不知道在何处加载子数据源(医生的时间表),以及何时可以加载它,因为我不能有像下面这样的两个函数:
collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell

这是我想要实现的效果:

collection view 的设计

UIImage 和医生姓名 (UILabel) 放在父级 UICollectionViewCell 中(垂直滚动),然后盒子中的所有内容(练习日n和练习时间)都是子级 UICollectionView (水平滚动)。

PS:有许多医生,每个医生都有几个练习日。

请告诉我如何做到这一点。


请检查我的答案,其中我使用更面向对象的方法设置了代理。 - tapizquent
4个回答

11

如果你真的想在collectionViewCell中插入一个collectionView,那么有一个非常简单的步骤。创建一个UICollectionView实例并将其添加到collectionViewCell中。如果你喜欢,可以使用这个例子。

//
//  ViewController.swift
//  StackOverFlowAnswer
//
//  Created by BIKRAM BHANDARI on 18/6/17.
//  Copyright © 2017 BIKRAM BHANDARI. All rights reserved.
//

import UIKit

class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {

    let cellId = "CellId"; //Unique cell id

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .red; //just to test

        collectionView.register(Cell.self, forCellWithReuseIdentifier: cellId) //register collection view cell class
        setupViews(); //setup all views
    }

    func setupViews() {

        view.addSubview(collectionView); // add collection view to view controller
        collectionView.delegate = self; // set delegate
        collectionView.dataSource = self; //set data source

        collectionView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true; //set the location of collection view
        collectionView.rightAnchor.constraint(equalTo:  view.rightAnchor).isActive = true; // top anchor of collection view
        collectionView.heightAnchor.constraint(equalTo: view.heightAnchor).isActive = true; // height
        collectionView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true; // width

    }

    let collectionView: UICollectionView = { // collection view to be added to view controller
        let cv = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout()); //zero size with flow layout
        cv.translatesAutoresizingMaskIntoConstraints = false; //set it to false so that we can suppy constraints
        cv.backgroundColor = .yellow; // test
        return cv;
    }();

    //deque cell
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath);
//        cell.backgroundColor = .blue;
        return cell;
    }

    // number of rows
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 5;
    }

    //size of each CollecionViewCell
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        return CGSize(width: view.frame.width, height: 200);
    }
}

// first UICollectionViewCell
class Cell: UICollectionViewCell, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {

    let cellId = "CellId"; // same as above unique id

    override init(frame: CGRect) {
        super.init(frame: frame);

        setupViews();
        collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: cellId); //register custom UICollectionViewCell class.
        // Here I am not using any custom class

    }


    func setupViews(){
        addSubview(collectionView);

        collectionView.delegate = self;
        collectionView.dataSource = self;

        collectionView.leftAnchor.constraint(equalTo: leftAnchor).isActive = true;
        collectionView.rightAnchor.constraint(equalTo: rightAnchor).isActive = true;
        collectionView.topAnchor.constraint(equalTo: topAnchor).isActive = true;
        collectionView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true;
    }

    let collectionView: UICollectionView = {
        let layout = UICollectionViewFlowLayout();
        layout.scrollDirection = .horizontal; //set scroll direction to horizontal
        let cv = UICollectionView(frame: .zero, collectionViewLayout: layout);
        cv.backgroundColor = .blue; //testing
        cv.translatesAutoresizingMaskIntoConstraints = false;
        return cv;
    }();

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath);
        cell.backgroundColor = .red;
        return cell;
    }

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 5;
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        return CGSize(width: self.frame.width, height: self.frame.height - 10);
    }
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

示例截图


我必须有两个视图控制器类吗?我需要从JSON中“拉取”数据,那么在代码中应该把它放在哪里?谢谢。 - christ2702
我已经解决了上一个错误,请给我一些建议,在什么时候我应该加载我的数据。 - christ2702
您可以在上面的示例中的Cell类中填充JSON数据。 - Jivan Bhandari
请查看我上面的屏幕截图。 - christ2702
请翻译以下与程序编写相关的内容,仅返回翻译后的文本。并请您在代码中添加注释,以便我知道在哪里放置我的数据。 - christ2702
显示剩余12条评论

5
这可能有点晚了,但对于仍在寻找答案的人来说,经过一些研究和挖掘,我偶然发现了几篇帖子,说明为什么你不应该将你的手机设置为collectionView的代理。所以,我迷失了,因为我找到的几乎所有解决方案都在这样做,直到我最终找到了我认为是实现嵌套collectionViews的最佳方式。
为了背景说明,我的应用程序不仅包括一个而且是两个集合视图,在另一个collectionView的不同单元格中,因此使用标签等设置代理并不是最好的方法,也不是正确的面向对象解决方案。
所以做法如下:
首先,您必须创建一个不同的类作为内部collectionView的代理。我是这样做的:
class InnerCollectionViewDelegate: NSObject, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
 // CollectionView and layout delegate methods here
 // sizeForItemAt, cellForItemAt, etc...
}

现在,在您的内部collectionView(或者更确切地说是您拥有内部collectionView的单元格)中创建一个函数,该函数将允许您设置其代理。

class InnerCell: UICollectionViewCell {

    var collectionView: UICollectionView

    init() {
        let layout = UICollectionViewFlowLayout()
        collectionView = UICollectionView(frame: CGRect(x: 0, y: 0, width: frame.width, height: frame.height), collectionViewLayout: layout)
     }

    func setCollectionViewDataSourceDelegate(dataSourceDelegate: UICollectionViewDataSource & UICollectionViewDelegate) {
            collectionView.delegate = dataSourceDelegate
            collectionView.dataSource = dataSourceDelegate
            collectionView.reloadData()
    }
}

最后,在您的ViewController中,您拥有最外层(主要)collectionView,请执行以下操作:
首先实例化内部collectionView的委托。
var innerDelegate = InnerCollectionViewDelegate()

然后

override func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
        if let cell = cell as? InnerCell {
            cell.setCollectionViewDataSourceDelegate(dataSourceDelegate: innerDelegate)
        }
}

这可能不是完美的解决方案,但至少你实现了关注点分离,因为你的单元格不应该是代理。请记住,你的单元格只负责显示信息,而不是试图确定collectionView的大小等内容。

我发现有类似的答案涉及设置collectionView标记等操作,但我发现这样做会更难处理每个collectionView,而且处理标记可能会导致混乱代码或意外行为。

我省略了注册和出队单元格的步骤,但我相信你们都很熟悉。如果不是,请告诉我,我会尝试为你提供帮助。


1

我已经尝试了,但是我仍然不理解。我不知道如何在项目中实现它。 - christ2702
一样!我一直在尝试找到正确的方法来实现这个。我的collectionView里面包含了不只一个,而是两个其他的collectionView(我的两个cell包含了collectionViews),但是还没有找到合适的解决方案。 - tapizquent
找到了更好的解决方案。请查看下面的答案。 - tapizquent

0
在集合视图单元格中添加collectionView,并在collectionviewclass.swift中添加委托方法。然后在collectionview的cellforrowatindexpath中传递要显示在单元格中的列表。如果您在实现时没有成功,请告诉我。我会提供代码,因为我已经以这种方式实现了它。

对不起,我只是不知道如何执行它。 - christ2702
我不理解的主要问题是,我必须有两个集合视图类还是只需要一个父集合视图?如何将子列表传递给父级? - christ2702

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接