Expandable Sections Uitableview Indexpath Swift

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

Expandable Sections UITableView IndexPath SWIFT

I don't know why you need sections to do it what you want, you can achieve with normal cells, no need to complicate at all. Just differentiating the parent cells of the child cells is all you need.

I have a repository in Github that achieve what you want, without using sections neither UITapGestureRecognizer. As soon as possible I'll try to update the project to better performance and more levels of depth in the dropdown.

You can reach it AccordionMenu
, and feel free to post anything you need as issue.

I hope this help you.

Swift 5 UITableViewCell : Expand one section and collapse the expanded section

do this in didselecterow method. This is the else case of your condition

 // You will need to reload multiple sections. So make an array.
var reloadSections = [Int]()
// find already opened array
if let alreadyOpenSection = supportArray.firstIndex(where: { (faq) -> Bool in
return faq.openSection
}) {
// if found, toggle the openSections bit
supportArray[alreadyOpenSection].openSection = false
// add it to reload sections array
reloadSections.append(alreadyOpenSection)
}
supportArray[indexPath.section].openSection = true
reloadSections.append(indexPath.section)
// create index set with reload sections array
let sections = IndexSet.init(reloadSections)
tableView.reloadSections(sections, with: .fade)
// below code is same
if indexPath.section != 0{
if indexPath.row == 0{
let cell = tableView.cellForRow(at: indexPath) as! SupportCenterFaqTableViewCell
cell.faqView.backgroundColor = UIColor(hexString: "#D71B61")
cell.imageButton.tintColor = .white
cell.imageText.textColor = .white

}
}

Swift expandable table view header for sections problem

I am not deep dive in your code but I copy-paste into an Xcode project to look into. So there is basic way to solve your problem, I hope it helps to you.

I assume your class is like that.

class Adres{
var title: String = ""
var isExpanded: Bool = true

init(title: String, isExpanded: Bool){
self.title = title
self.isExpanded = isExpanded
}
}

And in there mainly 2 different variable for adresDict & adresDictForTeslim.

So I keep an lock array to doing logic stuff of expandable.

var keys: [Int] = [1,1]

<1> Show
<0> Hide

Now, the arrays elements are showing into UITableView, because its expanded.

There's mock data.

var userAdressDict = [Adres(title: "First Adres", isExpanded: true) ,Adres(title: "Second Adres", isExpanded: true)]
var userAdressDictForTeslim = [Adres(title: "First Teslim", isExpanded: true),Adres(title: "Second Teslim", isExpanded: true)]

viewForHeaderInSection is same as well.

And for numberOfRowsInSection check array for key that if its expanded or not.

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
if self.keys[0] == 1 {
return userAdressDict.count
}else{
return 0
}
}else{
if self.keys[1] == 1 {
return userAdressDictForTeslim.count
}else{
return 0
}
}
}

Then check these keys in handler functions.

 @objc func handleExpandCloseForAlim(sender: UIButton) {

if keys[0] == 0 {
keys[0] = 1
}else{
keys[0] = 0
}

DispatchQueue.main.async {
self.tableView.reloadData()
}
}

If first array elements in table view is expanded close it, or not expand it.

Then in another function.

@objc func handleExpandCloseForTeslim(sender: UIButton) {

if keys[1] == 0 {
keys[1] = 1
}else{
keys[1] = 0
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
}

So far so good. It's working on my simulator.

Then there's more efficient way to handle keys of expanded.

Make your UIButton's of the sectionHeader as public variable in your ViewController and control it if it's title is "Kapat" and clicked then it must be "Aç" or if its "Aç" and touched it must be "Kapat".

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

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.

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