Here are 4 easy steps to do it
- Keep track of the open section
- Create custom section view using method viewForHeaderInSection so that we can attach a UITapGestureRecognizer
- Set row count only for section that you want expanded else send ZERO
- Then on tap check if the new section is different, if so then expand that one and collapse the other one
private var openSection: Int = 0
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
//my custom view for the section display
let sectionView = UIView()
sectionView.backgroundColor = UIColor(red:0.87, green:0.87, blue:0.87, alpha:1.0)
//set the tag so that we can find the section no later
sectionView.tag = section
//add a tap gesture so we know when user taps on the section
let tap = UITapGestureRecognizer(target: self, action:#selector(self.sectionTap(_:)))
sectionView.addGestureRecognizer(tap)
//add label so that we can display the section text
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
sectionView.addSubview(label)
//just auto layout stuff
let views = ["sectionView": view!, "label" : label]
var allConstraints: [NSLayoutConstraint] = []
allConstraints += NSLayoutConstraint.constraints(withVisualFormat: "V:|[label(50)]|", options: [], metrics: nil, views: views as [String : Any])
allConstraints += NSLayoutConstraint.constraints(withVisualFormat: "H:|-[label]|", options: [], metrics: nil, views: views as [String : Any])
NSLayoutConstraint.activate(allConstraints)
//just some easy indication of expanded and collapsed section using + and -,
//you can use images icons for better creativity
if section < companies.count {
var prefix = "-"
if section == openSection {
prefix = "+"
}
//section title
let sectionName = "\(prefix) \(companies[section].name): Count is \(companies[section].employees.count)"
label.text = sectionName
return sectionView
}
return nil
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
//hide rows if not the open section
if section == openSection {
return companies[section].employees.count
}
else {
return 0
}
}
@objc func sectionTap(_ sender: UITapGestureRecognizer) {
let newSection = sender.view!.tag
//only do anything if new section is different
if(newSection != openSection) {
var sectionArray: [Int] = [openSection]
sectionArray.append(newSection)
openSection = newSection
//list of original open section and the new section that we need to expand
let indices: IndexSet = IndexSet(sectionArray)
//refresh those sections
myTableView.reloadSections(indices, with: .automatic)
}
}
Complete Source for UIViewController to Collapse and Expand Sections
import UIKit
//for parsing json data
struct Company: Codable {
var name: String
var employees: [Employee]
}
struct Employee: Codable {
var name: String
var salary: Double
}
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
private var myTableView: UITableView!
private var companies: [Company]!
//keeping track of the current open section
private var openSection: Int = 0
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.title = "Main View"
//get my sample data for loading the table view
companies = getCompanyData()
//setup my table view
myTableView = UITableView()
myTableView.register(UITableViewCell.self, forCellReuseIdentifier: "MyCell")
myTableView.dataSource = self
myTableView.delegate = self
myTableView.translatesAutoresizingMaskIntoConstraints = false
myTableView.tableFooterView = UIView()
self.view.addSubview(myTableView)
//auto layout for the table view
let views = ["view": view!, "tableView" : myTableView]
var allConstraints: [NSLayoutConstraint] = []
allConstraints += NSLayoutConstraint.constraints(withVisualFormat: "V:|[tableView]|", options: [], metrics: nil, views: views as [String : Any])
allConstraints += NSLayoutConstraint.constraints(withVisualFormat: "H:|[tableView]|", options: [], metrics: nil, views: views as [String : Any])
NSLayoutConstraint.activate(allConstraints)
}
//when user taps on the row
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("Company Selected: \(companies[indexPath.section].name)")
print("Employee Selected: \(companies[indexPath.section].employees[indexPath.row].name)")
}
//no of sections for the table view
func numberOfSections(in tableView: UITableView) -> Int {
return companies.count
}
//section heading, not used when you implement viewForHeaderInSection
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if section < companies.count {
let sectionName = "\(companies[section].name): Count is \(companies[section].employees.count)"
return sectionName
}
return nil
}
//custom view for section header
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
//my custom view for the section display
let sectionView = UIView()
sectionView.backgroundColor = UIColor(red:0.87, green:0.87, blue:0.87, alpha:1.0)
//set the tag so that we can find the section no later
sectionView.tag = section
//add a tap gesture so we know when user taps on the section
let tap = UITapGestureRecognizer(target: self, action:#selector(self.sectionTap(_:)))
sectionView.addGestureRecognizer(tap)
//add label so that we can display the section text
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
sectionView.addSubview(label)
//just auto layout stuff
let views = ["sectionView": view!, "label" : label]
var allConstraints: [NSLayoutConstraint] = []
allConstraints += NSLayoutConstraint.constraints(withVisualFormat: "V:|[label(50)]|", options: [], metrics: nil, views: views as [String : Any])
allConstraints += NSLayoutConstraint.constraints(withVisualFormat: "H:|-[label]|", options: [], metrics: nil, views: views as [String : Any])
NSLayoutConstraint.activate(allConstraints)
//just some easy indication of expanded and collapsed section using + and -,
//you can use images icons for better creativity
if section < companies.count {
var prefix = "-"
if section == openSection {
prefix = "+"
}
//section title
let sectionName = "\(prefix) \(companies[section].name): Count is \(companies[section].employees.count)"
label.text = sectionName
return sectionView
}
return nil
}
//when the user taps on the section
@objc func sectionTap(_ sender: UITapGestureRecognizer) {
let newSection = sender.view!.tag
//only do anything if new section is different
if(newSection != openSection) {
var sectionArray: [Int] = [openSection]
sectionArray.append(newSection)
openSection = newSection
//list of original open section and the new section that we need to expand
let indices: IndexSet = IndexSet(sectionArray)
//refresh those sections
myTableView.reloadSections(indices, with: .automatic)
}
}
//number of rows for that section
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
//hide rows if not the open section
if section == openSection {
return companies[section].employees.count
}
else {
return 0
}
}
//render the cell and display data
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MyCell", for: indexPath)
let formatter = NumberFormatter()
formatter.numberStyle = .currency
let employee = companies[indexPath.section].employees[indexPath.row]
let salary = formatter.string(from: NSNumber(value: employee.salary))
cell.textLabel!.text = "\(employee.name), salary is \(salary ?? "")"
return cell
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
}
//get JSON data into object for display
func getCompanyData() -> [Company] {
var companies: [Company] = [Company]()
let myJsonData = """
[{
"name": "ABC Company",
"employees": [{
"name": "Employee1",
"salary": 110000
},
{
"name": "Employee2",
"salary": 150000
}
]
},
{
"name": "KLM Company",
"employees": [{
"name": "Employee3",
"salary": 170000
},
{
"name": "Employee4",
"salary": 190000
}
]
},
{
"name": "XYZ Company",
"employees": [{
"name": "Employee5",
"salary": 220000.5
},
{
"name": "Employee6",
"salary": 215000.0
},
{
"name": "Employee7",
"salary": 2350000
}
]
}
]
"""
//Decode JSON Data String to Swift Objects
if let jsonData = myJsonData.data(using: .utf8) {
let decoder = JSONDecoder()
do {
companies = try decoder.decode([Company].self, from: jsonData)
} catch {
//do nothing
}
}
return companies
}
}
No comments:
Post a Comment
NO JUNK, Please try to keep this clean and related to the topic at hand.
Comments are for users to ask questions, collaborate or improve on existing.