Skip to content

Commit 5f9e0ff

Browse files
committed
Fix bugs Add code improvements
Bug #1: Section arrow image had wrong diraction when opening from code Bug #2: Section headers were using wrong that caused wrong diraction of arrow for open/close sections
1 parent 15cf92c commit 5f9e0ff

File tree

10 files changed

+116
-43
lines changed

10 files changed

+116
-43
lines changed

CollapseTableView.xcodeproj/project.pbxproj

+4
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
4919EFE122AC27A200CD5A74 /* CollapseTableView.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4922F97122AAE53700967BB5 /* CollapseTableView.framework */; };
2020
4919EFE222AC27A200CD5A74 /* CollapseTableView.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 4922F97122AAE53700967BB5 /* CollapseTableView.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
2121
4922F97622AAE53700967BB5 /* CollapseTableView.h in Headers */ = {isa = PBXBuildFile; fileRef = 4922F97422AAE53700967BB5 /* CollapseTableView.h */; settings = {ATTRIBUTES = (Public, ); }; };
22+
49F918182312E1BB0075D644 /* ReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49F918172312E1BB0075D644 /* ReusableView.swift */; };
2223
/* End PBXBuildFile section */
2324

2425
/* Begin PBXContainerItemProxy section */
@@ -60,6 +61,7 @@
6061
4922F97122AAE53700967BB5 /* CollapseTableView.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CollapseTableView.framework; sourceTree = BUILT_PRODUCTS_DIR; };
6162
4922F97422AAE53700967BB5 /* CollapseTableView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CollapseTableView.h; sourceTree = "<group>"; };
6263
4922F97522AAE53700967BB5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
64+
49F918172312E1BB0075D644 /* ReusableView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReusableView.swift; sourceTree = "<group>"; };
6365
/* End PBXFileReference section */
6466

