将多个 UICollectionViewCell 推送到同一个 UIViewController

Posted

技术标签:

【中文标题】将多个 UICollectionViewCell 推送到同一个 UIViewController【英文标题】:Push multiple UICollectionViewCell to the same UIViewController 【发布时间】:2020-03-10 11:39:08 【问题描述】:

当我单击任何单元格时,我有多个 UICollectionView 和多个 UICollectionViewCell,它应该将我推送到另一个带有 UITableView 的 UIViewController。

我想要的只是每个 UICollectionViewCell 调用基于 indexPath 的特定 API 并将响应放入 UITableView。

例子:

如果我点击“牛肉”单元格,API 会喜欢那个“https://apilink.com/recipes/random?search=beef”,其他单元格依此类推。

我根本没有使用故事板,我使用的是程序化方法。

主视图:

protocol HomeViewDidSelectActionDelegate: class 
    func recipesSelectionAction(indexPath: IndexPath)



class HomeView: UIView 

    var recipes: Recipes?
    var recipesDetails = [Recipe]()
    let indicator = ActivityIndicator()

    weak var homeViewDidSelectActionDelegate: HomeViewDidSelectActionDelegate?

    let categories = ["italian food", "chinese food", "korean food", "italian food", "chinese food", "korean food", "italian food", "chinese food", "korean food", "italian food", "chinese food", "korean food"]

    override init( frame: CGRect) 
        super.init(frame: frame)
        layoutUI()
    

    required init?(coder: NSCoder) 
        fatalError("init(coder:) has not been implemented")
    

    lazy var foodTableView: UITableView = 
        let foodTableView = UITableView()
        foodTableView.translatesAutoresizingMaskIntoConstraints = false
        foodTableView.backgroundColor = #colorLiteral(red: 0.9568627451, green: 0.9568627451, blue: 0.9568627451, alpha: 1)
        foodTableView.delegate = self
        foodTableView.dataSource = self
        foodTableView.register(CategoriesTableViewCellCollectionViewCell.self, forCellReuseIdentifier: "CategoriesTableViewCellCollectionViewCell")
        foodTableView.register(PopularRecipesTableViewCellCollectionViewCell.self, forCellReuseIdentifier: "PopularRecipesTableViewCellCollectionViewCell")
        foodTableView.register(HomeTableViewCell.self, forCellReuseIdentifier: "HomeTableViewCell")
        foodTableView.rowHeight = UITableView.automaticDimension
        foodTableView.estimatedRowHeight = 100
        foodTableView.showsVerticalScrollIndicator = false
        foodTableView.separatorStyle = .none
        return foodTableView
    ()

    func setupFoodTableView() 

        NSLayoutConstraint.activate([
            foodTableView.topAnchor.constraint(equalTo: topAnchor),
            foodTableView.bottomAnchor.constraint(equalTo: bottomAnchor),
            foodTableView.leadingAnchor.constraint(equalTo: leadingAnchor),
            foodTableView.trailingAnchor.constraint(equalTo: trailingAnchor)
        ])
    

    func addSubview() 
        addSubview(foodTableView)
    

    func layoutUI() 
        indicator.setupIndicatorView(self, containerColor: .customDarkGray(), indicatorColor: .white)
        addSubview()
        setupFoodTableView()
        DispatchQueue.main.async 
            self.fetchData()
        

    

    func fetchData() 
        AF.request("https://api.com").responseJSON  (response) in
            if let error = response.error 
                print(error)
            
            do 
                if let data = response.data 
                    self.recipes = try JSONDecoder().decode(Recipes.self, from: data)
                    self.recipesDetails = self.recipes?.recipes ?? []
                    DispatchQueue.main.async 
                        self.foodTableView.reloadData()
                    
                

             catch 
                print(error)
            
            self.indicator.hideIndicatorView(self)
        
    



