-1

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

}

1
  • This is because your table view cell is a reusable cell and you are assigning collection view a tag to use it later on to access the data, best will be to assign the data to collection view and perform reload data in the cell for row method. Hope this will be helpful. Commented Apr 5, 2024 at 4:14

1 Answer 1

1

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.

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you very much! Missing that one line certainly fixed my issue. Thanks.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.