Home

Awesome

Savory Carthage compatible GitHub license

Savory is a swift accordion view implementation.

screencast

Requirements

Installation

Usage

import UIKit
import Savory

class ViewController: UIViewController, SavoryViewDelegate {

    var savoryView: SavoryView!

    override func viewDidLoad() {
        super.viewDidLoad()

        /* 1. Initiate SavoryView */
        savoryView = SavoryView(frame: view.frame)
        // offset for status bar
        savoryView.frame.origin.y = 20
        savoryView.frame.size.height -= 20 

        /* 2. Provide a implementation of SavoryStateProtivder protocol */
        // Savory providers a simple implementation - SimpleStateProvider
        savoryView.stateProvider = SimpleStateProvider([.expanded, .collapsed, .collapsed])

        /* 3. Set the reuse identifiers for header and body cells */
        savoryView.headerIdentifier = "header"
        savoryView.bodyIdentifier = "body"

        /* 4. register the reuse identifiers */
        savoryView.register(UITableViewCell.self, forCellReuseIdentifier: "header")
        savoryView.register(UITableViewCell.self, forCellReuseIdentifier: "body")

        /* 5. set the savoryDelegate */
        savoryView.savoryDelegate = self

        /* 6. add subview */
        view.addSubview(savoryView)
    }

    func headerCell(forPanelAt index: SavoryPanelIndex, in savoryView: SavoryView) -> SavoryHeaderCell {

        /* Dequeues a reusable cell */
        // SavoryView provides a method to dequeue reusable header cell for certain index
        // SavoryView internally dequeues a cell using its headerIdentifier as reuse identifier
        let cell = savoryView.dequeueReusableHeaderCell(forPanelAt: index)

        /* customize the cell */
        cell.selectionStyle = .none
        cell.textLabel?.text = "Header \(index)"
        cell.backgroundColor = UIColor.lightGray

        return cell
    }

    func bodyCell(forPanelAt index: SavoryPanelIndex, in savoryView: SavoryView) -> SavoryBodyCell {

        // almost same as headerCell(forPanelAt:in:)
        let cell = savoryView.dequeueReusableBodyCell(forPanelAt: index)
        cell.textLabel?.numberOfLines = 0
        cell.textLabel?.text = [String](repeating: "Body \(index)", count: index + 1).joined(separator: "\n")
        return cell
    }
}

Savory also can be used along with Interface Builder:

Reference

Glossary

SavoryView

Accordion effect view developed on UITableView.

SavoryStateProvider

SavoryView renders its panels as collapsed or expanded based on its stateProvider property.

Actually [SavoryPanelState] type "conforms to" this protocol naturally. However, because swift hasn't supported an extension like below yet( may come soon according to apple/swift)

extension Array: SavoryStateProvider where Element == SavoryPanelState {} 

Savory provides a wrapper implementation as SimpleStateProvider and it requires a [SavoryPanelState] for initializing and then it acts just like a stored array from SavoryView's perspective.

For most circumstances, this is sufficient unless you want to provide some complex state managing feature.

Remember to call reloadData on SavoryView if you provided a custom implementation of SavoryStateProvider and it changes its state programatically

TODO

License

Savory is released under the MIT license. See LICENSE for details.