CocoaPods trunk is moving to be read-only. Read more on the blog, there are 19 months to go.

CollapsibleTable 1.1.0

CollapsibleTable 1.1.0

TestsTested
LangLanguage SwiftSwift
License MIT
ReleasedLast Release Aug 2017
SwiftSwift Version 3.2
SPMSupports SPM

Maintained by Rob Nash.




The collapsing mechanism can be installed in just a few minutes. But the creative design and styling of the UI, is at the mercy of the implementing developer.

demo

Take a look at the demo App by running the XCode scheme 'CollapsibleTableDemo'

Installation

CocoaPods:

Add the line pod "CollapsibleTable" to your Podfile

Carthage:

Add the line github "rob-nash/CollapsibleTable" to your Cartfile

Usage

First subclass UITableViewHeaderFooterView

import UIKit

class ArrowSectionHeaderView: UITableViewHeaderFooterView
{
    @IBOutlet fileprivate weak var mainTitleLabel: UILabel!
    
    @IBOutlet fileprivate weak var arrowImageView: UIImageView?
    
    fileprivate var isRotating = false
}

Then conform to the HeaderFooterViewCollapsible protocol

import CollapsibleTable

extension ArrowSectionHeaderView: HeaderFooterViewCollapsible
{
    func updateTitle(with value: String) {
        mainTitleLabel.text = value
    }
    
    func open(animated: Bool) {
        if animated == true && isRotating == false {
            isRotating = true
            UIView.animate(withDuration: 0.2, delay: 0, options: [.curveLinear,.allowUserInteraction], animations: {
                self.arrowImageView?.transform = .identity
            }, completion: { _ in
                self.isRotating = false
            })
        } else {
            layer.removeAllAnimations()
            arrowImageView?.transform = .identity
            isRotating = false
        }
    }
    
    func close(animated: Bool) {
        let angle = radians(degrees: 90)
        let transform = CGAffineTransform(rotationAngle: angle)
        if animated == true && isRotating == false {
            isRotating = true
            UIView.animate(withDuration: 0.2, delay: 0, options: [.curveLinear,.allowUserInteraction], animations: {
                self.arrowImageView?.transform = transform
            }, completion: { _ in
                self.isRotating = false
            })
        } else {
            layer.removeAllAnimations()
            arrowImageView?.transform = transform
            isRotating = false
        }
    }

    private func radians(degrees: CGFloat) -> CGFloat {
        return .pi * degrees / 180
    }
    
    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesEnded(touches, with: event)
        guard let t = touches.first else { return }
        let point = t.location(in: self)
        respondToTouchAtPoint(point)
    }
}

Create a collection of instances that represent each section of your table view.

import Foundation

class Food
{
    /// The section title
    fileprivate let title: String
    
    /// The rows
    fileprivate var items: [Item]
    
    /// Are rows hidden
    fileprivate var isHidden = false
    
    init(title: String, items: [Item], isHidden: Bool) {
        self.title = title
        self.items = items
        self.isHidden = isHidden
    }
}

class Item
{
    let title: String
    var isSelected: Bool
    
    init(title: String, isSelected: Bool = false) {
        self.title = title
        self.isSelected = isSelected
    }
}

Each model instance representing your table view sections, must conform to CollapsibleTableSectionDatasource.

extension Food: CollapsibleTableSectionDatasource
{
    typealias TableRow = Item
    
    var rows: [Item] {
        get {
            return items
        }
        set(newValue) {
            items = newValue
        }
    }
    
    var state: RowVisibility {
        get {
            return isHidden ? .collapsed : .expanded
        }
        set(newValue) {
            switch newValue {
            case .collapsed:
                isHidden = true
            case .expanded:
                isHidden = false
            }
        }
    }
    
    var sectionTitle: String {
        return title
    }
    
    var sectionHeaderNibName: String {
        return "ArrowSectionHeaderView"
    }
    
    var sectionHeaderViewIdentifier: String {
        return "ArrowSectionHeaderViewID"
    }
    
    var sectionHeaderNibBundle: Bundle {
        return .main
    }
}

Create a UITableView subclass that conforms to TableCollapsible.

import UIKit
import CollapsibleTable

class FoodShoppingTableView: UITableView, TableCollapsible
{
    typealias TableSection = Food
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        separatorStyle = .none
        let nib = UINib(nibName: "ArrowSectionHeaderView", bundle: nil)
        register(nib, forHeaderFooterViewReuseIdentifier: "ArrowSectionHeaderViewID")
        observeSectionHeaders()
    }
    
    deinit {
        stopObservingSectionHeaders()
    }
}

Create a datasource by subclassing CollapsibleTableDatasource

import UIKit
import CollapsibleTable

class FoodShoppingTableViewDatasource: CollapsibleTableDatasource<Food>
{
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell: CustomCell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! CustomCell
        let section: Food = sections[indexPath.section]
        let item: Item = section.rows[indexPath.row]
        cell.mainTitleLabel?.text = item.title
        return cell
    }
}

Then connect it all up

import UIKit
import CollapsibleTable

class ViewController: UIViewController
{
    @IBOutlet private weak var tableView: FoodShoppingTableView! {
        didSet {
            let appDelegate = UIApplication.shared.delegate as? AppDelegate
            tableView.dataSource = appDelegate?.foodShoppingTableViewDatasource
        }
    }
}

Donations.

If you like this and you want to buy me a drink, use bitcoin.

Bitcoin Image

Bitcoin Address: 15Gj4DBTzSujnJrfRZ6ivrR9kDnWXNPvNQ