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.
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
How to Use Scnetworkreachability in Swift
What Is "Constrain to Margin" in Storyboard in Xcode 6
How to Count Occurrences of an Element in a Swift Array
Load Resources from Relative Path Using Local HTML in Uiwebview
How to Detect Avplayer and Get Url of Current Video from Wkwebview
How to Hide Keyboard When Using Swiftui
Get Current Scroll Position of Scrollview in React Native
What Is the Documents Directory (Nsdocumentdirectory)
Request Failed: Unacceptable Content-Type: Text/Html Using Afnetworking 2.0
Send and Receive Messages Through Nsnotificationcenter in Objective-C
Libc++Abi.Dylib: Terminating With Uncaught Exception of Type Nsexception (Lldb)
How to Record Audio on Iphone With Avaudiorecorder
How to Find Specific Subclass of Nsmanagedobject
How to Convert a Swift Array to a String
How to Pass Textfield Value to View Controller Through Button Click in Swift Ui
Handling Applicationdidbecomeactive - "How Can a View Controller Respond to the App Becoming Active"
Nsdictionary to Nsdata and Nsdata to Nsdictionary in Swift
Tricks For Improving Iphone Uitableview Scrolling Performance