Expand/Collapse Section in Uitableview in Ios

Expand/collapse section in UITableView in iOS

You have to make your own custom header row and put that as the first row of each section. Subclassing the UITableView or the headers that are already there will be a pain. Based on the way they work now, I am not sure you can easily get actions out of them. You could set up a cell to LOOK like a header, and setup the tableView:didSelectRowAtIndexPath to manually expand or collapse the section it is in.

I'd store an array of booleans corresponding the the "expended" value of each of your sections. Then you could have the tableView:didSelectRowAtIndexPath on each of your custom header rows toggle this value and then reload that specific section.

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.row == 0) {
///it's the first row of any section so it would be your custom section header

///put in your code to toggle your boolean value here
mybooleans[indexPath.section] = !mybooleans[indexPath.section];

///reload this section
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation:UITableViewRowAnimationFade];
}
}

Then set numberOfRowsInSection to check the mybooleans value and return 1 if the section isn't expanded, or 1+ the number of items in the section if it is expanded.

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {

if (mybooleans[section]) {
///we want the number of people plus the header cell
return [self numberOfPeopleInGroup:section] + 1;
} else {
///we just want the header cell
return 1;
}
}

Also, you will need to update cellForRowAtIndexPath to return a custom header cell for the first row in any section.

Swift/iOS: Collapsing a section in a UITableView

Hi after a lot of research, i found a solution which worked for me perfectly using storyboard.

storyboard setup

View controller code:

import UIKit

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

@IBOutlet weak var tblView: UITableView!

var sections = ["section1","section2","section3"]

var cells = ["cell1","cell2","cell3","cell4"]

var selectedIndx = -1

var thereIsCellTapped = false

override func viewDidLoad() {
super.viewDidLoad()
tblView.reloadData()
}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}

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

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch section {
case 0:
return 2
case 1:
return 3
default:
return 4
}
}

func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 50
}

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.section == selectedIndx && thereIsCellTapped{
return 50
}else{
return 0
}
}

func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerCell = tableView.dequeueReusableCell(withIdentifier: "SectionTableViewCell") as! SectionTableViewCell
headerCell.lblHeader.text = sections[section]
headerCell.btnSelection.tag = section
headerCell.btnSelection.addTarget(self, action: #selector(ViewController.btnSectionClick(sender:)), for: .touchUpInside)
return headerCell
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ExpandeTableViewCell") as! ExpandeTableViewCell
cell.lblCell.text = cells[indexPath.row]
return cell
}

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print(indexPath.section)
}

@objc func btnSectionClick(sender:UIButton!){
print("selected index",sender.tag)
if selectedIndx != sender.tag {
self.thereIsCellTapped = true
self.selectedIndx = sender.tag
}
else {
// there is no cell selected anymore
self.thereIsCellTapped = false
self.selectedIndx = -1
}
tblView.reloadData()
}
}

If you don't want to do select and unselect on the same selection then, see code below.

@objc func btnSectionClick(sender:UIButton!){
print("selected index",sender.tag)

selectedIndx = sender.tag

tblView.reloadData()
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.section == selectedIndx{
return 50
}else{
return 0
}
}

It works for me, i referred lot of answers and made it. I hope it will help you.

TableView expand and collapse cells in Swift

You need to reset them all to false then toggle the current state of the clicked section

let toSet = !sections[indexPath.section].isOpened
sections.forEach {
$0.isOpened = false
}
sections[indexPath.section].isOpened = toSet
tableView.reloadData()

How to reload selected cell in particular section for expand/collapse in tableview Swift

If you store section and row independently in separate arrays, your algorithm will fail.
The reason is that both are dependent:
Think of three expanded cells (row:1, section:1), (row:2, section:1), (row:3, section:2)

Now what happens for the cell (row:3, section:1)?
The row-array contains the value "3", and the section-array contains value "1", therefore it will be considered as expanded.

Therefore, you need to store the index path as a whole - see the sample code:

var expanded:[IndexPath] = []

expanded.append(IndexPath(row:1, section:1))
expanded.append(IndexPath(row:2, section:1))
expanded.append(IndexPath(row:3, section:2))

let checkPath = IndexPath(row:3, section:1)
if (expanded.contains(checkPath)) {
print ("is expanded")
} else {
print ("collapsed")
}

Update

So in your button handle, you'll do the following:

@IBAction moreButtonTapped(_ sender: Any) {

if(expanded.contains(indexPath)) {
expanded.removeAll { (checkPath) -> Bool in
return checkPath == indexPath
}
} else {
expanded.append(indexPath)
}
entriesTableView.beginUpdates()
entriesTableView.reloadRows(at: [indexPath], with: .none)
entriesTableView.endUpdates()
}

