Expandable tableView in iphone
Finally i get two very useful helping link below which describes exact what the requirement is here
Expanding/Collapsing TableView Sections
Collapsable Table View for iOS
Really, good articles for such kind of expanding/collapsing tableview sections
Expanding the expandable table View cells to further extent
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
How to create 2 steps expandable tableview Swift
To deal with expandable cells (or cells inside cells effect) I recommend using UIStackView inside UITableViewCell and add inside needed views with data.
For example you can create UIView inside Xib file and load it inside needed UITableViewCell then add to UIStackView and fill with data.
Then you can simply hide/unhide elements inside UIStackView to archive expanding/unxpanding.
This way or other you should use 1 UITableView.
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"
.
ios Collapsable/expandable tableview on storyboard
I have found a great sample project on this at:
https://github.com/singhson/Expandable-Collapsable-TableView
It is very easy to understand and implement.
ios - Animated expanding TableView (tap on first row) inside TableView
I solved the animation/update.
1) I forgot to reload data after changing .expanded
property:
extension GoalsMainCell: UITableViewDelegate
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let goalNotificationInfo = ["index" : goalIndex ]
if indexPath.row == 0 {
if goal != nil {
do {
try realm.write {
goal!.expanded = !goal!.expanded
}
} catch {
print("Error saving done status, \(error)")
}
}
loadSubtasks()
tableView.deselectRow(at: indexPath, animated: true)
let section = IndexSet.init(integer: indexPath.section)
tableView.reloadSections(section, with: .none)
NotificationCenter.default.post(name: .goalNotKey, object: nil, userInfo: goalNotificationInfo as [AnyHashable : Any])
}
}
2) I added notification observer to the slave table delegate and call .beginUpdate()
+ .endUpdate()
@objc func updateGoalSection(_ notification: Notification) {
tableView.beginUpdates()
tableView.endUpdates()
}
Now the update works with a smooth transition. Of coarse solving this revealed another issues with indexing and main call automatic dimensions but that is another topic...
Hope it helpes other to struggle less.
Related Topics
Undefined Symbols for Architecture X86_64 on Xcode 6.1
How to Recognize Which Pin Was Tapped
Is Silent Remote Notifications Possible If User Has Disabled Push for the App
Iad Is Shutting Down. Should I Remove the Iad Framework from All My Applications
Update Restkit 'Lcl_Rk.H' File Not Found in Rklog.H
Uitableview Load More When Scrolling to Bottom Like Facebook Application
Behaviour for Significant Change Location API When Terminated/Suspended
How to Create a Delay in Swift
"From View Controller" Disappears Using Uiviewcontrollercontexttransitioning
How to Get the Console Logs from the iOS Simulator
Uitextview That Expands to Text Using Auto Layout
Objective C: Downloading File with Progress Bar
What Is the Meaning of the "No Index Path for Table Cell Being Reused" Message in iOS 6/7
Renew Push Certificate and Keep Current App Store App Working
How to Add a Toolbar Above the Keyboard
Does an iOS App Have Write Access Inside Its Bundle
Does H.264 Encoded Video with Bt.709 Matrix Include Any Gamma Adjustment