extension HomeView: UITableViewDelegate, UITableViewDataSource 

    func numberOfSections(in tableView: UITableView) -> Int 
        return 3
    

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
        if section == 0 
            return 1
         else if section == 1 
            return 1
         else 
            return recipesDetails.count
        
    

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 

        if indexPath.section == 0 
            let cell = tableView.dequeueReusableCell(withIdentifier: "CategoriesTableViewCellCollectionViewCell", for: indexPath) as! CategoriesTableViewCellCollectionViewCell
            cell.recipesDidselectActionDelegate = self
            cell.collectionView.reloadData()
            return cell
         else if indexPath.section == 1 
            let cell = tableView.dequeueReusableCell(withIdentifier: "PopularRecipesTableViewCellCollectionViewCell", for: indexPath) as! PopularRecipesTableViewCellCollectionViewCell
            cell.collectionView.reloadData()
            return cell
         else 
            let cell = tableView.dequeueReusableCell(withIdentifier: "HomeTableViewCell", for: indexPath) as! HomeTableViewCell
            let url = URL(string: recipesDetails[indexPath.row].image ?? "Error")
            cell.foodImage.kf.setImage(with: url)
            cell.foodTitle.text = recipesDetails[indexPath.row].title

            if let readyInMin = recipesDetails[indexPath.row].readyInMinutes 
                cell.cookingTimeInfoLabel.text = "\(readyInMin) Minutes"
            

            if let pricePerServing = recipesDetails[indexPath.row].pricePerServing 
                cell.priceInfoLabel.text = "$\(Int(pricePerServing))"
            

            if let serving = recipesDetails[indexPath.row].servings 
                cell.servesInfoLabel.text = "\(serving)"
            

            return cell
        

    

    func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? 
        if section == 2 
            return "Random recipes"
         else 
            return ""
        
    

    func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) 
        (view as! UITableViewHeaderFooterView).contentView.backgroundColor = #colorLiteral(red: 0.9568627451, green: 0.9568627451, blue: 0.9568627451, alpha: 1)
        (view as! UITableViewHeaderFooterView).textLabel?.font = UIFont(name: "AvenirNext-DemiBold", size: 16)
        (view as! UITableViewHeaderFooterView).textLabel?.textColor = .customDarkGray()
    

    func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat 
        return 30.0
    

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat 
        if indexPath.section == 0 
            return 130
         else if indexPath.section == 1 
            return 180
         else 
            return UITableView.automaticDimension
        

    



extension HomeView: RecipesDidselectActionDelegate
    func recipesSelectionAction(indexPath: IndexPath) 
            homeViewDidSelectActionDelegate?.recipesSelectionAction(indexPath: indexPath)
    

HomeViewController:

class HomeViewController: UIViewController 

    var recipes: Recipes?
    var recipesDetails = [Recipe]()
    let indicator = ActivityIndicator()

    let searchController = UISearchController(searchResultsController: nil)
    let leftMenuNavigationController = SideMenuNavigationController(rootViewController: SideMenuTableViewController())

    lazy var mainView: HomeView = 
        let view = HomeView(frame: self.view.frame)
        view.homeViewDidSelectActionDelegate = self
        return view
    ()

    override func loadView() 
        super.loadView()
        view = mainView
    

    override func viewDidLoad() 
        super.viewDidLoad()

    

    override var preferredStatusBarStyle: UIStatusBarStyle 
        .lightContent
    

    override func viewWillAppear(_ animated: Bool) 
        super.viewWillAppear(animated)
        navigationController?.isNavigationBarHidden = false
        setNeedsStatusBarAppearanceUpdate()
        setupNavigationWithLargeTitle()
        setupLeftSideMenu()
        setupNavigation()
    

    func setupLeftSideMenu() 
        SideMenuManager.default.leftMenuNavigationController = leftMenuNavigationController
        leftMenuNavigationController.leftSide = true
        leftMenuNavigationController.statusBarEndAlpha = 0
        leftMenuNavigationController.presentationStyle = .viewSlideOut
        leftMenuNavigationController.allowPushOfSameClassTwice = false
        leftMenuNavigationController.menuWidth = view.frame.width * (3/4)
        leftMenuNavigationController.navigationBar.isHidden = true
    



