Skip to content

Commit 29b3c7f

Browse files
authored
Update README.md
1 parent e30e0dd commit 29b3c7f

File tree

1 file changed

+75
-39
lines changed

1 file changed

+75
-39
lines changed

README.md

Lines changed: 75 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# How to Implement Collapsible Table Section in iOS
22

3-
:iphone: A simple iOS swift project demonstrates how to implement collapsible table section programmatically, that is no main storyboard, no XIB, no need to register nib, just purely Swift!
3+
A simple iOS swift project demonstrates how to implement collapsible table section programmatically, that is no main storyboard, no XIB, no need to register nib, just pure Swift!
4+
5+
In this project, the table view automatically resizes the height of the rows to fit the content in each cell.
46

57
![rocket](screenshots/cover.gif)
68

@@ -14,28 +16,63 @@ Let's say we have the following data that is grouped into different sections, ea
1416

1517
```swift
1618
struct Section {
17-
var name: String!
18-
var items: [String]!
19-
var collapsed: Bool!
19+
var name: String
20+
var items: [String]
21+
var collapsed: Bool
2022

21-
init(name: String, items: [String], collapsed: Bool = false) {
23+
init(name: String, items: [Item], collapsed: Bool = false) {
2224
self.name = name
2325
self.items = items
2426
self.collapsed = collapsed
2527
}
2628
}
29+
30+
struct Item {
31+
var name: String
32+
var detail: String
33+
34+
init(name: String, detail: String) {
35+
self.name = name
36+
self.detail = detail
37+
}
38+
}
2739

2840
var sections = [Section]()
2941

3042
sections = [
31-
Section(name: "Mac", items: ["MacBook", "MacBook Air", "MacBook Pro", "iMac", "Mac Pro", "Mac mini", "Accessories", "OS X El Capitan"]),
32-
Section(name: "iPad", items: ["iPad Pro", "iPad Air 2", "iPad mini 4", "Accessories"]),
33-
Section(name: "iPhone", items: ["iPhone 6s", "iPhone 6", "iPhone SE", "Accessories"])
43+
Section(name: "Mac", items: [
44+
Item(name: "MacBook", detail: "Apple's ultraportable laptop"),
45+
Item(name: "MacBook Air", detail: "A very light ultraportable laptop.")
46+
]),
47+
Section(name: "iPad", items: [
48+
Item(name: "iPad Pro", detail: "iPad Pro delivers epic power."),
49+
Item(name: "iPad Air 2", detail: "The second-generation of iPad Air tablet.")
50+
]),
51+
Section(name: "iPhone", items: [
52+
Item(name: "iPhone 7", detail: "The latest iPhone."),
53+
Item(name: "iPhone 6", detail: "The 6th-generation of iPhone.")
54+
])
3455
]
3556
```
3657
`collapsed` indicates whether the current section is collapsed or not, by default is `false`.
3758

38-
#### Step 2. The Section Header ####
59+
#### Step 2. Setup TableView to Support Autosizing ####
60+
61+
```swift
62+
override func viewDidLoad() {
63+
super.viewDidLoad()
64+
65+
// Auto resizing the height of the cell
66+
tableView.estimatedRowHeight = 44.0
67+
tableView.rowHeight = UITableViewAutomaticDimension
68+
69+
...
70+
}
71+
```
72+
73+
Ignore this step if your cells have a fixed height.
74+
75+
#### Step 3. The Section Header ####
3976

