uicollectionviewtableview

ColletionView inside table view not displaying data correctly


[Edit] If additional information is needed, I will be happy to provide it.

I have a situation that I have thus far been unable to figure out. A Collection View embedded inside of a table view; I am using a data dictionary for the data, a total of 7 sections. The problem is that on reloading the data it isn't reading the data dictionary correctly and it's displaying images and section names that do not go together.

One note: If I limit the dictionary to say 4 sections it works just fine, it is only when it's more than that that I am having the problem.

here is a copy of the data dictionary:

var data1 = [quoteData(sectionType: "For You", quotes: ["Categories/General.png"
            ]),
             
             quoteData(sectionType: "Free Today", quotes: ["Categories/Loneliness.png", "Categories/Setting Goals.png"
            ]),
             
             quoteData(sectionType: "Hard Times", quotes: ["Categories/Death.png", "Categories/Loneliness.png"
            ]),
             
             quoteData(sectionType: "Calm Down", quotes:["Categories/Appreciation.png", "Categories/Managing Anxiety.png"
            ]),
             
             quoteData(sectionType: "Relationships", quotes:["Categories/Friendship.png"
            ]),
             
             quoteData(sectionType: "Personal Growth", quotes: ["Categories/Confidence.png", "Categories/Courage.png"
            ]),
             
             quoteData(sectionType: "Spiritual and Philosophy", quotes: ["Categories/Poetry.png"
            ]),
]

View Controller is as follows:

extension CategoriesViewController: UITableViewDelegate, UITableViewDataSource {
    
    //MARK: - Methods
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 200
    }
    
    func numberOfSections(in tableView: UITableView) -> Int {
        return data1.count
    }
    
    func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        return data1[section].sectionType
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 1
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell1", for: indexPath) as! CategoriesTableViewCell
        cell.collectionView.tag = indexPath.section

        guard let catCell = cell as? CategoriesTableViewCell else {
            return cell
        }
        
        catCell.didSelectItemAction = { [weak self]  String in
            self!.existingCat = (self?.defaults.string(forKey: "DefaultQuotes"))!
            self?.defaultCat = String
            if self?.defaultCat != "" {
                if self?.defaultCat != "General" {
                    self?.adAlert()
                } else {
                    // update
                    self?.defaults.set(self?.defaultCat, forKey: "DefaultQuotes")
                    self?.navigationController?.popViewController(animated: true)
                }
            } else {
                self?.navigationController?.popViewController(animated: true)
            }
        }
        return cell
    }
    
    func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
        view.layer.borderWidth = 4
        view.layer.borderColor = UIColor.white.cgColor
        if section == 0 {
            view.tintColor = .clear
        } else {
            // view.tintColor = .colorFromHex("a5d7fa")
            view.tintColor = .colorFromHex("ffffff")
        }
        let header = view as! UITableViewHeaderFooterView
        header.textLabel?.textColor = UIColor.black
        header.textLabel?.font = UIFont.preferredFont(forTextStyle: .title3)

    }
    
    func adAlert() {
        let showAlert = UIAlertController(title: "Unlock Quotes", message: nil, preferredStyle: .alert)
        //let image = UIImage(named: "MIsc/PaintBrushColor")
        
        showAlert.addAction(UIAlertAction(title: "Watch Video", style: .default, handler: { action in
            // Show Ad & update
            self.showAd()
            if self.defaultCat == self.existingCat {
                self.defaults.set(false, forKey: "resetIndex")
            } else {
                self.defaults.set(true, forKey: "resetIndex")
            }
            // update the Default quotes data area and Highlight box
            self.defaults.set(String(self.defaultCat), forKey: "DefaultQuotes")
            // pop view controller
            self.navigationController?.popViewController(animated: true)
        }))
        showAlert.addAction(UIAlertAction(title: "Cancel", style: .default, handler: { action in
        }))
        self.present(showAlert, animated: true, completion: nil)
    }

The TableViewCell code: class CategoriesTableViewCell: UITableViewCell {

// Outlets
@IBOutlet weak var collectionView: UICollectionView!

//Constants
let defaults = UserDefaults.standard

// Variables
var resetIndex: Bool = false
var myCat: String = ""
var didSelectItemAction: ((String) -> Void)?


//Methods
override func awakeFromNib() {
    super.awakeFromNib()
    // Initialization code
    collectionView.delegate = self
    collectionView.dataSource = self
    myCat = defaults.string(forKey: "DefaultQuotes")!
    resetIndex = defaults.bool(forKey: "ResetIndex")
}

override func setSelected(_ selected: Bool, animated: Bool) {
    super.setSelected(selected, animated: animated)

    // Configure the view for the selected state
}

}

