Search code examples
arraysswiftuitableviewuicollectionviewreloaddata

Reloading TableViewCell inside CollectionViewCell


I tried viewWillAppear, tableView.reloadData(), and collectionView.reloadData() but the data array never seems to show up

import UIKit

class yearOne: UICollectionViewController, coursesDelegate {

    let customCellIdentifier = "cellID"
    let customCellIdentifier2 = "cellID2"
    let customCellIdentifier3 = "cellID3"
    let customCellIdentifier4 = "cellID4"

    let quarters = [
        customLabel (title: "Fall Quarter"),
        customLabel (title: "Winter Quarter"),
        customLabel (title: "Spring Quarter"),
        customLabel (title: "Summer Quarter")
    ]


    func sendDataBackFall(data: String) {
        fallQuarterCell.data.append(data)
        //UserDefaults.standard.set(fallQuarterCell.data, forKey: "SavedArray")
    }
    func sendDataBackWinter(data2: String) {
        winterQuarterCell.data.append(data2)
        //UserDefaults.standard.set(winterQuarterCell.data, forKey: "SavedArray")
    }
    func sendDataBackSpring(data3: String) {
        springQuarterCell.data.append(data3)
        //UserDefaults.standard.set(springQuarterCell.data, forKey: "SavedArray")
    }
    func sendDataBackSummer(data4: String) {
        summerQuarterCell.data.append(data4)
        //UserDefaults.standard.set(summerQuarterCell.data, forKey: "SavedArray")
    }

    let vc = fallQuarterCell()

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        print("1")
        print(fallQuarterCell.data)
        vc.tableView.reloadData()
        collectionView.reloadData()
        print("2")
        print(fallQuarterCell.data)
    }


    override func viewDidLoad() {
        super.viewDidLoad()

        collectionView.dataSource = self
        collectionView.delegate = self

        self.collectionView!.register(fallQuarterCell.self, forCellWithReuseIdentifier: customCellIdentifier)
        self.collectionView!.register(winterQuarterCell.self, forCellWithReuseIdentifier: customCellIdentifier2)
        self.collectionView!.register(springQuarterCell.self, forCellWithReuseIdentifier: customCellIdentifier3)
        self.collectionView!.register(summerQuarterCell.self, forCellWithReuseIdentifier: customCellIdentifier4)

        navigationItem.title = "Year One"
        navigationController?.navigationBar.prefersLargeTitles = true
        collectionView?.backgroundColor = .lightGray
        //navigationItem.prompt = "Click the + button to add courses, Swipe left on a course to delete."

    }


    override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return quarters.count
    }

    override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

        if (indexPath.row == 0){
            let cell1 = collectionView.dequeueReusableCell(withReuseIdentifier: customCellIdentifier, for: indexPath) as! fallQuarterCell
            cell1.layer.borderColor = UIColor.orange.cgColor
            cell1.layer.borderWidth = 2
            cell1.layer.cornerRadius = 5
            cell1.quarters = self.quarters[0]
            return cell1
        }
        else if (indexPath.row == 1){
            let cell2 = collectionView.dequeueReusableCell(withReuseIdentifier: customCellIdentifier2, for: indexPath) as! winterQuarterCell
            cell2.layer.borderColor = UIColor.blue.cgColor
            cell2.layer.borderWidth = 2
            cell2.layer.cornerRadius = 5
            cell2.quarters = self.quarters[1]
            return cell2
        }
        else if (indexPath.row == 2){
            let cell3 = collectionView.dequeueReusableCell(withReuseIdentifier: customCellIdentifier3, for: indexPath) as! springQuarterCell
            cell3.layer.borderColor = UIColor.green.cgColor
            cell3.layer.borderWidth = 2
            cell3.layer.cornerRadius = 5
            cell3.quarters = self.quarters[2]
            return cell3
        }
        else if (indexPath.row == 3){
            let cell4 = collectionView.dequeueReusableCell(withReuseIdentifier: customCellIdentifier4, for: indexPath) as! summerQuarterCell
            cell4.layer.borderColor = UIColor.red.cgColor
            cell4.layer.borderWidth = 2
            cell4.layer.cornerRadius = 5
            cell4.quarters = self.quarters[3]
            return cell4
        }
        else{
            return UICollectionViewCell()
        }
    }

    @objc func buttonAction(sender: UIButton!) {
        switch sender.tag {
        case 0:
            let destination = SearchPage()
            destination.delegate = self
            destination.tag = sender.tag
            navigationController?.pushViewController(destination, animated: true)
        case 1:
            let destination = SearchPage()
            destination.delegate = self
            destination.tag = sender.tag
            navigationController?.pushViewController(destination, animated: true)
        case 2:
            let destination = SearchPage()
            destination.delegate = self
            destination.tag = sender.tag
            navigationController?.pushViewController(destination, animated: true)
        case 3:
            let destination = SearchPage()
            destination.delegate = self
            destination.tag = sender.tag
            navigationController?.pushViewController(destination, animated: true)
        default:
            let destination = SearchPage()
            destination.delegate = self
            destination.tag = sender.tag
            navigationController?.pushViewController(destination, animated: true)
        }

    }
}