4077
According to [Apple API reference](https://developer.apple.com/reference/uikit/uitableviewheaderfooterview), we should use `UITableViewHeaderFooterView`. Let's subclass it and implement the section header `CollapsibleTableViewHeader`:
4178

@@ -83,7 +120,7 @@ class CollapsibleTableViewHeader: UITableViewHeaderFooterView {
83120

84121
func setCollapsed(_ collapsed: Bool) {
85122
// Animate the arrow rotation (see Extensions.swf)
86-
arrowLabel.rotate(collapsed ? 0.0 : CGFloat(M_PI_2))
123+
arrowLabel.rotate(collapsed ? 0.0 : .pi / 2)
87124
}
88125
}
89126
```
@@ -131,7 +168,7 @@ override func layoutSubviews() {
131168
}
132169
```
133170

134-
#### Step 3. The UITableView DataSource and Delegate ####
171+
#### Step 4. The UITableView DataSource and Delegate ####
135172

136173
Now we implemented the header view, let's get back to the table view controller.
137174

@@ -147,11 +184,13 @@ and the number of rows in each section is:
147184

148185
```swift
149186
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
150-
return sections[section].items.count
187+
return sections[section].collapsed ? 0 : sections[section].items.count
151188
}
152189
```
153190

154-
We use tableView's viewForHeaderInSection function to hook up our custom header:
191+
Noticed that we don't need to render any cell for the collapsed section, this can improve the performance a lot if there are lots of cells in that section.
192+
193+
Next, we use tableView's viewForHeaderInSection function to hook up our custom header:
155194

156195
```swift
157196
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
@@ -172,53 +211,50 @@ Setup the normal row cell is pretty straightforward:
172211

173212
```swift
174213
override func tableView(_ tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
175-
let cell = tableView.dequeueReusableCellWithIdentifier("cell") as UITableViewCell? ?? UITableViewCell(style: .Default, reuseIdentifier: "cell")
176-
177-
cell.textLabel?.text = sections[indexPath.section].items[indexPath.row]
178-
179-
return cell
214+
let cell: CollapsibleTableViewCell = tableView.dequeueReusableCell(withIdentifier: "cell") as? CollapsibleTableViewCell ?? CollapsibleTableViewCell(style: .default, reuseIdentifier: "cell")
215+
216+
let item: Item = sections[(indexPath as NSIndexPath).section].items[(indexPath as NSIndexPath).row]
217+
218+
cell.nameLabel.text = item.name
219+
cell.detailLabel.text = item.detail
220+
221+
return cell
180222
}
181223
```
182224

183-
#### Step 4. How to Toggle Collapse and Expand ####
225+
Of course you can use a plain `UITableViewCell`, our `CollapsibleTableViewCell` is a subclass of `UITableViewCell` that adds the name and detail labels, and the most important thing is that it supports autosizing feature, the key is to setup the autolayout constrains properly, please refer to the source code for more details.
226+
227+
#### Step 5. How to Toggle Collapse and Expand ####
184228

185-
The idea is really simple, if a section's `collapsed` property is `true`, we set the height of the rows inside that section to be `0`, otherwise `44.0`!
229+
The idea is really simple, if a section's `collapsed` property is `true`, we set the height of the rows inside that section to be `0`, otherwise `UITableViewAutomaticDimension`!
186230

187231
```swift
188232
override func tableView(_ tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
189-
return sections[indexPath.section].collapsed! ? 0 : 44.0
233+
return sections[(indexPath as NSIndexPath).section].collapsed ? 0 : UITableViewAutomaticDimension
190234
}
191235
```
192236

193237
And here is the toggle function:
194238

195239
```swift
196240
extension CollapsibleTableViewController: CollapsibleTableViewHeaderDelegate {
197-
func toggleSection(_ header: CollapsibleTableViewHeader, section: Int) {
198-
let collapsed = !sections[section].collapsed
199-
200-
// Toggle collapse
201-
sections[section].collapsed = collapsed
202-
header.setCollapsed(collapsed)
241+
func toggleSection(_ header: CollapsibleTableViewHeader, section: Int) {
242+
let collapsed = !sections[section].collapsed
203243

204-
// Adjust the height of the rows inside the section
205-
tableView.beginUpdates()
206-
for i in 0 ..< sections[section].items.count {
207-
tableView.reloadRowsAtIndexPaths([NSIndexPath(forRow: i, inSection: section)], withRowAnimation: .Automatic)
208-
}
209-
tableView.endUpdates()
210-
}
244+
// Toggle collapse
245+
sections[section].collapsed = collapsed
246+
header.setCollapsed(collapsed)
247+
248+
// Reload the whole section
249+
tableView.reloadSections(NSIndexSet(index: section) as IndexSet, with: .automatic)
250+
}
211251
}
212252
```
213253

214-
Noticed that we don't lazily just reload the whole section, we only reload the rows inside that section, so that we won't see the refresh of the section header, and most importantly it will allow us to animate anything in the section header smoothly, i.e., rotate the arrow label, change the background etc.
254+
After the sections get reloaded, the number of cells in that section will be recalculated and redrawn.
215255

216256
That's it, please refer to the source code and see the detailed implementation.
217257

218-
### What's coming next? ###
219-
220-
- Custom tableview cell with auto height support, see branch https://github.com/jeantimex/ios-swift-collapsible-table-section/pull/22.
221-
222258
### More Collapsible Demo ###
223259

224260
Sometimes you might want to implement the collapsible cells in a grouped-style table, I have a separate demo at [https://github.com/jeantimex/ios-swift-collapsible-table-section-in-grouped-section](https://github.com/jeantimex/ios-swift-collapsible-table-section-in-grouped-section). The implementation is pretty much the same but slightly different.

0 commit comments

Comments
 (0)