extension CategoriesTableViewCell: UICollectionViewDelegate, UICollectionViewDataSource {

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell1", for: indexPath)
    let lblQuote = cell.viewWithTag(80) as? UILabel
    let dougie = data1[collectionView.tag].quotes[indexPath.row]
    // Get range 4 places from the start, and 6 from the end.
    let r = dougie.index(dougie.startIndex, offsetBy: 11)..<dougie.index(dougie.endIndex, offsetBy: -4)
    // Access the string by the range.
    let subString = dougie[r]
    myCat = String(subString)
    didSelectItemAction?(myCat)
    //collectionView.reloadData()
}

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return data1[collectionView.tag].quotes.count
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell1", for: indexPath) as! CategoriesCollectionViewCell
    let Defaults = UserDefaults.standard
    let defQuote = Defaults.string(forKey: "DefaultQuotes")
    cell.images.image = UIImage(named: data1[collectionView.tag].quotes[indexPath.row])
    let myLbl = cell.viewWithTag(80) as? UILabel
    let myIcon = cell.viewWithTag(83) as? UIImageView
    myIcon?.image = UIImage(named: "bed.double.circle")
    let padlock = cell.viewWithTag(81) as? UIImageView
    let dougie = data1[collectionView.tag].quotes[indexPath.row]
    // Get range 4 places from the start, and 6 from the end.
    let r = dougie.index(dougie.startIndex, offsetBy: 11)..<dougie.index(dougie.endIndex, offsetBy: -4)
    // Access the string by the range.
    let substring = dougie[r]
    myLbl?.text? = String(substring)
    if myLbl?.text == defQuote {
        // we are going to set the border
        cell.images?.layer.borderWidth = 2.5
        cell.images?.layer.borderColor = UIColor.red.cgColor
        cell.images?.clipsToBounds = true
    } else {
        cell.images?.layer.borderWidth = 0.0
        cell.images?.layer.borderColor = UIColor.clear.cgColor
        cell.images?.clipsToBounds = false
    }
    print(data1[collectionView.tag].sectionType)
    let doggie = getImg(Inb: String(substring))
    if doggie == "" {
        let strIcon = "CatIcons/" + String(substring) + ".png"
        myIcon?.image = UIImage(named: strIcon)
    } else {
        myIcon?.image = UIImage(systemName: doggie)
    }
    if myLbl?.text == "General" || data1[collectionView.tag].sectionType == "Free Today" {
        padlock?.isHidden = true
    } else {
        padlock?.isHidden = false
    }
    return cell
}

func getImg(Inb: String) -> String {
    var retVal: String
    switch Inb {
    case "General":
        retVal = "rainbow"
    case "Hope":
        retVal = "sunrise"
    case "Sleep":
        retVal = "bed.double.circle"
    case "Mindfulness":
        retVal = "brain.head.profile"
    case "Enjoy the Moment":
        retVal = "figure.2"
    case "Calm":
        retVal = "figure.yoga"
    default:
        retVal = ""
    }
    return retVal
}

}


Solution

  • One possible issue in your code arises from the management of cell reuse and data synchronization between the table view and the embedded collection views, synchronizing the collection view's data by correctly setting its with a tag and reloading the collection view data in the cellForRowAt method of your table view data source

    Something like this:

        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let cell = tableView.dequeueReusableCell(withIdentifier: "cell1", for: indexPath) as! CategoriesTableViewCell
            /* 
            Set the collection view tag to match the section index; 
            the tag will later be used to ensure 
            the correct data is loaded for each collection view. 
            */
            cell.collectionView.tag = indexPath.section
            cell.collectionView.reloadData() 
        
            // Continue your code
        }
    

    Then you can use the tags

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        // Use collectionView.tag to access the correct section of your data model.
        return data1[collectionView.tag].quotes.count
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell1", for: indexPath) as! CategoriesCollectionViewCell
        // Configure the cell using data1[collectionView.tag] to get the correct data for this collection view
        let quote = data1[collectionView.tag].quotes[indexPath.row]
        // Your code continues here
    }
    

    This is far from the best solution, but should work.