extension yearOne : UICollectionViewDelegateFlowLayout{

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        let width = (view.frame.width - 30)
        return CGSize(width: width, height: 200)
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
        return 8
    }
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
        return 1
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
        UIEdgeInsets(top: 30, left: 10, bottom: 30, right: 10)
    }

}


class fallQuarterCell: UICollectionViewCell, UITableViewDelegate, UITableViewDataSource {

//    var data = UserDefaults.standard.object(forKey: "SavedArray") as? [String] ?? [String](){
//        didSet{
//            self.tableView.reloadData()
//        }
//    }

    static var data: [String] = []

//    func load(){
//        if let loadeddata: [String] = UserDefaults.standard.object(forKey: "SavedArray") as? [String] {
//            data = loadeddata
//            tableView.reloadData()
//        }
//    }

    let cellId = "coursesName"

    let tableView:UITableView = {
        let tableView = UITableView()
        tableView.translatesAutoresizingMaskIntoConstraints = false
        tableView.backgroundColor = UIColor.white
        return tableView
    }()

    override init(frame: CGRect){
        super.init(frame: frame)
        addSubview(tableView)
        setupView()
    }

    func setupView(){

        tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellId)
        tableView.delegate = self
        tableView.dataSource = self

        self.backgroundColor = UIColor.white
        contentView.addSubview(quarterLabel)
        contentView.addSubview(addButton)

        quarterLabel.translatesAutoresizingMaskIntoConstraints = false
        quarterLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 10).isActive = true
        quarterLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 10).isActive = true

        addButton.translatesAutoresizingMaskIntoConstraints = false
        addButton.topAnchor.constraint(equalTo: quarterLabel.topAnchor, constant: -5).isActive = true
        addButton.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -10).isActive = true
        addButton.heightAnchor.constraint(equalToConstant: 25).isActive = true
        addButton.widthAnchor.constraint(equalToConstant: 25).isActive = true

        tableView.translatesAutoresizingMaskIntoConstraints = false
        tableView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 35).isActive = true
        tableView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 5).isActive = true
        tableView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -10).isActive = true
        tableView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -5).isActive = true

    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return fallQuarterCell.data.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath)
        let stringLabel = fallQuarterCell.data[indexPath.row]
        cell.textLabel?.text = stringLabel
        return cell
    }

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 40
    }

    func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
        return true
    }

    func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
        if (editingStyle == .delete) {
            if (indexPath.row == 0){
                fallQuarterCell.data.remove(at: 0)
                self.tableView.reloadData()
                //UserDefaults.standard.set(fallQuarterCell.data, forKey: "SavedArray")
            }
            else if (indexPath.row == 1){
                fallQuarterCell.data.remove(at: 1)
                self.tableView.reloadData()
                //UserDefaults.standard.set(fallQuarterCell.data, forKey: "SavedArray")
            }
            else if (indexPath.row == 2){
                fallQuarterCell.data.remove(at: 2)
                self.tableView.reloadData()
                //UserDefaults.standard.set(fallQuarterCell.data, forKey: "SavedArray")
            }
            else if (indexPath.row == 3){
                fallQuarterCell.data.remove(at: 3)
                self.tableView.reloadData()
                //UserDefaults.standard.set(fallQuarterCell.data, forKey: "SavedArray")
            }
            else if (indexPath.row == 4){
                fallQuarterCell.data.remove(at: 4)
                self.tableView.reloadData()
                //UserDefaults.standard.set(fallQuarterCell.data, forKey: "SavedArray")
            }
        }
    }

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)

        //add class information???
    }

    var quarters: customLabel? {
        didSet {
            guard let quarters = quarters else {return}
            quarterLabel.text = quarters.title
        }
    }

    let quarterLabel : UILabel = {
        let label = UILabel()//frame: CGRect(x: 15, y: -75, width: 300, height: 50))
        label.translatesAutoresizingMaskIntoConstraints = false
        label.textColor = UIColor.black
        label.font = UIFont.boldSystemFont(ofSize: 16)
        //label.textAlignment = .center
        return label
    }()

    let addButton : UIButton = {
        let button = UIButton()//frame: CGRect(x: 345, y: 10, width: 30, height: 30))
        button.setImage(UIImage(named: "addicon"), for: .normal)
        button.imageEdgeInsets = UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5)
        button.tag = 0
        button.addTarget(self, action: #selector(yearOne.buttonAction), for: .touchUpInside)
        return button
    }()


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

}

I also have the

class winterQuarterCell: UICollectionViewCell, UITableViewDelegate, UITableViewDataSource

class springQuarterCell: UICollectionViewCell, UITableViewDelegate, UITableViewDataSource

class summerQuarterCell: UICollectionViewCell, UITableViewDelegate, UITableViewDataSource

similar to that of fallQuarterCell(). I used the print(fallQuarterCell.data) to see whether the delegation works and data string appends, and it does seem to append to the array, just that it won't appear on the fallQuarterCell tableView when backed out from the SearchPage()

