Search code examples
iosswiftuitableviewuisegmentedcontrol

How can I make a segmented control show different numbers of cells?


I am making a screen for a program schedule for two days. I’ve got a ViewController with the following layout:

NavigationBar - SearchBar - Segmented control - TableView.

In a separate file UITableViewCell I draw a custom cell. The main logic in my VC:

struct Schedule {
    var time: String
    var title: String
}

struct SectionForDay {
    let sectionTitle: String
    var dayProgram: [Schedule]
}

class ProgramViewController: UIViewController {

    var tableView = UITableView()
    let identifier = "Cell"

    var dayOne = [
      Schedule(time: "10:00 - 11:00", title: "DayOne SessionOne"), 
      Schedule(time: "11:00 - 12:00", title: "DayOne SessionTwo")
    ]

    var dayTwo = [
      Schedule(time: "22:00 - 23:00", title: "DayTwo SessionThree"), 
      Schedule(time: "24:00 - 01:00", title: "DayTwo SessionFour")
    ]

    var sections = [SectionForDay]()

    let segmentedControl: UISegmentedControl = {
        let sc = UISegmentedControl(items: ["All", "Day 1", "Day 2"])
        sc.selectedSegmentIndex = 0
        sc.addTarget(self, action: #selector(handleSegmentedChange), for: .valueChanged)
        return sc
    }()

   @objc func handleSegmentedChange() {
        switch segmentedControl.selectedSegmentIndex {
        case 0:
            dayToDisplay = dayOne + dayTwo
        case 1:
            dayToDisplay = dayOne
        default:
            dayToDisplay = dayTwo
        }
        tableView.reloadData()
    }

    lazy var dayToDisplay = dayOne + dayTwo

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.delegate = self
        tableView.dataSource = self
        tableView.register(ProgramCell.self, forCellReuseIdentifier: identifier)
        sections = [
        SectionForDay(sectionTitle: "Day 1", dayProgram: dayOne),
        SectionForDay(sectionTitle: "Day 2", dayProgram: dayTwo)
        ]
    }

extension ProgramViewController: UITableViewDelegate, UITableViewDataSource {

    func numberOfSections(in tableView: UITableView) -> Int {
        return self.sections.count
    }

    func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        return self.sections[section].sectionTitle
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
                let items = self.sections[section].dayProgram
                return items.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: identifier, for: indexPath) as! ProgramCell
        let items = self.sections[indexPath.section].dayProgram
        let currentDay = items[indexPath.row]
        cell.dateLabel.text = currentDay.time
        cell.titleLabel.text = currentDay.title
        return cell
    }   
}

I tried several methods, but still can’t make the segmented control switch the way, so that in All it shows both two days with their section headers, Day 1 - only day one program with its section header, Day 2 - only day two program with its section header. Can anybody give me a hint of what to do? Maybe I should change the whole model?

Image:

Image

When I toggle the segmented control between 3 items it always shows two days.


Solution

  • You need to update your sections array when the segmented control value changes.

    @objc func handleSegmentedChange() {
        switch segmentedControl.selectedSegmentIndex {
        case 0:
            sections = [
                SectionForDay(sectionTitle: "Day 1", dayProgram: dayOne),
                SectionForDay(sectionTitle: "Day 2", dayProgram: dayTwo),
            ]
        case 1:
            sections = [
                SectionForDay(sectionTitle: "Day 1", dayProgram: dayOne),
            ]
        default:
            sections = [
                SectionForDay(sectionTitle: "Day 2", dayProgram: dayTwo),
            ]
        }
        tableView.reloadData()
    }