diff --git a/Example/ViewController.swift b/Example/ViewController.swift index c55e9c4..6dc986c 100644 --- a/Example/ViewController.swift +++ b/Example/ViewController.swift @@ -33,6 +33,7 @@ class ViewController: TableViewController { tableView.estimatedSectionHeaderHeight = 13.5 tableView.estimatedSectionFooterHeight = 13.5 + dataSource = DataSource(tableViewDelegate: self) dataSource.sections = [ Section(header: "Styles", rows: [ Row(text: "Value 1", detailText: "Detail", cellClass: Value1Cell.self), @@ -89,6 +90,25 @@ class ViewController: TableViewController { } } +extension TableViewController: UITableViewDelegate { + // MARK: - UIScrollViewDelegate example functions + public func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { + // You can get UIScrollViewDelegate functions forwarded, even though the `DataSource` instance is the true delegate + // ... + } + + // MARK: - UITableViewDelegate example functions + public func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { + // You can get UITableViewDelegate functions forwarded, even though the `DataSource` instance is the true delegate + // ... + } + + public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + // The Row object's `selection` property will handle most of your use cases, but + // if you need to do something additional you can still implement this function. + } +} + class LargeAutoSizedExtremityView: UIView { lazy var label: UILabel = { let label = UILabel() diff --git a/Static.podspec b/Static.podspec index 0253a77..6e60bdd 100644 --- a/Static.podspec +++ b/Static.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |spec| spec.name = 'Static' - spec.version = '2.2.0' + spec.version = '2.3.0' spec.summary = 'Simple static table views for iOS in Swift.' spec.description = 'Static provides simple static table views for iOS in Swift.' spec.homepage = 'https://github.com/venmo/static' diff --git a/Static/DataSource.swift b/Static/DataSource.swift index dcb89d0..670ab25 100644 --- a/Static/DataSource.swift +++ b/Static/DataSource.swift @@ -48,12 +48,13 @@ public class DataSource: NSObject { // MARK: - Initializers - /// Initialize with optional `tableView` and `sections`. - public init(tableView: UITableView? = nil, sections: [Section]? = nil) { + /// Initialize with optional `tableView`, `sections` and `tableViewDelegate`. + public init(tableView: UITableView? = nil, sections: [Section]? = nil, tableViewDelegate: UITableViewDelegate? = nil) { assert(Thread.isMainThread, "You must access Static.DataSource from the main thread.") self.tableView = tableView self.sections = sections ?? [] + self.tableViewDelegate = tableViewDelegate super.init() @@ -73,6 +74,23 @@ public class DataSource: NSObject { return row(at: indexPath) } + // MARK: - Forwarding UITableViewDelegate messages + + /// If you have a use for `UITableViewDelegate` or `UIScrollViewDelegate` messages, you can use this property to receive those messages. `DataSource` needs to be the `UITableView` instance's true `delegate`, but will forward messages to this property. + /// You must pass this in the `init` function. + weak public private(set) var tableViewDelegate: UITableViewDelegate? + + override public func forwardingTarget(for aSelector: Selector!) -> Any? { + if let forwardDelegate = tableViewDelegate, forwardDelegate.responds(to: aSelector) { + return forwardDelegate + } else { + return super.forwardingTarget(for: aSelector) + } + } + + override public func responds(to aSelector: Selector!) -> Bool { + return super.responds(to: aSelector) || tableViewDelegate?.responds(to: aSelector) == true + } // MARK: - Private @@ -275,12 +293,16 @@ extension DataSource: UITableViewDelegate { if let row = row(at: indexPath) { row.selection?() } + + tableViewDelegate?.tableView?(tableView, didSelectRowAt: indexPath) } public func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) { if let row = row(at: indexPath) { row.accessory.selection?() } + + tableViewDelegate?.tableView?(tableView, accessoryButtonTappedForRowWith: indexPath) } } diff --git a/Static/Tests/DataSourceTests.swift b/Static/Tests/DataSourceTests.swift index 5cfca0a..0f9c775 100644 --- a/Static/Tests/DataSourceTests.swift +++ b/Static/Tests/DataSourceTests.swift @@ -138,4 +138,31 @@ class DataSourceTests: XCTestCase { XCTAssertEqual(dataSource, tableView2.dataSource as? DataSource) XCTAssertEqual(dataSource, tableView2.delegate as? DataSource) } + + func testTableViewDelegateForwarding() { + // Sample object that conforms to `UITableViewDelegate` protocol + class TestTableViewDelegate: NSObject, UITableViewDelegate { + static var delegateDidForward = false + + func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { + TestTableViewDelegate.delegateDidForward = true + } + } + + let sampleDelegate = TestTableViewDelegate() + let dataSource2 = DataSource(tableViewDelegate: sampleDelegate) + + XCTAssertNotNil(dataSource2.tableViewDelegate) + + let forwardingTarget = dataSource2.forwardingTarget(for: #selector(UITableViewDelegate.tableView(_:willDisplay:forRowAt:))) + XCTAssertNotNil(forwardingTarget) + XCTAssertNotNil(forwardingTarget as? TestTableViewDelegate) + + // Test actual message + TestTableViewDelegate.delegateDidForward = false + + (dataSource2 as UITableViewDelegate).tableView!(UITableView(), willDisplay: UITableViewCell(), forRowAt: IndexPath()) + XCTAssertTrue(TestTableViewDelegate.delegateDidForward) + + } }