6567
/* Begin PBXFrameworksBuildPhase section */
@@ -93,6 +95,7 @@
9395
4919EFCC22AC25D400CD5A74 /* CollapseTableViewDemo */ = {
9496
isa = PBXGroup;
9597
children = (
98+
49F918172312E1BB0075D644 /* ReusableView.swift */,
9699
4919EFCD22AC25D400CD5A74 /* AppDelegate.swift */,
97100
4919EFDD22AC263D00CD5A74 /* SectionHeaderView.swift */,
98101
4919EFDE22AC263D00CD5A74 /* SectionHeaderView.xib */,
@@ -250,6 +253,7 @@
250253
4919EFD022AC25D400CD5A74 /* ViewController.swift in Sources */,
251254
4919EFDF22AC263D00CD5A74 /* SectionHeaderView.swift in Sources */,
252255
4919EFCE22AC25D400CD5A74 /* AppDelegate.swift in Sources */,
256+
49F918182312E1BB0075D644 /* ReusableView.swift in Sources */,
253257
);
254258
runOnlyForDeploymentPostprocessing = 0;
255259
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<Bucket
3+
type = "1"
4+
version = "2.0">
5+
</Bucket>

CollapseTableView/Source/CollapseSectionHeader.swift

+8-6
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@ public protocol CollapseSectionHeader {
1717

1818
/// Default implementation: rotates `indicatorImageView` in
1919
/// appropriate direction.
20-
func handleChangeToOpenState()
20+
func updateViewForOpenState(animated: Bool)
2121
/// Default implementation: rotates `indicatorImageView` in
2222
/// appropriate direction.
23-
func handleChangeToCloseState()
23+
func updateViewForCloseState(animated: Bool)
2424
}
2525

2626
// MARK: Default implementation of methods and properties.
@@ -32,14 +32,16 @@ extension CollapseSectionHeader {
3232
return 0.25
3333
}
3434

35-
public func handleChangeToOpenState() {
36-
UIView.animate(withDuration: imageAnimationDuration) {
35+
public func updateViewForOpenState(animated: Bool) {
36+
let duration = animated ? imageAnimationDuration : 0
37+
UIView.animate(withDuration: duration) {
3738
self.indicatorImageView.transform = CGAffineTransform(rotationAngle: CGFloat(Double.pi))
3839
}
3940
}
4041

41-
public func handleChangeToCloseState() {
42-
UIView.animate(withDuration: imageAnimationDuration) {
42+
public func updateViewForCloseState(animated: Bool) {
43+
let duration = animated ? imageAnimationDuration : 0
44+
UIView.animate(withDuration: duration) {
4345
self.indicatorImageView.transform = CGAffineTransform(rotationAngle: CGFloat(2 * Double.pi))
4446
}
4547
}

CollapseTableView/Source/CollapseTableView.swift

+19-6
Original file line numberDiff line numberDiff line change
@@ -60,18 +60,15 @@ open class CollapseTableView: UITableView {
6060

6161
// MARK: Public methods
6262

63-
public func toggleSection(_ sectionIndex: Int, sectionView: UIView, animated: Bool) {
63+
public func toggleSection(_ sectionIndex: Int, animated: Bool) {
6464
if sectionIndex >= sectionStates.count {
6565
return
6666
}
6767
let sectionIsOpen = sectionStates[sectionIndex]
68-
let collapseSectionHeader = sectionView as? CollapseSectionHeader
6968
if sectionIsOpen {
70-
collapseSectionHeader?.handleChangeToCloseState()
7169
closeSection(sectionIndex, animated: animated)
7270
didTapSectionHeaderView?(sectionIndex, false)
7371
} else {
74-
collapseSectionHeader?.handleChangeToOpenState()
7572
openSection(sectionIndex, animated: animated)
7673
didTapSectionHeaderView?(sectionIndex, true)
7774
}
@@ -82,6 +79,9 @@ open class CollapseTableView: UITableView {
8279
return
8380
}
8481
setSectionAtIndex(sectionIndex, open: true)
82+
if let headerView = headerView(forSection: sectionIndex) as? CollapseSectionHeader {
83+
headerView.updateViewForOpenState(animated: true)
84+
}
8585
if animated {
8686
if let indexPathsToInsert = indexPathsForRowsInSectionAtIndex(sectionIndex) {
8787
insertRows(at: indexPathsToInsert, with: .top)
@@ -92,7 +92,13 @@ open class CollapseTableView: UITableView {
9292
}
9393

9494
public func closeSection(_ sectionIndex: Int, animated: Bool) {
95+
if sectionIndex >= sectionStates.count {
96+
return
97+
}
9598
setSectionAtIndex(sectionIndex, open: false)
99+
if let headerView = headerView(forSection: sectionIndex) as? CollapseSectionHeader {
100+
headerView.updateViewForCloseState(animated: true)
101+
}
96102
if animated {
97103
if let indexPathsToDelete = indexPathsForRowsInSectionAtIndex(sectionIndex) {
98104
deleteRows(at: indexPathsToDelete, with: .top)
@@ -134,7 +140,7 @@ open class CollapseTableView: UITableView {
134140
guard let view = sender.view, view.tag >= 0 else {
135141
return
136142
}
137-
toggleSection(view.tag, sectionView: view, animated: true)
143+
toggleSection(view.tag, animated: true)
138144
}
139145
}
140146

@@ -181,10 +187,17 @@ extension CollapseTableView: UITableViewDelegate {
181187
}
182188
}
183189
if !tapGestureFound {
184-
view.tag = section
185190
view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleTapGesture(_:))))
186191
}
187192
}
193+
view.tag = section
194+
if let collapseSectionHeader = view as? CollapseSectionHeader {
195+
if isOpenSection(section) {
196+
collapseSectionHeader.updateViewForOpenState(animated: false)
197+
} else {
198+
collapseSectionHeader.updateViewForCloseState(animated: false)
199+
}
200+
}
188201
return view
189202
}
190203
}

CollapseTableViewDemo/Base.lproj/Main.storyboard

+6-13
Original file line numberDiff line numberDiff line change
@@ -20,36 +20,29 @@
2020
<subviews>
2121
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="mlE-gm-cwW" customClass="CollapseTableView" customModule="CollapseTableView">
2222
<rect key="frame" x="0.0" y="20" width="375" height="647"/>
23-
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
23+
<color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
2424
<prototypes>
25-
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="blue" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="customCell" editingAccessoryType="disclosureIndicator" textLabel="59f-Q8-5t4" detailTextLabel="cKH-tz-ejd" style="IBUITableViewCellStyleSubtitle" id="KXn-bp-oiK">
25+
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="blue" indentationWidth="10" reuseIdentifier="customCell" editingAccessoryType="disclosureIndicator" textLabel="JoT-hf-pFa" style="IBUITableViewCellStyleDefault" id="KXn-bp-oiK">
2626
<rect key="frame" x="0.0" y="28" width="375" height="44"/>
2727
<autoresizingMask key="autoresizingMask"/>
2828
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="KXn-bp-oiK" id="ACh-8J-1QC">
29-
<rect key="frame" x="0.0" y="0.0" width="342" height="43.5"/>
29+
<rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/>
3030
<autoresizingMask key="autoresizingMask"/>
3131
<subviews>
32-
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Title" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="59f-Q8-5t4">
33-
<rect key="frame" x="15" y="5" width="33.5" height="20.5"/>
32+
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Title" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="JoT-hf-pFa">
33+
<rect key="frame" x="15" y="0.0" width="345" height="43.5"/>
3434
<autoresizingMask key="autoresizingMask"/>
3535
<fontDescription key="fontDescription" type="system" pointSize="17"/>
3636
<nil key="textColor"/>
3737
<nil key="highlightedColor"/>
3838
</label>
39-
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Detail" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="cKH-tz-ejd">
40-
<rect key="frame" x="15" y="25.5" width="33" height="14.5"/>
41-
<autoresizingMask key="autoresizingMask"/>
42-
<fontDescription key="fontDescription" type="system" pointSize="12"/>
43-
<nil key="textColor"/>
44-
<nil key="highlightedColor"/>
45-
</label>
4639
</subviews>
4740
</tableViewCellContentView>
4841
</tableViewCell>
4942
</prototypes>
5043
</tableView>
5144
</subviews>
52-
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
45+
<color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
5346
<constraints>
5447
<constraint firstItem="tsZ-KP-nHI" firstAttribute="trailing" secondItem="mlE-gm-cwW" secondAttribute="trailing" id="Enb-Jv-DVp"/>
5548
<constraint firstItem="mlE-gm-cwW" firstAttribute="leading" secondItem="tsZ-KP-nHI" secondAttribute="leading" id="JFZ-1t-DnH"/>
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//
2+
// ReusableView.swift
3+
//
4+
//
5+
// Created by Serhii Kharauzov on 1/9/18.
6+
// Copyright © 2018 Serhii Kharauzov. All rights reserved.
7+
//
8+
9+
import Foundation
10+
import UIKit
11+
12+
/// Object, that adopts this protocol, will use identifier that matches name of its class.
13+
protocol ReusableView: class {}
14+
15+
extension ReusableView {
16+
static var reuseIdentifier: String {
17+
return String(describing: self)
18+
}
19+
}
20+
21+
extension UIView: ReusableView {}

CollapseTableViewDemo/SectionHeaderView.swift

+7-1
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,17 @@
99
import UIKit
1010
import CollapseTableView
1111

12-
class SectionHeaderView: UIView, CollapseSectionHeader {
12+
class SectionHeaderView: UITableViewHeaderFooterView, CollapseSectionHeader {
1313

1414
@IBOutlet weak var imageView: UIImageView!
1515

1616
var indicatorImageView: UIImageView {
1717
return imageView
1818
}
19+
20+
override func awakeFromNib() {
21+
super.awakeFromNib()
22+
imageView.image = #imageLiteral(resourceName: "arrow_down").withRenderingMode(.alwaysTemplate)
23+
imageView.tintColor = .white
24+
}
1925
}

CollapseTableViewDemo/SectionHeaderView.xib

+21-10
Original file line numberDiff line numberDiff line change
@@ -12,22 +12,33 @@
1212
<objects>
1313
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
1414
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
15-
<view contentMode="scaleToFill" id="iN0-l3-epB" customClass="SectionHeaderView" customModule="CollapseTableView" customModuleProvider="target">
16-
<rect key="frame" x="0.0" y="0.0" width="414" height="66"/>
15+
<view contentMode="scaleToFill" id="iN0-l3-epB" customClass="SectionHeaderView" customModule="CollapseTableViewDemo" customModuleProvider="target">
16+
<rect key="frame" x="0.0" y="0.0" width="414" height="50"/>
1717
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
1818
<subviews>
19-
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="arrow_down" translatesAutoresizingMaskIntoConstraints="NO" id="diD-fa-6dA">
20-
<rect key="frame" x="360" y="11" width="44" height="44"/>
19+
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="ur3-jR-pmv">
20+
<rect key="frame" x="0.0" y="0.0" width="414" height="50"/>
21+
<subviews>
22+
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="arrow_down" translatesAutoresizingMaskIntoConstraints="NO" id="diD-fa-6dA">
23+
<rect key="frame" x="360" y="3" width="44" height="44"/>
24+
<constraints>
25+
<constraint firstAttribute="height" constant="44" id="2BN-V4-DGF"/>
26+
<constraint firstAttribute="width" constant="44" id="CV0-rK-Tkv"/>
27+
</constraints>
28+
</imageView>
29+
</subviews>
30+
<color key="backgroundColor" red="0.85098039219999999" green="0.81568627449999997" blue="0.80000000000000004" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
2131
<constraints>
22-
<constraint firstAttribute="height" constant="44" id="Ms1-d3-7lQ"/>
23-
<constraint firstAttribute="width" constant="44" id="thi-hR-BN3"/>
32+
<constraint firstAttribute="trailing" secondItem="diD-fa-6dA" secondAttribute="trailing" constant="10" id="B9b-CG-6RP"/>
33+
<constraint firstItem="diD-fa-6dA" firstAttribute="centerY" secondItem="ur3-jR-pmv" secondAttribute="centerY" id="bhd-ax-ALd"/>
2434
</constraints>
25-
</imageView>
35+
</view>
2636
</subviews>
27-
<color key="backgroundColor" red="0.25098039220000001" green="0.75972222219999996" blue="0.33663194439999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
2837
<constraints>
29-
<constraint firstAttribute="trailing" secondItem="diD-fa-6dA" secondAttribute="trailing" constant="10" id="0M4-uM-535"/>
30-
<constraint firstItem="diD-fa-6dA" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="centerY" id="Pwf-eP-iLx"/>
38+
<constraint firstItem="vUN-kp-3ea" firstAttribute="trailing" secondItem="ur3-jR-pmv" secondAttribute="trailing" id="2u4-0M-rwC"/>
39+
<constraint firstItem="ur3-jR-pmv" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" id="Ucq-FP-CIK"/>
40+
<constraint firstItem="ur3-jR-pmv" firstAttribute="leading" secondItem="vUN-kp-3ea" secondAttribute="leading" id="az1-GB-tiY"/>
41+
<constraint firstAttribute="bottom" secondItem="ur3-jR-pmv" secondAttribute="bottom" id="cRe-SW-VXf"/>
3142
</constraints>
3243
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
3344
<viewLayoutGuide key="safeArea" id="vUN-kp-3ea"/>

CollapseTableViewDemo/ViewController.swift

+25-7
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,29 @@ class ViewController: UIViewController {
1515

1616
override func viewDidLoad() {
1717
super.viewDidLoad()
18-
tableView.delegate = self
19-
tableView.dataSource = self
18+
setupTableView()
2019
tableView.didTapSectionHeaderView = { (sectionIndex, isOpen) in
2120
debugPrint("sectionIndex \(sectionIndex), isOpen \(isOpen)")
2221
}
22+
reloadTableView { [unowned self] in
23+
self.tableView.openSection(0, animated: true)
24+
}
25+
}
26+
27+
func setupTableView() {
28+
tableView.delegate = self
29+
tableView.dataSource = self
30+
tableView.tableFooterView = UIView(frame: .zero)
31+
tableView.register(UINib(nibName: SectionHeaderView.reuseIdentifier, bundle: nil), forHeaderFooterViewReuseIdentifier: SectionHeaderView.reuseIdentifier)
32+
}
33+
34+
func reloadTableView(_ completion: @escaping () -> Void) {
35+
CATransaction.begin()
36+
CATransaction.setCompletionBlock({
37+
completion()
38+
})
2339
tableView.reloadData()
24-
tableView.openSection(0, animated: true)
40+
CATransaction.commit()
2541
}
2642
}
2743

@@ -36,13 +52,15 @@ extension ViewController: UITableViewDataSource, UITableViewDelegate {
3652

3753
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
3854
let cell = tableView.dequeueReusableCell(withIdentifier: "customCell", for: indexPath)
39-
cell.textLabel?.text = "textLabel \(indexPath.row)"
40-
cell.detailTextLabel?.text = "detailTextLabel \(indexPath.row)"
55+
cell.textLabel?.text = "Text for cell #\(indexPath.row + 1)"
4156
return cell
4257
}
4358

4459
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
45-
let view = Bundle.main.loadNibNamed("SectionHeaderView", owner: self, options: nil)?.first as? SectionHeaderView
60+
guard let view = tableView.dequeueReusableHeaderFooterView(withIdentifier: SectionHeaderView.reuseIdentifier) else {
61+
let view = Bundle.main.loadNibNamed(SectionHeaderView.reuseIdentifier, owner: self, options: nil)?.first as? SectionHeaderView
62+
return view
63+
}
4664
return view
4765
}
4866

@@ -51,7 +69,7 @@ extension ViewController: UITableViewDataSource, UITableViewDelegate {
5169
}
5270

5371
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
54-
return 60
72+
return 44
5573
}
5674

5775
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {

0 commit comments

Comments
 (0)