Latest 0.9.0
Homepage https://github.com/GargoyleSoft/ExpandableDatePicker
License MIT
Platforms ios 10.0
Frameworks UIKit
Authors

Usage

ExpandableDatePicker is a library written in Swift which makes the display of a drop-down UIDatePicker much simpler. It also includes
a table row to select the TimeZone that should be used with the date, which is especially helpful when creating calendar items.

For the cell you want to click on that causes the two new rows to appear and disappear you simply need to add the ShowsDatePicker protocol.
There is nothing to implement from that protocol, it just needs to exist on the UITableViewCell subclass. If you just want a simple "Right Detail"
cell you can use the provided ExpandableDatePickerSelectionCell class.

Demo

Adding To An Existing View Controller

  • Import the ExpandableDatePicker module
  • Add the ExpandableDatePicker protocol to your view controller
  • Add the three variables defined by the protocol (The tableView likely already exists…)
  • Replace all instances of indexPath in your UITableViewDelegate and UITableViewDataSource with edpUpdatedModelIndexPath(for:)

viewDidLoad()

Call tableView.registerExpandableDatePicker(). This registers the cells used internally and sets the estimatedRowHeight of the UITableView to 44.0 and sets the rowHeight to UITableViewAutomaticDimension as the UIDatePicker requires expandable cells.

tableView(_:cellForRowAt:)

The very start of this method should check edpShouldShowDatePicker(at:) and edpShouldShowTimeZoneRow(at:) before doing anything else, and taking the appropriate action based on the return values. See the example below for full details.

For the row which is supposed to return the current value of the date, and the one which is tapped on to expand into the date picker and time zone picker rows, you should return an ExpandableDatePickerSelectionCell unless you need something more custom. Simply grab a reusable cell, set the detailTextLabel and return the cell. See below for an example.

tableView(_:accessoryButtonTappedForRowWith:) and tableView(_:didSelectRowAt:)

You definitely need to call edpTableCellWasSelected(at:) first and examine the output. You’ll either get back nil or an updated IndexPath value. If it’s nil then they tapped on the row which is used to pick a time zone. You simply push an ExpandableDatePickerTimeZoneTableViewController (whew!) onto your UINavigationController. Your callback will be given a TimeZone object which you should store in your local data model and then update your UITableView. You can pass an optional segmentTintColor as a parameter to the constructor if you need the segment placed in the navigation bar to have a different color than the default Apple uses.

tableView(_:numberOfRowsInSection)

Add edpDatePickerRowsShowing to your output. This will either be 0, 1, or 2 depending on whether the extra rows are showing or not.

Example Implementation

You can use the below class as your starting point as it implements all the pieces required by the protocol.

import UIKit
import ExpandableDatePicker

class ViewController: UITableViewController, ExpandableDatePicker {
    // Not used directly by you, but is part of the protocol so the framework can use it.
    var edpIndexPath: IndexPath?

    // Whether or not the expansion should include a TimeZone row selector.
    var edpShowTimeZoneRow = true

    fileprivate var rowThatTogglesDatePicker: Int!

    fileprivate var selectedDate = Date()
    fileprivate var selectedTimeZone = TimeZone.autoupdatingCurrent

    override func viewDidLoad() {
        super.viewDidLoad()

        tableView.registerExpandableDatePicker()
    }
}

// MARK: - UITableViewDataSource
extension ViewController {
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        if edpShouldShowDatePicker(at: indexPath) {
            let cell = ExpandableDatePickerCell.reusableCell(for: indexPath, in: tableView)
            cell.onDateChanged = {
                [unowned self] date in
                self.selectedDate = date
                self.tableView.reloadRows(at: [self.edpLabelIndexPath!], with: .automatic)
            }

            cell.datePicker.date = selectedDate

            return cell
        } else if edpShouldShowTimeZoneRow(at: indexPath) {
            return ExpandableDatePickerTimeZoneCell.reusableCell(for: indexPath, in: tableView, timeZone: selectedTimeZone)
        }

        let modelIndexPath = edpUpdatedModelIndexPath(for: indexPath)

        if modelIndexPath.row == rowThatExpandsToDatePicker {
            let cell = ExpandableDatePickerSelectionCell.reusableCell(for: indexPath, in: tableView)
            cell.detailTextLabel!.text = DateFormatter.localizedString(from: selectedDate, dateStyle: .short, timeStyle: .none)

            return cell
        }

        let cell = tableView.dequeueReusableCell(withIdentifier: "normal", for: indexPath)
        cell.textLabel!.text = tableData[modelIndexPath.row]
        return cell
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return tableData.count + edpDatePickerRowsShowing
    }
}

// MARK: - UITableViewDelegate
extension ViewController {
    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        guard let modelIndexPath = edpTableCellWasSelected(at: indexPath) else {
            // If tableCellWasSelected(at:) returns nil, they clicked on the time zone selector row.
            let vc = ExpandableDatePickerTimeZoneTableViewController {
                [unowned self] timeZone in
                self.selectedTimeZone = timeZone
                tableView.reloadRows(at: [indexPath], with: .automatic)
            }

            navigationController!.pushViewController(vc, animated: true)

            return
        }

        // modelIndexPath is the new indexPath you use for which row was selected.
    }
}

Subclassing

If you wish to subclass either ExpandableDatePickerCell or ExpandableDatePickerTimeZoneCell simply register the cell yourself after the
call to registerExpandableDatePicker(). Both classes provide a static identifier property that you can use for registering your cell.

    override func viewDidLoad() {
        super.viewDidLoad()

        tableView.registerExpandableDatePicker()

        tableView.register(MyCoolTimeZoneSubclassCell.self, forCellReuseIdentifier: ExpandableDatePickerTimeZoneCell.identifier)
    }

Installation

CocoaPods

CocoaPods is a dependency manager for Cocoa projects. You can install it with the following command:

$ gem install cocoapods

To integrate ExpandableDatePicker into your Xcode project using CocoaPods, specify it in your Podfile:

source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '10.0'
use_frameworks!

target '<Your Target Name>' do
    pod 'ExpandableDatePicker'
end

Then, run the following command:

$ pod install

Latest podspec

{
    "name": "ExpandableDatePicker",
    "platforms": {
        "ios": "10.0"
    },
    "summary": "Expandable UIDatePicker and TimeZone selection for UITableView",
    "version": "0.9.0",
    "license": "MIT",
    "authors": {
        "Scott Grosch": "[email protected]"
    },
    "homepage": "https://github.com/GargoyleSoft/ExpandableDatePicker",
    "source": {
        "git": "https://github.com/GargoyleSoft/ExpandableDatePicker.git",
        "tag": "0.9.0"
    },
    "frameworks": "UIKit",
    "source_files": "ExpandableDatePicker/**/*.{swift}",
    "pushed_with_swift_version": "3.0.2"
}

Pin It on Pinterest

Share This