Expanding tableview sections on clicking each section

Add an array to keep track of section expend/collapse

let sectionStats = [Bool](repeating: true, count: 2)

Add a, IBAction to track section tap, update value of sectionStats for the corresponding section and reload section

and update your numberOfRowsInSection as show below

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
guard sectionStats[section] else {
return 0
}

if section == 0 {
return 1
} else {
return list.count
}
}

Tappable Header:

func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
return headerView(section: section, title: section == 0 ? "CHAT LIST" : "GROUPS")
}

private func headerView(section: Int, title: String) -> UIView {
let button = UIButton(frame: CGRect.zero)
button.tag = section
button.setTitle(title, for: .normal)
button.setTitleColor(UIColor.red, for: .normal)
button.addTarget(self, action: #selector(sectionHeaderTapped), for: .touchUpInside)

return button
}

@objc private func sectionHeaderTapped(sender: UIButton) {
let section = sender.tag
sectionStats[section] = !sectionStats[section]
tableView.beginUpdates()
tableView.reloadSections([section], with: .automatic)
tableView.endUpdates()
}

Good tutorial on How to build a Table View with Collapsible Sections:
https://medium.com/ios-os-x-development/ios-how-to-build-a-table-view-with-collapsible-sections-96badf3387d0

Want to expand and collapse UITableview Section

  • A simple implementation would be keep your cell height as zero for
    sections.
  • Make the viewForSectionHeader touchable
  • When you touch it, set proper height for cells under the section
  • Write a logic for switching between sections

OR,

  • On touching the section header reload the table with updated rows count for section touched.

Many other ways to do it. Apple example.

How to make Expandable with only Cells (NOT SECTION/ HEADER) with UITableView in Swift?

You can add a Bool "expanded" property to your Object.

In numberOfRowsInSection, if expanded is True, return the count of items in seactionObjection, else return 1.

Then, in didSelectRowAt, if it's the first row in a section, toggle the expanded property of that section's Object, and reload the section.

Here's a modified version of your controller class:

class HelpViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

struct Object {
var seactionName: String!
var expanded: Bool!
var seactionObjection: [String]!
var footerName: String!
}

var objectArray = [Object]()

var TableView = UITableView()

override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.


objectArray = [
Object(seactionName: "Header 1",
expanded: false,
seactionObjection: ["Expandable Cell 1", "One Row 1", "Two Row 1"],
footerName: "Footer 1"),

Object(seactionName: "Header 2",
expanded: false,
seactionObjection: ["Expandable Cell 2", "One Row 2", "Two Row 2"],
footerName: "Footer 2"),

Object(seactionName: "Header 3",
expanded: false,
seactionObjection: ["Expandable Cell 3", "One Row 3", "Two Row 3"],
footerName: "Footer 3"),


// copy and paste new cells
]

title = "Help Center"

view.backgroundColor = UIColor.quaternarySystemFill

TableView.frame = view.bounds

TableView = UITableView(frame: self.view.bounds, style: UITableView.Style.insetGrouped)
TableView.showsVerticalScrollIndicator = true
TableView.indicatorStyle = .default
TableView.register(SliceCell.self, forCellReuseIdentifier: "Slice List")

TableView.delegate = self
TableView.dataSource = self

view.addSubview(TableView)
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// if section is expanded
// return count of seactionObjection
// else
// return 1
return objectArray[section].expanded ? objectArray[section].seactionObjection.count : 1
}

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

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

func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
return objectArray[section].footerName
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Slice List") as! SliceCell

cell.backgroundColor = .secondarySystemGroupedBackground
cell.textLabel?.text = objectArray[indexPath.section].seactionObjection[indexPath.row]
cell.textLabel?.numberOfLines = 0

// if it's the first row in a section,
// show accessoryView with
// chevron.up or chevron.down
// based on section Object expanded property
if indexPath.row == 0 {
let chevronName = objectArray[indexPath.section].expanded ? "chevron.up" : "chevron.down"
let img = UIImage(systemName: chevronName)
let disclosureView = UIImageView(image: img)
cell.accessoryView = disclosureView
} else {
// not the first row in a section, so
// clear the accessoryView
cell.accessoryView = nil
}

return cell
}

func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int){
view.tintColor = .clear
let header = view as! UITableViewHeaderFooterView
header.textLabel?.textColor = UIColor.label
}

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.row == 0 {
objectArray[indexPath.section].expanded.toggle()
tableView.reloadSections([indexPath.section], with: .automatic)
} else {
// do something else on row selection
}
}

}


Related Topics



Leave a reply



Submit