extension HomeViewController: UISearchControllerDelegate, UISearchBarDelegate 
    func setupNavigationWithLargeTitle() 
        navigationController?.navigationBar.prefersLargeTitles = true
        searchController.delegate = self
        searchController.searchBar.delegate = self
        searchController.searchBar.searchTextField.backgroundColor = .white
        searchController.searchBar.searchTextField.textColor = .customDarkGray()
        searchController.searchBar.searchTextField.font = UIFont(name: "AvenirNext-Regular", size: 14)
        searchController.searchBar.tintColor = UIColor.CustomGreen()
        self.navigationItem.searchController = searchController
        self.title = "Home"
        let navBarAppearance = UINavigationBarAppearance()
        navBarAppearance.configureWithOpaqueBackground()
        navBarAppearance.titleTextAttributes = [.foregroundColor: UIColor.CustomGreen()]
        navBarAppearance.largeTitleTextAttributes = [.foregroundColor: UIColor.CustomGreen(), .font: UIFont(name: "AvenirNext-Heavy", size: 36)!]
        navigationController?.navigationBar.tintColor = .white
        navigationController?.navigationBar.standardAppearance = navBarAppearance
        navigationController?.navigationBar.scrollEdgeAppearance = navBarAppearance
        navigationItem.rightBarButtonItem = UIBarButtonItem(image: UIImage(systemName: "heart.fill"), style: .plain, target: self, action: #selector(saveButtonTapped))
        navigationItem.rightBarButtonItem?.tintColor = UIColor.CustomGreen()
        navigationItem.leftBarButtonItem = UIBarButtonItem(image: UIImage(named: "menu"), style: .plain, target: self, action: #selector(menuButtonTapped))
        navigationItem.leftBarButtonItem?.tintColor = UIColor.CustomGreen()
    

    @objc func saveButtonTapped() 
        print("OK")
    

    @objc func menuButtonTapped() 
        self.present(leftMenuNavigationController, animated: true, completion: nil)
    



extension HomeViewController: HomeViewDidSelectActionDelegate
    func recipesSelectionAction(indexPath: IndexPath) 
        // Here you can push your destination View Controller
        if indexPath.row == 0 
            let vc = RecipesTableViewDetails()
            self.show(vc, sender: nil)
        

    

CategoriesTableViewCellCollectionViewCell:

protocol RecipesDidselectActionDelegate: class 
    func recipesSelectionAction(indexPath: IndexPath)


class CategoriesTableViewCellCollectionViewCell: UITableViewCell, UICollectionViewDelegateFlowLayout 

    weak var recipesDidselectActionDelegate: RecipesDidselectActionDelegate?

    let categories: [String] = [
        "Main course",
        "Beef",
        "Chicken",
        "Seafood",
        "Vegetarian",
        "Breakfast",
        "Side dish",
        "Drink",
        "Sauce",
        "Soup",
        "Snacks",
        "Dessert"
    ]

    let categoriesImages: [UIImage] = [
        UIImage(named: "maincourse")!,
        UIImage(named: "beef")!,
        UIImage(named: "chicken")!,
        UIImage(named: "seafood")!,
        UIImage(named: "vegetarian")!,
        UIImage(named: "breakfast")!,
        UIImage(named: "sidedish")!,
        UIImage(named: "drink")!,
        UIImage(named: "sauce")!,
        UIImage(named: "soup")!,
        UIImage(named: "snacks")!,
        UIImage(named: "dessert")!
    ]

    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) 
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        layoutUI()
        selectionStyle = .none
        self.backgroundColor = .clear
    

    required init?(coder: NSCoder) 
        fatalError("init(coder:) has not been implemented")
    

    lazy var containerView: UIView = 
        let containerView = UIView()
        containerView.backgroundColor = .clear
        containerView.translatesAutoresizingMaskIntoConstraints = false
        return containerView
    ()

    lazy var categoriesNameLabel: UILabel = 
        let categoriesNameLabel = UILabel()
        categoriesNameLabel.text = "Categories"
        categoriesNameLabel.textColor = .customDarkGray()
        categoriesNameLabel.textAlignment = .left
        categoriesNameLabel.font = UIFont(name: "AvenirNext-DemiBold", size: 16)
        categoriesNameLabel.translatesAutoresizingMaskIntoConstraints = false
        return categoriesNameLabel
    ()

    lazy var seeAllCategoriesButton: UIButton = 
        let seeAllCategoriesButton = UIButton()
        seeAllCategoriesButton.setTitle("See all", for: .normal)
        seeAllCategoriesButton.setTitleColor(.CustomGreen(), for: .normal)
        seeAllCategoriesButton.titleLabel?.font = UIFont(name: "AvenirNext-Regular", size: 14)
        seeAllCategoriesButton.translatesAutoresizingMaskIntoConstraints = false
        seeAllCategoriesButton.addTarget(self, action: #selector(test), for: .touchUpInside)
        return seeAllCategoriesButton
    ()

    @objc func test() 
        print("Test worked")
    

    lazy var collectionView: UICollectionView = 
        let layout = UICollectionViewFlowLayout()
        layout.scrollDirection = .horizontal
        let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
        collectionView.translatesAutoresizingMaskIntoConstraints = false
        collectionView.backgroundColor = .clear
        collectionView.showsHorizontalScrollIndicator = false
        collectionView.delegate = self
        collectionView.dataSource = self
        collectionView.register(CategoriesCollectionViewCell.self, forCellWithReuseIdentifier: "CategoriesCollectionViewCell")
        return collectionView
    ()

    func setupContainerViewConstraints() 
        NSLayoutConstraint.activate([
            containerView.topAnchor.constraint(equalTo: topAnchor, constant: 8),
            containerView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16),
            containerView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16),
            containerView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: 0)
        ])
    

    func setupCategoriesNameLabelConstraints() 
        NSLayoutConstraint.activate([
            categoriesNameLabel.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
            categoriesNameLabel.centerYAnchor.constraint(equalTo: seeAllCategoriesButton.centerYAnchor)
        ])
    

    func setupSeeAllCategoriesButtonConstraints() 
        NSLayoutConstraint.activate([
            seeAllCategoriesButton.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
            seeAllCategoriesButton.topAnchor.constraint(equalTo: containerView.topAnchor)
        ])
    

    func setupCollectionViewConstraints() 
        NSLayoutConstraint.activate([
            collectionView.topAnchor.constraint(equalTo: seeAllCategoriesButton.bottomAnchor, constant: 0),
            collectionView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: 0),
            collectionView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16),
            collectionView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16),
        ])
    

    func addSubviews() 
        addSubview(containerView)
        containerView.addSubview(categoriesNameLabel)
        containerView.addSubview(seeAllCategoriesButton)
        containerView.addSubview(collectionView)
    

    func layoutUI() 
        addSubviews()
        setupCollectionViewConstraints()
        setupContainerViewConstraints()
        setupCategoriesNameLabelConstraints()
        setupSeeAllCategoriesButtonConstraints()
    




