如何以编程方式从 UICollectionView 中的 "didSelectItemAt" 推送 UIViewController(使用单独的文件)?

How to push a UIViewController programmatically from "didSelectItemAt" in UICollectionView (using separate files)?

我在单独的 swift 文件中设置了一个 UICollectionView,该文件显示在 HomeViewController 上。我想在集合视图单元格 selected 时推送 DetailViewController。我想在没有故事板的情况下以编程方式执行此操作。我一直使用 "navigationController.pushViewController(vc, animated: true)" 推送 ViewController,但我无法从 UICollectionView 的 "didSelectItemAt" 函数中访问 navigationController。如果重要的话,我确实有一个自定义的 UICollectionViewCell 设置并用于单元格。

我怎样才能完成这项工作? collectionView 显示正确,当我 select 一个单元格时,我知道 didSelectItemAt 正在 运行 正如我的打印语句 "Did select item at..." 在控制台中显示的那样。

经过几个小时的谷歌搜索,我无法弄明白。我仍在学习 M-V-C 结构,因此我可能在所有设置方面存在根本缺陷。我还看到我可能需要创建一个自定义委托来处理这个问题,但我觉得我缺少一些更简单的东西,因为 didSelectItemAt 函数就在那里。

这是我的 UICollectionView swift 文件中的相关代码:

class HomeDataController: NSObject, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {

    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {

        print("Did select item at...")
        // this is where I am trying to push the DetailViewController

    }
}

这是我的 HomeViewController:

class HomeViewController: UIViewController {

    let collectionView: UICollectionView = {
            let layout = UICollectionViewFlowLayout()
            layout.scrollDirection = .vertical
            layout.minimumLineSpacing = 30
            layout.sectionInset.top = 20
            layout.sectionInset.bottom = 20

            let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
            cv.translatesAutoresizingMaskIntoConstraints = false
            cv.backgroundColor = UIColor(named: "background")
            cv.register(HomeCustomCell.self, forCellWithReuseIdentifier: "homeCell")
            return cv
        }()

    let dataController: HomeDataController = {
        let dataController = HomeDataController()
        return dataController
    }()


    override func viewDidLoad() {
        super.viewDidLoad()

        collectionView.dataSource = self.dataController
        collectionView.delegate = self.dataController

        view.addSubview(collectionView)
    }
}
class HomeViewController: UIViewController {

let collectionView: UICollectionView = {
            let layout = UICollectionViewFlowLayout()
            layout.scrollDirection = .vertical
            layout.minimumLineSpacing = 30
            layout.sectionInset.top = 20
            layout.sectionInset.bottom = 20

            let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
            cv.translatesAutoresizingMaskIntoConstraints = false
            cv.backgroundColor = UIColor(named: "background")
            cv.register(HomeCustomCell.self, forCellWithReuseIdentifier: "homeCell")
            return cv
        }()

    lazy var dataController: HomeDataController = {
        let dataController = HomeDataController(controller: self)
        dataController.didSelectionCompletion = { indexPath in
            print("Called...")
        }
        dataController.delegate = self
        return dataController
    }()


    override func viewDidLoad() {
        super.viewDidLoad()

        collectionView.dataSource = self.dataController
        collectionView.delegate = self.dataController

        view.addSubview(collectionView)
    }
}

//MARK: - HomeDataControllerDelegate 
extension HomeViewController : HomeDataControllerDelegate {
    func homeDataController(_ controller: HomeDataController, didSelectItemAt: IndexPath) {
        print("Called..")
    }
}

HomeDataController.swift

protocol HomeDataControllerDelegate {
    func homeDataController(_ controller : HomeDataController, didSelectItemAt : IndexPath)
}


class HomeDataController: NSObject, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {

    private var controller : UIViewController? = nil

    var delegate : HomeDataControllerDelegate? = nil

    var didSelectionCompletion : ((IndexPath) -> (Void))? = nil

    override init() {
        super.init()
    }

    init(controller : UIViewController) {
        super.init()
        self.controller = controller
    }


    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {

        print("Did select item at...")
        // this is where I am trying to push the DetailViewController

        /* Method1 - using property*/
        let detialController = UIViewController()
        controller?.navigationController?.pushViewController(detialController, animated: true)

        /* Method2 - delegate */
        delegate?.homeDataController(self, didSelectItemAt: indexPath)

        /* Method3 - Block */
        self.didSelectionCompletion?(indexPath)
    }
}

使用方法 1(使用 属性)存在局限性,即 HomeDataController 始终只推送一个细节控制器。它变得依赖 class。还有选项 pass destination controller into init method of HomeDataController 再次变得更加复杂。但是如果你有修复 detailViewController 那么你可以使用这个方法。但它不是针对任何特定更改请求的可扩展代码。

块和委托是更标准和可扩展的选项。使用这种方法,我们可以完全控制 UIController 和任何操作。

希望对你有所帮助。