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

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:
When I toggle the segmented control between 3 items it always shows two days.

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()
}

Related

UITableView with Sections Repeating cell data in all the sections

I Know same question has been asked before , but it did not help.
I am trying to add section titles to my UITableView. I am able to create the sections and count the number of elements in each section properly, , the cells are repeated the data in all the sections.
I am posting only relevant code -
My model is -
import UIKit
struct Product:Equatable {
let imagename: UIImage }
var productarray = [Product(imagename:#imageLiteral(resourceName: "CakeImage")),
Product( imagename:#imageLiteral(resourceName: "PeasImge")),Product(imagename:#imageLiteral(resourceName: "vectorlogo")),
Product(imagename: #imageLiteral(resourceName: "blue"))]
The ProductViewController is -
import UIKit
class ProductViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
let sections = ["Section A", "Section B","Section C", "Section D"]
let rowspersection = [1, 1,1,1]
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
}
func numberOfSections(in tableView: UITableView) -> Int {
return sections.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return rowspersection[section]
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let data = productarray[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "ProductTableViewCell") as! ProductTableViewCell
cell.imageView?.image = data.imagename
cell.myParent = self
return cell
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 44
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
switch(section) {
case 0:return "Section A"
case 1:return "Section B"
case 2:return "Section C"
case 3 :return "Section D"
default :return ""
}
}
}
Now, in the above only the first image of the productarray i.e. "[Product(imagename:#imageLiteral(resourceName: "CakeImage"))," is repeated in all the sections as shown in the image below:-
I want all the images/cell to be in the respective sections and not just one image/cell to be repeated in all the sections.
Any help will be appreciated.
Reason:
Since you've a single row in each section, everytime you use indexPath.row which is 0 for the 1st row, you end up accessing productarray[0] for each row in every section.
That's the reason all the rows are same because all of them are filled with productarray[0].
Solution:
Simply use indexPath.section instead of indexPath.row
let data = productarray[indexPath.section]
Note: Instead of making 3 different arrays, you can create a single array of custom type and use that as the dataSource of your tableView. Example:
struct Section {
let name: String
let products: [Product]
}
let sections = [
Section(name: "Section A", products: [Product(imagename:#imageLiteral(resourceName: "CakeImage"))]),
Section(name: "Section B", products: [Product(imagename:#imageLiteral(resourceName: "PeasImge"))]),
Section(name: "Section C", products: [Product(imagename:#imageLiteral(resourceName: "vectorlogo"))]),
Section(name: "Section D", products: [Product(imagename:#imageLiteral(resourceName: "blue"))]),
]
Use sections array as the dataSource. This will avoid a lot of confusion.
in every section indexPath start with zero so it display first index of productarray.
let data = productarray[indexPath.row]
replace with
let data = productarray[indexPath.row + indexPath.section]
Edit
var index = indexPath.row
if indexPath.section != 0, rowspersection.count > indexPath.section - 1{
index += rowspersection[indexPath.section - 1]
}
if index < productarray.count{
let data = productarray[index]
cell.imageView?.image = data.imagename
}
Edit Checkout
Replace this methods
func updateCart(cell: ProductTableViewCell) {
guard let indexPath = tableView.indexPath(for: cell) else { return }
var index = indexPath.row
if indexPath.section != 0, rowspersection.count > indexPath.section - 1{
index += rowspersection[indexPath.section - 1]
}
if index < productarray.count{
let product = productarray[index]
//Update Cart with product
cart.updateCart(with: product)
self.navigationItem.rightBarButtonItem?.title = "Checkout (\(cart.items.count))"
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ProductTableViewCell") as! ProductTableViewCell
cell.delegate = self // original issue was here, now resolved.
var index = indexPath.row
if indexPath.section != 0, rowspersection.count > indexPath.section - 1{
index += rowspersection[indexPath.section - 1]
}
if index < productarray.count{
let data = productarray[index]
cell.name?.text = data.name
cell.imageView?.image = data.imagename
let product = productarray[index]
cell.setButton(state: self.cart.contains(product: product))
}
return cell
}

How to link delegate and datasource to multiple tableviews in one ViewController?

I created two UITableView inside a UIViewController. To link to the delegate and datasource I used the basic method, holding Ctrl and dragging to the "yellow ball" above. However, I can only do this with one, when each UITableView should separately have its own database in the class. When I do the same with the second UITableView, it apparently links the same datasource and delegate to both, without extracting the data from the class to the second and displaying them on the screen.
How can I fix that?
//I declared four buttons, two in each table view.
#IBOutlet weak var btnDrop: UIButton!
#IBOutlet weak var tblView: UITableView!
#IBOutlet weak var btnDropProj: UIButton!
#IBOutlet weak var tblViewProj: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tblView.isHidden = true
tblViewProj.isHidden = true
}
var selectClient = ["Cliente 1", "Cliente 2", "Cliente 3", "Cliente 4", "Cliente 5", "Cliente 6", "Cliente 7", "Cliente 8"]
var selectProject = ["Projeto 1", "Projeto 2", "Projeto 3", "Projeto 4", "Projeto 5", "Projeto 6", "Projeto 7", "Projeto 8"]
It seems to me that you are trying to link your table views to 2 different data sources.
You can do that by first creating 2 separate data sources like below.
class Table1DataSource: NSObject, UITableViewDataSource {
// Properties
private var table1Data: [String]
init(table1Data: [String]) {
self.table1Data = table1Data
super.init()
}
// MARK: - Data Source
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.table1Data.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var profileCell: ProfileCell
let cell = tableView.dequeueReusableCell(withIdentifier: Table1Cell.reuseIdentifier, for: indexPath) as! Table1Cell
// Initialize the cell here
return cell
}
}
After that you can link the data source to your table view in your controller.
class MainController: UIViewController {
// Outlets
#IBOutlet weak var tblView: UITableView!
#IBOutlet weak var tblViewProj: UITableView!
// Properties
var selectClient = ["Cliente 1", "Cliente 2", "Cliente 3", "Cliente 4", "Cliente 5", "Cliente 6", "Cliente 7", "Cliente 8"]
var selectProject = ["Projeto 1", "Projeto 2", "Projeto 3", "Projeto 4", "Projeto 5", "Projeto 6", "Projeto 7", "Projeto 8"]
// DataSource
lazy var tblViewDataSource: Table1DataSource = {
return Table1DataSource(table1Data: self.selectClient)
}()
override func viewDidLoad() {
super.viewDidLoad()
self.tblView.dataSource = self.tblViewDataSource
}
}
All that's left to do is to repeat the steps to link your second table.
Hope it helps!
// MARK: - UITableView datasource
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if tableView == self.tblView {
return selectClient.count
}
else {
return selectProject.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
if tableView == self.tblView {
let CellIdentifier: String = "YOURCUSTOMCELL"
var cell: YOURCUSTOMCELL? = (tableView.dequeueReusableCell(withIdentifier: CellIdentifier) as? YOURCUSTOMCELL)
if cell == nil {
let topLevelObjects: [Any] = Bundle.main.loadNibNamed("YOURCUSTOMCELL", owner: nil, options: nil)!
cell = (topLevelObjects[0] as? YOURCUSTOMCELL)
cell?.selectionStyle = .none
}
cell?.backgroundColor = UIColor.white
return cell!
}
else {
let CellIdentifier: String = "YOURCUSTOMCELL"
var cell: YOURCUSTOMCELL? = (tableView.dequeueReusableCell(withIdentifier: CellIdentifier) as? YOURCUSTOMCELL)
if cell == nil {
let topLevelObjects: [Any] = Bundle.main.loadNibNamed("YOURCUSTOMCELL", owner: nil, options: nil)!
cell = (topLevelObjects[0] as? YOURCUSTOMCELL)
cell?.selectionStyle = .none
}
cell?.backgroundColor = UIColor.white
return cell!
}
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
if tableView == self.tblView {
return 50.0
}
else {
return 50.0
}
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if tableView == self.tblView {
}
else {
}
}

How can I access a var inside of a structure to count the number of items listed in the var's array?

I need help being able to display in a string format within the each sections title of the table view the number of listed (Cells) inside the sectionData array. Which the sectionData is listed as a var inside of a structure(cellData).
import UIKit
struct cellData
{
var opened = Bool()
var title = String()
var sectionData = [String]()
}
class TableViewController: UITableViewController {
var tableViewData = [cellData]()
override func viewDidLoad()
{
super.viewDidLoad()
tableViewData = [cellData(opened: false, title: "Monday, September 10, 2018", sectionData: ["Cell1", "Cell2", "Cell3"]),
cellData(opened: false, title: "Tuesday, September 11, 2018", sectionData: ["Cell1", "Cell2", "Cell3"]),
cellData(opened: false, title: "Wednesday, September 12, 2018", sectionData: ["Cell1", "Cell2", "Cell3"]),
cellData(opened: false, title: "Thursday, September 13, 2018", sectionData: ["Cell1", "Cell2", "Cell3"]),
cellData(opened: false, title: "Friday, September 14, 2018", sectionData: ["Cell1", "Cell2", "Cell3"])]
}
override func numberOfSections(in tableView: UITableView) -> Int
{
return tableViewData.count
}
//
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
if tableViewData[section].opened == true
{
return tableViewData[section].sectionData.count + 1
}
else
{
return 1
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let dataIndex = indexPath.row - 1
if indexPath.row == 0
{
guard let cell = tableView.dequeueReusableCell(withIdentifier: "cell") else {return UITableViewCell()}
cell.textLabel?.text = tableViewData[indexPath.section].title
return cell
}
else
{
guard let cell = tableView.dequeueReusableCell(withIdentifier: "cell") else {return UITableViewCell()}
cell.textLabel?.text = tableViewData[indexPath.section].sectionData[dataIndex]
return cell
}
}
//
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
if tableViewData[indexPath.section].opened == true
{
tableViewData[indexPath.section].opened = false
let sections = IndexSet.init(integer: indexPath.section)
tableView.reloadSections(sections, with: .none)// play around with this
}
else
{
tableViewData[indexPath.section].opened = true
let sections = IndexSet.init(integer: indexPath.section)
tableView.reloadSections(sections, with: .none)// play around with this
}
}
//
}
Swift 4: Structured array to display in tableView
iPhoneX Build: Display of Sections in tableview
iPhoneX Build: Display of Sections and Cells in tableView when clicked
This is the easiest way i can think of. You would need two extra variables - one for getting the count, another for the count appended title. If you don't mind building the string to be displayed yourself, you can skip the second one.
struct cellData {
var opened = Bool()
var title = String()
var sectionData = [String]()
var count: Int {
get {
return sectionData.count
}
}
// This variable is for convenience
var titleWithCount: String {
get {
return "\(title) (\(count) Cells)" // Format it as you require
}
}
}
Use the titleWithCount variable when populating the section title.

Expanding the expandable table View cells to further extent

I created the Expandable table view cells which is like Below mentioned image.
Library used is JExpandableTableView.
Code for Creating This ExpandableTable View is given below :
Model For Sections:
class SectionInfo: NSObject {
var cells = [CellInfo]()
var CategoryName: String
var CategoryCount: String
var CategoryImage: UIImage
init(_ text: String,SubCount: String, Image: UIImage ) {
self.CategoryName = text
self.CategoryCount = SubCount
self.CategoryImage = Image
}
}
Model For SubCategoryCell:
class CellInfo: NSObject {
var SubcategoryName: String!
var SubcategoryCount: String!
init(_ SubName: String, SubCount: String) {
self.SubcategoryName = SubName
self.SubcategoryCount = SubCount
}
}
View Controller :
class CategoryVC: BaseVC,JExpandableTableViewDataSource,JExpandableTableViewDelegate{
#IBOutlet weak var tblViewCategory: JExpandableTableView!
var tableViewData = [SectionInfo]()
var expandedIndexPath: IndexPath?
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Category"
self.tblViewCategory.dataSource = self
self.tblViewCategory.delegate = self
self.tblViewCategory.keepPreviousCellExpanded = false
self.LoadData()
}
func LoadData() {
var cellInfo = CellInfo("SubCategory 1",SubCount: "10")
let sec1 = SectionInfo("Category 1", SubCount: "5", Image: UIImage(named: "Category3")!)
sec1.cells.append(cellInfo)
let sec2 = SectionInfo("Category 2", SubCount: "8", Image: UIImage(named: "Category3")!)
cellInfo = CellInfo("SubCategory 2",SubCount: "20")
sec2.cells.append(cellInfo)
cellInfo = CellInfo("SubCategory 2.1",SubCount: "30")
sec2.cells.append(cellInfo)
let sec3 = SectionInfo("Category 3", SubCount: "10", Image: UIImage(named: "Category3")!)
cellInfo = CellInfo("SubCategory 3",SubCount: "30")
sec3.cells.append(cellInfo)
tableViewData.append(sec1)
tableViewData.append(sec2)
tableViewData.append(sec3)
let celNib = UINib.init(nibName: "SubCategoryCell", bundle: nil)
tblViewCategory.register(celNib, forCellReuseIdentifier: "SubCategoryCell")
let headerNib = UINib.init(nibName: "HeaderView", bundle: nil)
tblViewCategory.register(headerNib, forHeaderFooterViewReuseIdentifier: "HeaderView")
}
#IBAction func DrawerMenutap(_ sender: Any) {
self.OpenDrawerMenu()
}
func tableView(_ tableView: JExpandableTableView, numberOfRowsInSection section: Int, callback: #escaping (Int) -> Void) {
let sectionInfo = self.tableViewData[section]
callback(sectionInfo.cells.count)
}
func tableView(_ tableView: JExpandableTableView, heightForHeaderInSection section: Int) -> CGFloat {
return 150
}
func tableView(_ tableView: JExpandableTableView, heightForRowAtIndexPath indexPath: IndexPath) -> CGFloat {
return 44
}
func tableView(_ tableView: JExpandableTableView, initialNumberOfRowsInSection section: Int) -> Int {
// let sectionInfo = self.tableViewData[section]
return 0
}
func numberOfSections(in tableView: JExpandableTableView) -> Int {
return tableViewData.count
}
func tableView(_ tableView: JExpandableTableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let section = tableViewData[indexPath.section]
let row = section.cells[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "SubCategoryCell", for: indexPath) as! SubCategoryCell
cell.contentView.backgroundColor = UIColor.white
cell.lblSubCategoryName.text = row.SubcategoryName
return cell
}
func tableView(_ tableView: JExpandableTableView, viewForHeaderInSection section: Int) -> UIView? {
let section = self.tableViewData[section]
let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: "HeaderView") as! HeaderView
header.contentView.backgroundColor = UIColor.groupTableViewBackground
header.CatName.text = section.CategoryName
header.CatImgView.image = UIImage(named: "Category4")
header.CatCount.text = section.CategoryCount
return header
}
}
//MARK: Table View Cell for Category
class CatCell: UITableViewCell {
#IBOutlet weak var lblName: UILabel!
}
My further requirement is I want to expand the Cells (Subcategory 2, SubCategory 2.1) in order to accommodate SubSubCategory(Childrens of SubCategory) in Case if they Exist. So what should be the approach for achieving this.
UITableView is really designed in a way to show two levels, sections and rows.
But to show more then two levels you can manipulate rows that will increase/expand or decrease/collapse according to your model for Section, SubCategory.
So table structure will look like that
section_header_1
subCategory_1.0
subSubCategory_1.0.1
subCategory_1.1
subSubCategory_1.1.1
subCategory_1.2
subSubCategory_1.2.1
section_header_2
subCategory_2.0
subSubCategory_2.0.1
subCategory_2.1
subSubCategory_2.1.1
subCategory_2.2
subSubCategory_2.2.1
For Header you have to make your own custom header row and put that as the first row of each section. You could set up a cell to LOOK like a header, and setup the tableView:didSelectRowAt to manually expand or collapse the section, subCategory or SubSubCategory it is in. the rows after first row will be your subCategory and subSubCategory.
Then a Model For Section, SubCategory and SubSubCategory to store a booleans corresponding the the "expend" value of each of your sections, subCategories. you can avoid SubSubCategory model if it's only store it's name but it's easy to understand if you do so. for an example a struct for holding Section, SubCategory "expend" booleans.
public struct Section {
var name: String
var expand: Bool
var subCategory:[SubCategory]
public init(name: String, expand: Bool = false ,subCategory: [SubCategory]) {
self.name = name
self.expand = expand
self.subCategory = subCategory
}
}
public struct SubCategory {
var name: String
var expand: Bool
var subSubCategory: SubSubCategory
public init(name: String, expand: Bool = false, subSubCategory: SubSubCategory) {
self.name = name
self.expand = expand
self.subSubCategory = subSubCategory
}
}
public struct SubSubCategory {
var name: String
public init(name: String) {
self.name = name
}
}
Create 3 Custom cell one for Header other for SubCategory and SubSubCategory and display it in the first row of every section Header Cell and after expand or collapse show your SubCategory or SubSubCategory Cell accordingly.
after all together your code should be look that that.
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
var sampleData: [Section] = [
Section(name: "Category 1", expand: false,
subCategory: [
SubCategory(name: "Category 1.1", expand: false, subSubCategory: SubSubCategory(name: "SubSubCategory 1.1.1")),
SubCategory(name: "Category 1.2", expand: false, subSubCategory: SubSubCategory(name: "SubSubCategory 1.2.1"))
]
),
Section(name: "Category 2", expand: false,
subCategory: [
SubCategory(name: "Category 2.1", expand: false, subSubCategory: SubSubCategory(name: "SubSubCategory 2.1.1")),
SubCategory(name: "Category 2.2", expand: false, subSubCategory: SubSubCategory(name: "SubSubCategory 2.2.1"))
]
)
]
override func viewDidLoad() {
super.viewDidLoad()
}
//
// MARK: - View Controller DataSource and Delegate
//
func numberOfSections(in tableView: UITableView) -> Int {
return sampleData.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
var expandCount = 0
if sampleData[section].expand {
// if header is expanded all subCategory will be also expanded
expandCount = sampleData[section].subCategory.count
for subCategory in sampleData[section].subCategory{
//check for how many subSubCategory is expanded
if subCategory.expand{
expandCount += 1
}
}
}
// returning the count of total expanded SubCategories and SubSubCategories
// 1 is for header you can remove if you are using `viewForHeaderInSection`
return 1 + expandCount
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Header cell
if indexPath.row == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "header")!
return cell
}else{
var countValue = 0
var indexSubCategory = 0
let sampleDataSection = sampleData[indexPath.section]
// check for how many "subCategory" expanded or collapsed
if sampleDataSection.expand{
for (index, subCategory) in sampleDataSection.subCategory.enumerated(){
countValue += 1
if countValue >= indexPath.row{
indexSubCategory = index
break
}
// check for how many "subSubCategory" expanded or collapsed
if subCategory.expand{
if index == sampleDataSection.subCategory.count-1{
countValue += 2
indexSubCategory = index + 1
}else{
countValue += 1
}
}
}
// if countValue is grater then indexPath.row it will return "subSubCategory" cell
// else/countValue = indexPath.row then return "subCategory" cell
if countValue > indexPath.row{
// Cell subSubCategory
let cell = tableView.dequeueReusableCell(withIdentifier: "subSubCategory")!
cell.textLabel?.text = self.sampleData[indexPath.section].subCategory[indexSubCategory - 1].subSubCategory.name
return cell
}else{
// Cell subCategory
let cell = tableView.dequeueReusableCell(withIdentifier: "subCategory")!
cell.textLabel?.text = self.sampleData[indexPath.section].subCategory[indexSubCategory].name
return cell
}
}
else{
// Cell subCategory
let cell = tableView.dequeueReusableCell(withIdentifier: "subCategory")!
cell.textLabel?.text = self.sampleData[indexPath.section].subCategory[indexPath.row].name
return cell
}
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// then header cell is selected switch between collapse or expand between "subCategory"
if indexPath.row == 0{
let expand = !sampleData[indexPath.section].expand
//Toggle collapse
sampleData[indexPath.section].expand = expand
self.tableView.reloadSections([indexPath.section], with: .none)
}else{
var countValue = 0
var indexSubCategory = 0
let sampleDataSection = sampleData[indexPath.section]
if sampleDataSection.expand{
for (index, subCategory) in sampleDataSection.subCategory.enumerated(){
countValue += 1
if countValue >= indexPath.row{
indexSubCategory = index
break
}
if subCategory.expand{
if index == sampleDataSection.subCategory.count-1{
countValue += 2
indexSubCategory = index + 1
}else{
countValue += 1
}
}
}
// and if "subCategory" cell is selected switch between collapse or expand between "subSubCategory"
if countValue == indexPath.row{
let subSubCategory = sampleData[indexPath.section].subCategory[indexSubCategory]
let expand = !subSubCategory.expand
sampleData[indexPath.section].subCategory[indexSubCategory].expand = expand
UIView.performWithoutAnimation {
self.tableView.reloadSections([indexPath.section], with: .none)
self.tableView.layoutIfNeeded()
}
}
}
}
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return CGFloat.leastNormalMagnitude
}
}
Download demo project from here
You can achieve this by creating a custom cell for the SubCategory 2.1, and displaying it in the first row of every section.
Than, in the didSelectRow method, if the first row is selected, you update the state of the SubCategory 2.1 to collapsed (or non collapsed),reload the section and in the numberOfRows method you should return the appropriate number of rows according to the state of the SubCategory 2.1.
A UITableView really isn't designed to show more than two levels of a hierarchy, as sections and rows.
If you want to show more than two levels there are all deferent type of custom solutions to this, one easy way is to design different cell for Subcategory and display accordingly like showed in the image bellow.
In the above example it is same cell but with different background colour to indicate subcategory.
I came across this video on youtube :
https://www.youtube.com/watch?v=VFtsSEYDNRU
It might help you to achieve what you want.
But I think that you could achieve this by making a custom UIView and putting it into your cell so that when you tap on the subcategory it expands and shows more detail as you wanted it.
Hope this helps.

tableview populating same string

I need to populate my tableview with an array of a custom struct, currently it will only populate the table view of each section with the same array.
Heres where I append...
let shake = Item(name: "Shake", carbs: 20)
let fries = Item(name: "Fries", carbs: 30)
let pie = Item(name: "Pie", carbs: 23)
let beverages = Category(name: "Beverages", items: [shake])
let chips_fries = Category(name: "Chips & Fries", items: [fries])
let desserts = Category(name: "Desserts", items: [pie])
let other = Category(name: "Other Menu Items", items: [])
let sandwiches_burgers = Category(name: "Sandwiches & Burgers", items: [])
let sides = Category(name: "Sides", items: [])
a_w = Restaurant(name: "A&W", categories: [beverages, chips_fries, desserts, other, sandwiches_burgers, sides])
restaurants = [a_w]
Here's where I try to populate the table view with the array of Items...
var restaurants = [Restaurant]()
override func viewDidLoad() {
super.viewDidLoad()
tableView.reloadData()
// Do any additional setup after loading the view.
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
let restaurant = restaurants[indexPath.section]
let category = restaurant.categories[indexPath.section]
let item = category.items[indexPath.row]
cell.textLabel!.text = item.name
return cell
}
override func numberOfSections(in tableView: UITableView) -> Int {
return restaurants.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let restaurant = restaurants[section].categories[section]
return restaurant.items.count
}
}
It will only populate each category with "shake".
You have to use indexPath.row instead of indexPath.section.
But to answer it better, please update how you want the UI to look.
Do you want each restaurant to be a section? If yes, how do you differentiate between the different categories?
If you want nested sections, please refer this tutorial:
http://sapandiwakar.in/nested-sections-in-uitableview/

Resources