extension CategoriesTableViewCellCollectionViewCell: UICollectionViewDelegate, UICollectionViewDataSource 

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int 
        return categories.count
    

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell 
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CategoriesCollectionViewCell", for: indexPath) as! CategoriesCollectionViewCell
        cell.categoriesImage.image = categoriesImages[indexPath.row]
        cell.categoryName.text = categories[indexPath.row]
        return cell
    

    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) 
        recipesDidselectActionDelegate?.recipesSelectionAction(indexPath: indexPath)
    

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize 
        let w: CGFloat = self.frame.width * 0.4
        let h: CGFloat = collectionView.frame.size.height - 6.0
        return CGSize(width: w, height: h)
    


RecipesTableViewDetailsView:

class RecipesTableViewDetailsView: UIView 

    var recipes: Recipes?
    var recipesDetails = [Recipe]()
    let indicator = ActivityIndicator()

    override init(frame: CGRect) 
        super.init(frame: frame)
        layoutUI()
    

    required init?(coder: NSCoder) 
        fatalError("init(coder:) has not been implemented")
    

    lazy var foodTableView: UITableView = 
        let foodTableView = UITableView()
        foodTableView.translatesAutoresizingMaskIntoConstraints = false
        foodTableView.backgroundColor = #colorLiteral(red: 0.9568627451, green: 0.9568627451, blue: 0.9568627451, alpha: 1)
        foodTableView.delegate = self
        foodTableView.dataSource = self
        foodTableView.register(HomeTableViewCell.self, forCellReuseIdentifier: "HomeTableViewCell")
        foodTableView.rowHeight = UITableView.automaticDimension
        foodTableView.estimatedRowHeight = 100
        foodTableView.showsVerticalScrollIndicator = false
        foodTableView.separatorStyle = .none
        return foodTableView
    ()

    func setupFoodTableView() 

        NSLayoutConstraint.activate([
            foodTableView.topAnchor.constraint(equalTo: topAnchor),
            foodTableView.bottomAnchor.constraint(equalTo: bottomAnchor),
            foodTableView.leadingAnchor.constraint(equalTo: leadingAnchor),
            foodTableView.trailingAnchor.constraint(equalTo: trailingAnchor)
        ])
    

    func addSubview() 
        addSubview(foodTableView)
    

    func layoutUI() 
        addSubview()
        setupFoodTableView()