Here is the 'SearchPage()where the user clicks on an element from the tableView to append to thefallQuarterCell.datathrough delegation and automatically pop backs toyearOne()`:

import UIKit

protocol coursesDelegate {
    func sendDataBackFall(data: String)
    func sendDataBackWinter(data2: String)
    func sendDataBackSpring(data3: String)
    func sendDataBackSummer(data4: String)
}

class SearchPage: UITableViewController {

    var delegate: coursesDelegate?

    let cellId = "course"

    var allCourses : NSArray = NSArray()
    var filteredCourses = [String]()
    var resultSearchController = UISearchController()

    var tag: Int?

    @objc func addTapped(sender: UIBarButtonItem!) {
        let addAlert = UIAlertController(title: "Create New Course", message: "Enter a course name", preferredStyle: .alert)

        addAlert.addTextField {(textfield:UITextField) in textfield.placeholder = "Course"}

        addAlert.addAction(UIAlertAction(title: "Create", style: .default, handler: { (action:UIAlertAction) in
            var _customCourse = String()

            if let courseTextField = addAlert.textFields?.first, let customCourse = courseTextField.text{
                _customCourse = customCourse
            }

        }))

        addAlert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))

        self.present(addAlert, animated: true, completion: nil)
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        let courses : CourseList = CourseList()
        allCourses = courses.coursesList

        navigationItem.title = "Select Courses"
        navigationController?.navigationBar.prefersLargeTitles = true

        let button1 = UIBarButtonItem(title: "Add Course", style: .plain, target: self, action: #selector(addTapped))
        self.navigationItem.rightBarButtonItem = button1


        self.view.backgroundColor = .systemBackground

        tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellId)
        tableView.delegate = self
        tableView.dataSource = self

        self.tableView.tableFooterView = UIView()

        resultSearchController = ({
            let controller = UISearchController(searchResultsController: nil)
            controller.searchResultsUpdater = self
            controller.obscuresBackgroundDuringPresentation = false
            controller.searchBar.placeholder = "Search Courses"
            controller.searchBar.sizeToFit()

            tableView.tableHeaderView = controller.searchBar

            return controller
        })()

    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        if  (resultSearchController.isActive) {
            return filteredCourses.count
        }
        else{
            return allCourses.count
        }
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath)

        if (resultSearchController.isActive) {
            cell.textLabel?.text = filteredCourses[indexPath.row]
            return cell
         }
         else {
            let courses = self.allCourses[indexPath.row]
            cell.textLabel?.text = courses as? String
            return cell
         }
    }

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath){

        tableView.deselectRow(at: indexPath, animated: true)

        if (tag == 0){
            if (resultSearchController.isActive){
//                fallQuarterCelll.data.append(filteredCourses[indexPath.row])
//                UserDefaults.standard.set(fallQuarterCelll.data, forKey: "SavedArray")
//                self.navigationController?.popViewController(animated: true)
                let data = filteredCourses[indexPath.row]
                delegate?.sendDataBackFall(data: data)
                self.navigationController?.popViewController(animated: true)

            }
            else{
//                fallQuarterCelll.data.append(allCourses[indexPath.row] as! String)
//                UserDefaults.standard.set(fallQuarterCelll.data, forKey: "SavedArray")
//                self.navigationController?.popViewController(animated: true)
                let data = allCourses[indexPath.row] as! String
                delegate?.sendDataBackFall(data: data)
                self.navigationController?.popViewController(animated: true)

            }
        }
    }

    var isSearchBarEmpty: Bool {
      return resultSearchController.searchBar.text?.isEmpty ?? true
    }
}

extension SearchPage: UISearchResultsUpdating {

  func updateSearchResults(for searchController: UISearchController) {
      filteredCourses.removeAll(keepingCapacity: false)

      let searchPredicate = NSPredicate(format: "SELF CONTAINS[c] %@", searchController.searchBar.text!)
      let array = (allCourses as NSArray).filtered(using: searchPredicate)
      filteredCourses = array as! [String]

      self.tableView.reloadData()
  }

}

Ignore the UsersDefault... I'm trying to make the data appear on the fallQuarterCell tableView first.


Solution

  • You don't reload table view in fallQuarterCell.

    let vc = fallQuarterCell() creates a new cell. Your collection view doesn't contain it, so vc.tableView.reloadData() does nothing.

    When you use collectionView.dequeueReusableCell, you may get the old cell, so you should reload the table view in this cell. You should read documentation:

    Call this method from your data source object when asked to provide a new cell for the collection view. This method dequeues an existing cell if one is available or creates a new one

    For example, you can add func updateViews() in fallQuarterCell:

    func updateViews() {
        tableView.reloadData()
    }
    

    And call it here:

    if (indexPath.row == 0){
        let cell1 = collectionView.dequeueReusableCell(withReuseIdentifier: customCellIdentifier, for: indexPath) as! fallQuarterCell
        cell1.layer.borderColor = UIColor.orange.cgColor
        cell1.layer.borderWidth = 2
        cell1.layer.cornerRadius = 5
        cell1.quarters = self.quarters[0]
        cell1.updateViews()
        return cell1
    }