//        fetchData()

    

    func fetchData() 
        indicator.setupIndicatorView(self, containerColor: .customDarkGray(), indicatorColor: .white)
        AF.request("https://api.com").responseJSON  (response) in
            if let error = response.error 
                print(error)
            
            do 
                if let data = response.data 
                    self.recipes = try JSONDecoder().decode(Recipes.self, from: data)
                    self.recipesDetails = self.recipes?.recipes ?? []
                    DispatchQueue.main.async 
                        self.foodTableView.reloadData()
                    
                

             catch 
                print(error)
            
            self.indicator.hideIndicatorView(self)
        
    



extension RecipesTableViewDetailsView: UITableViewDelegate, UITableViewDataSource 

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
        return recipesDetails.count
    

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 

        let cell = tableView.dequeueReusableCell(withIdentifier: "HomeTableViewCell", for: indexPath) as! HomeTableViewCell
        let url = URL(string: recipesDetails[indexPath.row].image ?? "Error")
        cell.foodImage.kf.setImage(with: url)
        cell.foodTitle.text = recipesDetails[indexPath.row].title

        if let readyInMin = recipesDetails[indexPath.row].readyInMinutes 
            cell.cookingTimeInfoLabel.text = "\(readyInMin) Minutes"
        

        if let pricePerServing = recipesDetails[indexPath.row].pricePerServing 
            cell.priceInfoLabel.text = "$\(Int(pricePerServing))"
        

        if let serving = recipesDetails[indexPath.row].servings 
            cell.servesInfoLabel.text = "\(serving)"
        

        return cell

    

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat 
        return UITableView.automaticDimension
    


【问题讨论】:

不清楚你在问什么。 @kjoe 每个单元格都有自己的 API,当我单击它时它必须获取它,结果将放在 UITableView 中,我想要的是如何制作它 当你说我想要的是怎么做时,是什么?退后一步,暂时忘记你所知道的,试着从不知道你遇到什么问题的人的角度来阅读这个问题。你能指出你具体说出问题所在的部分吗? 您有代码但不清楚您想要什么?当您点击 collectionView 时,将点击的对象 ID/模型对象发送到详细视图,它将下载所需的数据 @Caleb 抱歉,如果我的问题不够清楚,我希望每个 UICollectionViewCell 都调用基于 indexPath 的特定 API 并将响应放入 UITableView。我不知道这是否足够清楚,请告诉我 【参考方案1】:

您可以将 categories 数组项索引分配给 UICollectionViewCell

然后你可以在didSelectItemAt函数中使用cell.tag创建动态API

    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) 
        let id = collectionView.cellForItem(at: indexPath)?.tag
        let link = "https://apilink.com/recipes/random?search=\(categories[id])"
    

【讨论】:

也许你是对的,但我不认为这对我有用,因为我使用的是不同的方法(协议方法)。

以上是关于将多个 UICollectionViewCell 推送到同一个 UIViewController的主要内容,如果未能解决你的问题,请参考以下文章

将单个 UICollectionViewCell 对齐到 collectionView 的左侧

UICollectionView多个单元格(UICollectionViewCell)在Objective-C中闪烁同步

从多个 UICollectionVIews 内的 UICollectionViewCell 推送新的 UICollectionViewController

UICollectionViewCell:使用“查看更多”按钮展开文本 - AutoSizing

注册大量 UICollectionViewCell 类以在 UICollectionView 中重用

UICollectionViewCell 在collectionview的多个单元格中看到的一个变化