├── Screenshots
├── ss-load-more.png
├── ss-initial-loading.png
├── ss-load-more-error.png
├── ss-pull-to-refresh.png
└── ss-initial-load-error.png
├── Demo
├── Demo.xcodeproj
│ ├── project.xcworkspace
│ │ └── contents.xcworkspacedata
│ └── project.pbxproj
├── Podfile
├── Demo.xcworkspace
│ └── contents.xcworkspacedata
├── Podfile.lock
├── DemoTests
│ ├── Info.plist
│ └── DemoTests.swift
├── DemoUITests
│ ├── Info.plist
│ └── DemoUITests.swift
└── Demo
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ └── AppIcon.appiconset
│ │ └── Contents.json
│ ├── Info.plist
│ ├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
│ └── ViewController.swift
├── StatefulTableView.podspec
├── LICENSE
├── Sources
├── StatefulTableView+State.swift
├── StatefulTableView+InitialLoad.swift
├── StatefulTableView+PullToRefresh.swift
├── StatefulTableView+LoadMore.swift
├── StatefulTableDelegate.swift
├── StatefulTableView.swift
├── StatefulTableView+Views.swift
└── StatefulTableView+UITableView.swift
├── .gitignore
└── README.md
/Screenshots/ss-load-more.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adamghill/StatefulTableView/master/Screenshots/ss-load-more.png
--------------------------------------------------------------------------------
/Screenshots/ss-initial-loading.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adamghill/StatefulTableView/master/Screenshots/ss-initial-loading.png
--------------------------------------------------------------------------------
/Screenshots/ss-load-more-error.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adamghill/StatefulTableView/master/Screenshots/ss-load-more-error.png
--------------------------------------------------------------------------------
/Screenshots/ss-pull-to-refresh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adamghill/StatefulTableView/master/Screenshots/ss-pull-to-refresh.png
--------------------------------------------------------------------------------
/Screenshots/ss-initial-load-error.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adamghill/StatefulTableView/master/Screenshots/ss-initial-load-error.png
--------------------------------------------------------------------------------
/Demo/Demo.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Demo/Podfile:
--------------------------------------------------------------------------------
1 | platform :ios, '8.0'
2 | use_frameworks!
3 |
4 | target 'Demo' do
5 |
6 | pod 'StatefulTableView', :path => '../'
7 |
8 | end
9 |
10 | target 'DemoTests' do
11 |
12 | end
13 |
14 | target 'DemoUITests' do
15 |
16 | end
17 |
18 |
--------------------------------------------------------------------------------
/Demo/Demo.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Demo/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - StatefulTableView (0.0.14)
3 |
4 | DEPENDENCIES:
5 | - StatefulTableView (from `../`)
6 |
7 | EXTERNAL SOURCES:
8 | StatefulTableView:
9 | :path: "../"
10 |
11 | SPEC CHECKSUMS:
12 | StatefulTableView: 8b061afb2fb9e7b9ece1f996b0ae925c1ad586d3
13 |
14 | PODFILE CHECKSUM: e6944d74483536f64ec788f95fbe36a46c8b7346
15 |
16 | COCOAPODS: 1.0.0
17 |
--------------------------------------------------------------------------------
/StatefulTableView.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 | s.name = 'StatefulTableView'
3 | s.version = '0.0.15'
4 | s.license = {
5 | :type => 'MIT',
6 | :file => 'LICENSE'
7 | }
8 | s.homepage = 'http://github.com/timominous/StatefulTableView'
9 | s.description = 'Custom UITableView container class that supports pull-to-refresh, load-more, initial load, and empty states. Swift port of SKStatefulTableViewController'
10 | s.summary = 'Custom UITableView container class that supports pull-to-refresh, load-more, initial load, and empty states.'
11 | s.author = {
12 | 'timominous' => 'timominous@gmail.com'
13 | }
14 | s.source = {
15 | :git => 'https://github.com/timominous/StatefulTableView.git',
16 | :tag => s.version.to_s
17 | }
18 | s.ios.deployment_target = "8.0"
19 | s.source_files = 'Sources/*.swift'
20 | s.requires_arc = true
21 | end
--------------------------------------------------------------------------------
/Demo/DemoTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Demo/DemoUITests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Demo/Demo/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // Demo
4 | //
5 | // Created by Tim on 12/05/2016.
6 | // Copyright © 2016 timominous. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 | var window: UIWindow?
15 |
16 | func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
17 | return true
18 | }
19 |
20 | func applicationWillResignActive(application: UIApplication) {
21 | }
22 |
23 | func applicationDidEnterBackground(application: UIApplication) {
24 | }
25 |
26 | func applicationWillEnterForeground(application: UIApplication) {
27 | }
28 |
29 | func applicationDidBecomeActive(application: UIApplication) {
30 | }
31 |
32 | func applicationWillTerminate(application: UIApplication) {
33 | }
34 |
35 | }
36 |
37 |
--------------------------------------------------------------------------------
/Demo/DemoTests/DemoTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DemoTests.swift
3 | // DemoTests
4 | //
5 | // Created by Tim on 12/05/2016.
6 | // Copyright © 2016 timominous. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import Demo
11 |
12 | class DemoTests: XCTestCase {
13 |
14 | override func setUp() {
15 | super.setUp()
16 | // Put setup code here. This method is called before the invocation of each test method in the class.
17 | }
18 |
19 | override func tearDown() {
20 | // Put teardown code here. This method is called after the invocation of each test method in the class.
21 | super.tearDown()
22 | }
23 |
24 | func testExample() {
25 | // This is an example of a functional test case.
26 | // Use XCTAssert and related functions to verify your tests produce the correct results.
27 | }
28 |
29 | func testPerformanceExample() {
30 | // This is an example of a performance test case.
31 | self.measureBlock {
32 | // Put the code you want to measure the time of here.
33 | }
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 timominous
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Demo/DemoUITests/DemoUITests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DemoUITests.swift
3 | // DemoUITests
4 | //
5 | // Created by Tim on 12/05/2016.
6 | // Copyright © 2016 timominous. All rights reserved.
7 | //
8 |
9 | import XCTest
10 |
11 | class DemoUITests: XCTestCase {
12 |
13 | override func setUp() {
14 | super.setUp()
15 |
16 | // Put setup code here. This method is called before the invocation of each test method in the class.
17 |
18 | // In UI tests it is usually best to stop immediately when a failure occurs.
19 | continueAfterFailure = false
20 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method.
21 | XCUIApplication().launch()
22 |
23 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
24 | }
25 |
26 | override func tearDown() {
27 | // Put teardown code here. This method is called after the invocation of each test method in the class.
28 | super.tearDown()
29 | }
30 |
31 | func testExample() {
32 | // Use recording to get started writing UI tests.
33 | // Use XCTAssert and related functions to verify your tests produce the correct results.
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/Demo/Demo/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "29x29",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "29x29",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "40x40",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "40x40",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "60x60",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "60x60",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "ipad",
35 | "size" : "29x29",
36 | "scale" : "1x"
37 | },
38 | {
39 | "idiom" : "ipad",
40 | "size" : "29x29",
41 | "scale" : "2x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "40x40",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "40x40",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "76x76",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "76x76",
61 | "scale" : "2x"
62 | }
63 | ],
64 | "info" : {
65 | "version" : 1,
66 | "author" : "xcode"
67 | }
68 | }
--------------------------------------------------------------------------------
/Sources/StatefulTableView+State.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StatefulTableView+State.swift
3 | // Pods
4 | //
5 | // Created by Tim on 23/06/2016.
6 | //
7 | //
8 |
9 | import Foundation
10 |
11 | extension StatefulTableView {
12 | // MARK: - States
13 |
14 | internal func setState(_ newState: State) {
15 | setState(newState, updateView: true, error: nil)
16 | }
17 |
18 | internal func setState(_ newState: State, error: NSError?) {
19 | setState(newState, updateView: true, error: error)
20 | }
21 |
22 | internal func setState(_ newState: State, updateView: Bool, error: NSError?) {
23 | state = newState
24 |
25 | switch state {
26 | case .initialLoading:
27 | resetdynamicContentView(withChildView: viewForInitialLoad)
28 | case .emptyOrInitialLoadError:
29 | resetdynamicContentView(withChildView: viewForEmptyInitialLoad(withError: error))
30 | default: break
31 | }
32 |
33 | switch state {
34 | case .idle:
35 | watchForLoadMoreIfApplicable(true)
36 | case .emptyOrInitialLoadError:
37 | watchForLoadMoreIfApplicable(false)
38 | default: break
39 | }
40 |
41 | if updateView {
42 | let mode: ViewMode
43 |
44 | switch state {
45 | case .initialLoading: fallthrough
46 | case .emptyOrInitialLoadError:
47 | mode = .static
48 | default: mode = .table
49 | }
50 |
51 | viewMode = mode
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/Demo/Demo/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UIRequiredDeviceCapabilities
30 |
31 | armv7
32 |
33 | UISupportedInterfaceOrientations
34 |
35 | UIInterfaceOrientationPortrait
36 | UIInterfaceOrientationLandscapeLeft
37 | UIInterfaceOrientationLandscapeRight
38 |
39 | UISupportedInterfaceOrientations~ipad
40 |
41 | UIInterfaceOrientationPortrait
42 | UIInterfaceOrientationPortraitUpsideDown
43 | UIInterfaceOrientationLandscapeLeft
44 | UIInterfaceOrientationLandscapeRight
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/Sources/StatefulTableView+InitialLoad.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StatefulTableView+InitialLoad.swift
3 | // Pods
4 | //
5 | // Created by Tim on 23/06/2016.
6 | //
7 | //
8 |
9 | import UIKit
10 |
11 | extension StatefulTableView {
12 | // MARK: - Initial load
13 |
14 | /**
15 | Triggers initial load of data programatically. Defaults to hiding the tableView.
16 |
17 | - returns: Boolean for success status.
18 | */
19 | public func triggerInitialLoad() -> Bool {
20 | return triggerInitialLoad(false)
21 | }
22 |
23 | /**
24 | Triggers initial load of data programatically.
25 |
26 | - parameter shouldShowTableView: Control if the container should show the tableView or not.
27 |
28 | - returns: Boolean for success status.
29 | */
30 | public func triggerInitialLoad(_ shouldShowTableView: Bool) -> Bool {
31 | guard !state.isLoading else { return false }
32 |
33 | if shouldShowTableView {
34 | self.setState(.initialLoadingTableView)
35 | } else {
36 | self.setState(.initialLoading)
37 | }
38 |
39 | if let delegate = statefulDelegate {
40 | delegate.statefulTableViewWillBeginInitialLoad(self, handler: { [weak self](tableIsEmpty, errorOrNil) in
41 | DispatchQueue.main.async(execute: {
42 | self?.setHasFinishedInitialLoad(tableIsEmpty, error: errorOrNil)
43 | })
44 | })
45 | }
46 |
47 | return true
48 | }
49 |
50 | fileprivate func setHasFinishedInitialLoad(_ tableIsEmpty: Bool, error: NSError?) {
51 | guard state.isInitialLoading else { return }
52 |
53 | if tableIsEmpty {
54 | self.setState(.emptyOrInitialLoadError, updateView: true, error: error)
55 | } else {
56 | self.setState(.idle)
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by https://www.gitignore.io/api/swift
2 |
3 | ### Swift ###
4 | # Xcode
5 | #
6 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
7 |
8 | ## Build generated
9 | build/
10 | DerivedData/
11 |
12 | ## Various settings
13 | *.pbxuser
14 | !default.pbxuser
15 | *.mode1v3
16 | !default.mode1v3
17 | *.mode2v3
18 | !default.mode2v3
19 | *.perspectivev3
20 | !default.perspectivev3
21 | xcuserdata/
22 |
23 | ## Other
24 | *.moved-aside
25 | *.xcuserstate
26 |
27 | ## Obj-C/Swift specific
28 | *.hmap
29 | *.ipa
30 | *.dSYM.zip
31 | *.dSYM
32 |
33 | ## Playgrounds
34 | timeline.xctimeline
35 | playground.xcworkspace
36 |
37 | # Swift Package Manager
38 | #
39 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
40 | # Packages/
41 | .build/
42 |
43 | # CocoaPods
44 | #
45 | # We recommend against adding the Pods directory to your .gitignore. However
46 | # you should judge for yourself, the pros and cons are mentioned at:
47 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
48 | #
49 | Demo/Pods/
50 |
51 | # Carthage
52 | #
53 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
54 | # Carthage/Checkouts
55 |
56 | Carthage/Build
57 |
58 | # fastlane
59 | #
60 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
61 | # screenshots whenever they are needed.
62 | # For more information about the recommended setup visit:
63 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md
64 |
65 | fastlane/report.xml
66 | fastlane/Preview.html
67 | fastlane/screenshots
68 | fastlane/test_output
69 |
--------------------------------------------------------------------------------
/Sources/StatefulTableView+PullToRefresh.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StatefulTableView+PullToRefresh.swift
3 | // Pods
4 | //
5 | // Created by Tim on 23/06/2016.
6 | //
7 | //
8 |
9 | import UIKit
10 |
11 | extension StatefulTableView {
12 | // MARK: - Pull to refresh
13 |
14 | func refreshControlValueChanged() {
15 | if state != .loadingFromPullToRefresh && !state.isLoading {
16 | if (!triggerPullToRefresh()) {
17 | refreshControl.endRefreshing()
18 | }
19 | } else {
20 | refreshControl.endRefreshing()
21 | }
22 | }
23 |
24 | /**
25 | Triggers pull to refresh programatically. Also called when the user pulls down to refresh on the tableView.
26 |
27 | - returns: Boolean for success status.
28 | */
29 | public func triggerPullToRefresh() -> Bool {
30 | guard !state.isLoading && canPullToRefresh else { return false }
31 |
32 | self.setState(.loadingFromPullToRefresh, updateView: false, error: nil)
33 |
34 | if let delegate = statefulDelegate {
35 | delegate.statefulTableViewWillBeginLoadingFromRefresh(self, handler: { [weak self](tableIsEmpty, errorOrNil) in
36 | DispatchQueue.main.async(execute: {
37 | self?.setHasFinishedLoadingFromPullToRefresh(tableIsEmpty, error: errorOrNil)
38 | })
39 | })
40 | }
41 |
42 | refreshControl.beginRefreshing()
43 |
44 | return true
45 | }
46 |
47 | fileprivate func setHasFinishedLoadingFromPullToRefresh(_ tableIsEmpty: Bool, error: NSError?) {
48 | guard state == .loadingFromPullToRefresh else { return }
49 |
50 | refreshControl.endRefreshing()
51 |
52 | if tableIsEmpty {
53 | self.setState(.emptyOrInitialLoadError, updateView: true, error: error)
54 | } else {
55 | self.setState(.idle)
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/Demo/Demo/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # StatefulTableView
2 | [](http://cocoadocs.org/docsets/StatefulTableView)
3 | [](http://cocoadocs.org/docsets/StatefulTableView)
4 | [](http://cocoadocs.org/docsets/StatefulTableView)
5 | [](http://cocoadocs.org/docsets/StatefulTableView)
6 |
7 | Custom UITableView container class that supports pull-to-refresh, load-more, initial load, and empty states. This library aims to be a drop in replacement for `UITableView`. Swift port of [SKStatefulTableViewController](http://github.com/shiki/SKStatefulTableViewController).
8 |
9 | This is a *work in progress*. A lot of things may break as of the moment.
10 |
11 | ## Screenshots
12 |
13 | Initial loading:
14 |
15 |
16 |
17 | Pull-to-refresh:
18 |
19 |
20 |
21 | Load more:
22 |
23 |
24 |
25 | Initial load error:
26 |
27 |
28 |
29 | Load more error:
30 |
31 |
32 |
33 | ## Usage
34 |
35 | Currently, you can only assign the delegates and data source through code.
36 |
37 | ```swift
38 | tableView.dataSource = self // Confofrms to UITableViewDataSource
39 | tableView.delegate = self // Conforms to UITableViewDelegate
40 | tableView.statefulDelegate = self // Conforms to StatefulTableDelegate
41 | ```
42 |
43 | For initial loading, pull-to-refresh, and load more, you have to implement the following statefulDelegate methods:
44 |
45 | ```swift
46 | func statefulTableViewWillBeginInitialLoad(tvc: StatefulTableView, handler: InitialLoadCompletionHandler)
47 | func statefulTableViewWillBeginLoadingFromRefresh(tvc: StatefulTableView, handler: InitialLoadCompletionHandler)
48 | func statefulTableViewWillBeginLoadingMore(tvc: StatefulTableView, handler: LoadMoreCompletionHandler)
49 | ```
50 |
51 | To show custom views, return them through the following statefulDelegate methods. Otherwise, return `nil`.
52 |
53 | ```swift
54 | func statefulTableViewViewForInitialLoad(tvc: StatefulTableView) -> UIView?
55 | func statefulTableViewInitialErrorView(tvc: StatefulTableView, forInitialLoadError: NSError?) -> UIView?
56 | func statefulTableViewLoadMoreErrorView(tvc: StatefulTableView, forLoadMoreError: NSError?) -> UIView?
57 | ```
58 |
59 | ## Installation
60 |
61 | ### Cocoapods
62 |
63 | Add this to your Podfile.
64 |
65 | ```ruby
66 | pod 'StatefulTableView', '0.0.15'
67 | ```
68 |
69 | ### Credits
70 |
71 | * [SKStatefulTableViewController](http://github.com/shiki/SKStatefulTableViewController) by [shiki](http://github.com/shiki)
72 |
--------------------------------------------------------------------------------
/Demo/Demo/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/Sources/StatefulTableView+LoadMore.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StatefulTableView+LoadMore.swift
3 | // Pods
4 | //
5 | // Created by Tim on 23/06/2016.
6 | //
7 | //
8 |
9 | import UIKit
10 |
11 | extension StatefulTableView {
12 | // MARK: - Load more
13 |
14 | /**
15 | Tiggers loading more of data. Also called when the scroll content offset reaches the `loadMoreTriggerThreshold`.
16 | */
17 | public func triggerLoadMore() {
18 | guard !state.isLoading else { return }
19 |
20 | loadMoreViewIsErrorView = false
21 | lastLoadMoreError = nil
22 | updateLoadMoreView()
23 |
24 | setState(.loadingMore)
25 |
26 | if let delegate = statefulDelegate {
27 | delegate.statefulTableViewWillBeginLoadingMore(self, handler: { [weak self](canLoadMore, errorOrNil, showErrorView) in
28 | DispatchQueue.main.async(execute: {
29 | self?.setHasFinishedLoadingMore(canLoadMore, error: errorOrNil, showErrorView: showErrorView)
30 | })
31 | })
32 | }
33 | }
34 |
35 | internal func updateLoadMoreView() {
36 | if watchForLoadMore || lastLoadMoreError != nil {
37 | tableView.tableFooterView = viewForLoadingMore(withError: (loadMoreViewIsErrorView ? lastLoadMoreError : nil))
38 | } else {
39 | tableView.tableFooterView = UIView()
40 | }
41 | }
42 |
43 | internal func viewForLoadingMore(withError error: NSError?) -> UIView? {
44 | if let delegateMethod = statefulDelegate?.statefulTableViewLoadMoreErrorView, error != nil {
45 | return delegateMethod(self, error)
46 | }
47 |
48 | let container = UIView(frame: CGRect(origin: .zero, size: CGSize(width: tableView.bounds.width, height: 44)))
49 |
50 | let sub: UIView
51 |
52 | if let error = error {
53 | let label = UILabel()
54 | label.translatesAutoresizingMaskIntoConstraints = false
55 | label.text = error.localizedDescription
56 | label.font = UIFont.systemFont(ofSize: 12)
57 | label.textAlignment = .center
58 | sub = label
59 | } else {
60 | let activityIndicator = UIActivityIndicatorView(activityIndicatorStyle: .gray)
61 | activityIndicator.translatesAutoresizingMaskIntoConstraints = false
62 | activityIndicator.startAnimating()
63 | sub = activityIndicator
64 | }
65 |
66 | container.addSubview(sub)
67 | centerView(sub, inContainer: container)
68 |
69 | return container
70 | }
71 |
72 | internal func setHasFinishedLoadingMore(_ canLoadMore: Bool, error: NSError?, showErrorView: Bool) {
73 | guard state == .loadingMore else { return }
74 |
75 | self.canLoadMore = canLoadMore
76 | loadMoreViewIsErrorView = (error != nil) && showErrorView
77 | lastLoadMoreError = error
78 |
79 | setState(.idle)
80 | }
81 |
82 | internal func watchForLoadMoreIfApplicable(_ watch: Bool) {
83 | var watch = watch
84 |
85 | if (watch && !canLoadMore) {
86 | watch = false
87 | }
88 | watchForLoadMore = watch
89 | updateLoadMoreView()
90 |
91 | triggerLoadMoreIfApplicable(tableView)
92 | }
93 |
94 | /**
95 | Should be called when scrolling the tableView. This determines when to call `triggerLoadMore`
96 |
97 | - parameter scrollView: The scrolling view.
98 | */
99 | public func scrollViewDidScroll(_ scrollView: UIScrollView) {
100 | triggerLoadMoreIfApplicable(scrollView)
101 | }
102 |
103 | internal func triggerLoadMoreIfApplicable(_ scrollView: UIScrollView) {
104 | guard watchForLoadMore && !loadMoreViewIsErrorView else { return }
105 |
106 | let scrollPosition = scrollView.contentSize.height - scrollView.frame.size.height - scrollView.contentOffset.y
107 | if scrollPosition < loadMoreTriggerThreshold {
108 | triggerLoadMore()
109 | }
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/Sources/StatefulTableDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StatefulTableDelegate.swift
3 | // Demo
4 | //
5 | // Created by Tim on 12/05/2016.
6 | // Copyright © 2016 timominous. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | /**
12 | A closure declaration describing if the table is empty and has an optional error.
13 |
14 | - parameter tableIsEmpty: Describes if the table is empty.
15 | - parameter errorOrNil: Describes the error received from loading. May be nil.
16 | */
17 | public typealias InitialLoadCompletionHandler = (_ tableIsEmpty: Bool, _ errorOrNil: NSError?) -> Void
18 |
19 | /**
20 | A closure declaration describing if the table can load more, received an error, and should show an error view.
21 |
22 | - parameter canLoadMore: Describes if the table can loa dmore data.
23 | - parameter errorOrNil: Describes the error received from loading. May be nil.
24 | - parameter showErrorView: Describes if an error view should be shown.
25 | */
26 | public typealias LoadMoreCompletionHandler = (_ canLoadMore: Bool, _ errorOrNil: NSError?, _ showErrorView: Bool) -> Void
27 |
28 | /**
29 | This protocol represents the loading behavior of the `StatefulTableView`.
30 | */
31 | @objc public protocol StatefulTableDelegate: class {
32 | // MARK: - Managing Loading
33 |
34 | /**
35 | This delegate method will be called when the tableView is triggered to load data initially.
36 |
37 | - parameter tvc: The tableView calling the method.
38 | - parameter handler: The completion handler describing if the table is empty and if there is an error.
39 | */
40 | func statefulTableViewWillBeginInitialLoad(_ tvc: StatefulTableView, handler: @escaping InitialLoadCompletionHandler)
41 |
42 | /**
43 | This delegate method will be called when the user pulls down to refresh.
44 |
45 | - parameter tvc: The tableView calling the method.
46 | - parameter handler: The completion handler describing if the table is empty and if there is an error.
47 | */
48 | func statefulTableViewWillBeginLoadingFromRefresh(_ tvc: StatefulTableView, handler: @escaping InitialLoadCompletionHandler)
49 |
50 | /**
51 | This delegate method will be called when the user scrolls to load more.
52 |
53 | - parameter tvc: The tableView calling the method.
54 | - parameter handler: The completion handler describing if the table can load more, has an error, and should show an error view.
55 | */
56 | func statefulTableViewWillBeginLoadingMore(_ tvc: StatefulTableView, handler: @escaping LoadMoreCompletionHandler)
57 |
58 | // MARK: - Using Custom Views
59 |
60 | /**
61 | This delegate method will be called when the tableView is in need of a view to show when it is loading data initially.
62 |
63 | - parameter tvc: The tableView calling the method.
64 |
65 | - returns: An optional view to show.
66 | */
67 | @objc optional func statefulTableViewViewForInitialLoad(_ tvc: StatefulTableView) -> UIView?
68 |
69 | /**
70 | This delegate method will be called when the tableView is in need of a view to show when it's done loading initially and no data/an error was found.
71 |
72 | - parameter tvc: The tableView calling the method.
73 | - parameter forInitialLoadError: The optional error found.
74 |
75 | - returns: An optional view to show.
76 | */
77 | @objc optional func statefulTableViewInitialErrorView(_ tvc: StatefulTableView, forInitialLoadError: NSError?) -> UIView?
78 |
79 | /**
80 | This delegate method will be called when the tableView failed to load more data.
81 |
82 | - parameter tvc: The tableView calling the method.
83 | - parameter forLoadMoreError: The optional error found.
84 |
85 | - returns: An optional view to show.
86 | */
87 | @objc optional func statefulTableViewLoadMoreErrorView(_ tvc: StatefulTableView, forLoadMoreError: NSError?) -> UIView?
88 | }
89 |
--------------------------------------------------------------------------------
/Sources/StatefulTableView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StatefulTableView.swift
3 | // Demo
4 | //
5 | // Created by Tim on 12/05/2016.
6 | // Copyright © 2016 timominous. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | /**
12 | Drop-in replacement for `UITableView` that supports pull-to-refresh, load-more, initial load, and empty states.
13 | */
14 | public final class StatefulTableView: UIView {
15 | internal enum State {
16 | case idle
17 | case initialLoading
18 | case initialLoadingTableView
19 | case emptyOrInitialLoadError
20 | case loadingFromPullToRefresh
21 | case loadingMore
22 |
23 | var isLoading: Bool {
24 | switch self {
25 | case .initialLoading: fallthrough
26 | case .initialLoadingTableView: fallthrough
27 | case .loadingFromPullToRefresh: fallthrough
28 | case .loadingMore:
29 | return true
30 | default: return false
31 | }
32 | }
33 |
34 | var isInitialLoading: Bool {
35 | switch self {
36 | case .initialLoading: fallthrough
37 | case .initialLoadingTableView:
38 | return true
39 | default: return false
40 | }
41 | }
42 | }
43 |
44 | internal enum ViewMode {
45 | case table
46 | case `static`
47 | }
48 |
49 | /**
50 | Returns an object initialized from data in a given unarchiver.
51 |
52 | - Parameter aDecoder: An unarchiver object.
53 |
54 | - Returns: An initialized StatefulTableView object.
55 | */
56 | required public init?(coder aDecoder: NSCoder) {
57 | super.init(coder: aDecoder)
58 | commonInit()
59 | }
60 |
61 | /**
62 | Initializes and returns a newly allocatied view object with the specified frame rectangle.
63 |
64 | - Parameter frame: The frame rectangle for the view, measured in points. The origin of the frame is relative to the superview in which you plan to add it. this method uses the frame rectangle to set the center and bounds properties accordingly.
65 |
66 | - Returns: An initialized StatefulTableView object.
67 | */
68 | public override init(frame: CGRect) {
69 | super.init(frame: frame)
70 | commonInit()
71 | }
72 |
73 | func commonInit() {
74 | addSubview(tableView)
75 | addSubview(dynamicContentView)
76 |
77 | refreshControl.addTarget(self,
78 | action: #selector(refreshControlValueChanged), for: .valueChanged)
79 | tableView.addSubview(refreshControl)
80 | }
81 |
82 | /**
83 | Lays out subviews.
84 | */
85 | override public func layoutSubviews() {
86 | super.layoutSubviews()
87 | tableView.frame = bounds
88 | dynamicContentView.frame = bounds
89 | }
90 |
91 | internal lazy var tableView = UITableView()
92 |
93 | /**
94 | An accessor to the contained `UITableView`.
95 | */
96 | public var innerTable: UITableView {
97 | return tableView
98 | }
99 |
100 | internal lazy var dynamicContentView: UIView = { [unowned self] in
101 | let view = UIView(frame: self.bounds)
102 | view.backgroundColor = UIColor.white
103 | view.isHidden = true
104 | return view
105 | }()
106 |
107 | internal lazy var refreshControl = UIRefreshControl()
108 |
109 | // MARK: - Properties
110 |
111 | /**
112 | Enables the user to pull down on the tableView to initiate a refresh
113 | */
114 | public var canPullToRefresh = false
115 |
116 | /**
117 | Enables the user to control whether to trigger loading of more objects or not
118 | */
119 | public var canLoadMore = false
120 |
121 | /**
122 | Distance from the bottom of the tableView's vertical content offset where load more will be triggered
123 | */
124 | public var loadMoreTriggerThreshold: CGFloat = 64
125 |
126 | internal var loadMoreViewIsErrorView = false
127 | internal var lastLoadMoreError: NSError?
128 | internal var watchForLoadMore = false
129 |
130 | internal var state: State = .idle
131 |
132 | internal var viewMode: ViewMode = .table {
133 | didSet {
134 | let hidden = viewMode == .table
135 |
136 | guard dynamicContentView.isHidden != hidden else { return }
137 | dynamicContentView.isHidden = hidden
138 | }
139 | }
140 |
141 | // MARK: - Stateful Delegate
142 |
143 | /**
144 | The object that acts as the stateful delegate of the table view.
145 |
146 | - Discussion: The stateful delegate must adopt the `StatefulTableDelegate` protocol. The stateful delegate is not retained.
147 | */
148 | weak public var statefulDelegate: StatefulTableDelegate?
149 |
150 | }
151 |
--------------------------------------------------------------------------------
/Demo/Demo/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // Demo
4 | //
5 | // Created by Tim on 12/05/2016.
6 | // Copyright © 2016 timominous. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import StatefulTableView
11 |
12 | class ViewController: UIViewController {
13 |
14 | @IBOutlet weak var statefulTableView: StatefulTableView!
15 |
16 | var items = 0
17 |
18 | override func viewDidLoad() {
19 | super.viewDidLoad()
20 | statefulTableView.canPullToRefresh = true
21 | statefulTableView.canLoadMore = true
22 |
23 | statefulTableView.statefulDelegate = self
24 | statefulTableView.dataSource = self
25 | statefulTableView.delegate = self
26 | statefulTableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "identifier")
27 | }
28 |
29 | override func viewDidAppear(animated: Bool) {
30 | super.viewDidAppear(animated)
31 | statefulTableView.triggerInitialLoad()
32 | }
33 |
34 | override func didReceiveMemoryWarning() {
35 | super.didReceiveMemoryWarning()
36 | }
37 |
38 | }
39 |
40 | extension ViewController: StatefulTableDelegate {
41 | func statefulTableViewWillBeginLoadingFromRefresh(tvc: StatefulTableView, handler: InitialLoadCompletionHandler) {
42 | items = Int(arc4random_uniform(15))
43 | let empty = items == 0
44 |
45 | let time = dispatch_time(DISPATCH_TIME_NOW, Int64(3 * NSEC_PER_SEC))
46 | dispatch_after(time, dispatch_get_main_queue()) {
47 | let error = NSError(domain: "test", code: 1, userInfo: [NSLocalizedDescriptionKey: "Unknown error"])
48 | tvc.reloadData()
49 | handler(tableIsEmpty: empty, errorOrNil: error)
50 | }
51 | }
52 |
53 | func statefulTableViewWillBeginInitialLoad(tvc: StatefulTableView, handler: InitialLoadCompletionHandler) {
54 | items = Int(arc4random_uniform(15))
55 | let empty = items == 0
56 |
57 | let time = dispatch_time(DISPATCH_TIME_NOW, Int64(3 * NSEC_PER_SEC))
58 | dispatch_after(time, dispatch_get_main_queue()) {
59 | let error = NSError(domain: "test", code: 1, userInfo: [NSLocalizedDescriptionKey: "Unknown error"])
60 | tvc.reloadData()
61 | handler(tableIsEmpty: empty, errorOrNil: error)
62 | }
63 | }
64 |
65 | func statefulTableViewWillBeginLoadingMore(tvc: StatefulTableView, handler: LoadMoreCompletionHandler) {
66 | items += Int(arc4random_uniform(20))
67 | let loadMore = items < 50
68 |
69 | let time = dispatch_time(DISPATCH_TIME_NOW, Int64(3 * NSEC_PER_SEC))
70 | dispatch_after(time, dispatch_get_main_queue()) {
71 | let error = NSError(domain: "test", code: 1, userInfo: [NSLocalizedDescriptionKey: "Unknown error"])
72 | tvc.reloadData()
73 | handler(canLoadMore: loadMore, errorOrNil: error, showErrorView: !loadMore)
74 | }
75 | }
76 |
77 | // Uncomment to use a custom initial loading view
78 | // func statefulTableViewViewForInitialLoad(tvc: StatefulTableView) -> UIView? {
79 | // let view = UIView(frame: CGRect(origin: .zero, size: CGSize(width: 100, height: 100)))
80 | // view.backgroundColor = .blueColor()
81 | // return view
82 | // }
83 |
84 | // Uncomment to use a custom initial loading error view
85 | // func statefulTableViewInitialErrorView(tvc: StatefulTableView, forInitialLoadError: NSError?) -> UIView? {
86 | // let view = UIView(frame: CGRect(origin: .zero, size: CGSize(width: 100, height: 100)))
87 | // view.backgroundColor = .redColor()
88 | // return view
89 | // }
90 |
91 | // Uncommen to use a custom load more error view
92 | // func statefulTableViewLoadMoreErrorView(tvc: StatefulTableView, forLoadMoreError: NSError?) -> UIView? {
93 | // let view = UIView(frame: CGRect(origin: .zero, size: CGSize(width: 100, height: 100)))
94 | // view.backgroundColor = .greenColor()
95 | // return view
96 | // }
97 | }
98 |
99 | extension ViewController: UITableViewDataSource {
100 | func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
101 | return items
102 | }
103 |
104 | func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
105 | return tableView.dequeueReusableCellWithIdentifier("identifier", forIndexPath: indexPath)
106 | }
107 | }
108 |
109 | extension ViewController: UITableViewDelegate {
110 | func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
111 | cell.textLabel?.text = "Cell \(indexPath.row)"
112 | }
113 |
114 | func scrollViewDidScroll(scrollView: UIScrollView) {
115 | statefulTableView.scrollViewDidScroll(scrollView)
116 | }
117 | }
118 |
119 |
--------------------------------------------------------------------------------
/Sources/StatefulTableView+Views.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StatefulTableView+Views.swift
3 | // Pods
4 | //
5 | // Created by Tim on 23/06/2016.
6 | //
7 | //
8 |
9 | import UIKit
10 |
11 | extension StatefulTableView {
12 | // MARK: - Views
13 |
14 | internal func resetdynamicContentView(withChildView childView: UIView?) {
15 | dynamicContentView.subviews.forEach { $0.removeFromSuperview() }
16 |
17 | guard let childView = childView else { return }
18 |
19 | dynamicContentView.addSubview(childView)
20 |
21 | childView.translatesAutoresizingMaskIntoConstraints = false
22 |
23 | pinView(childView, toContainer: dynamicContentView)
24 | }
25 |
26 | internal var viewForInitialLoad: UIView? {
27 | if let delegateMethod = statefulDelegate?.statefulTableViewViewForInitialLoad {
28 | return delegateMethod(self)
29 | }
30 |
31 | let activityIndicatorView = UIActivityIndicatorView(activityIndicatorStyle: .gray)
32 | activityIndicatorView.startAnimating()
33 |
34 | return activityIndicatorView
35 | }
36 |
37 | internal func viewForEmptyInitialLoad(withError error: NSError?) -> UIView? {
38 | if let delegateMethod = statefulDelegate?.statefulTableViewInitialErrorView {
39 | return delegateMethod(self, error)
40 | }
41 |
42 | let container = UIView(frame: .zero)
43 |
44 | var centeredSize: CGSize = .zero
45 |
46 | let centered = UIView(frame: .zero)
47 | centered.translatesAutoresizingMaskIntoConstraints = false
48 |
49 | let label = UILabel()
50 | label.translatesAutoresizingMaskIntoConstraints = false
51 | label.textAlignment = .center
52 | label.text = error?.localizedDescription ?? "No records found"
53 | label.sizeToFit()
54 |
55 | label.setWidthConstraintToCurrent()
56 | label.setHeightConstraintToCurrent()
57 |
58 | centered.addSubview(label)
59 |
60 | apply([.top, .centerX], ofView: label, toView: centered)
61 |
62 | centeredSize.width = label.bounds.width
63 | centeredSize.height = label.bounds.height
64 |
65 | if let _ = error {
66 | let button = UIButton(type: .system)
67 | button.translatesAutoresizingMaskIntoConstraints = false
68 | button.setTitle("Try Again", for: UIControlState())
69 | button.addTarget(self, action: #selector(triggerInitialLoad(_:)), for: .touchUpInside)
70 | button.sizeToFit()
71 |
72 | button.setWidthConstraintToCurrent()
73 | button.setHeightConstraintToCurrent()
74 |
75 | centeredSize.width = max(centeredSize.width, button.bounds.width)
76 | centeredSize.height = label.bounds.height + button.bounds.height + 5
77 |
78 | centered.addSubview(button)
79 |
80 | apply([.bottom, .centerX], ofView: button, toView: centered)
81 | }
82 |
83 | centered.setWidthConstraint(centeredSize.width)
84 | centered.setHeightConstraint(centeredSize.height)
85 |
86 | container.addSubview(centered)
87 |
88 | centerView(centered, inContainer: container)
89 |
90 | return container
91 | }
92 | }
93 |
94 | internal extension StatefulTableView {
95 | // MARK: - Helpers
96 |
97 | internal func pinView(_ view: UIView, toContainer container: UIView) {
98 | let attributes: [NSLayoutAttribute] = [.top, .bottom, .leading, .trailing]
99 | apply(attributes, ofView: view, toView: container)
100 | }
101 |
102 | internal func centerView(_ view: UIView, inContainer container: UIView) {
103 | let attributes: [NSLayoutAttribute] = [.centerX, .centerY]
104 | apply(attributes, ofView: view, toView: container)
105 | }
106 |
107 | internal func centerViewHorizontally(_ view: UIView, inContainer container: UIView) {
108 | apply([.centerX], ofView: view, toView: container)
109 | }
110 |
111 | internal func centerViewVertically(_ view: UIView, inContainer container: UIView) {
112 | apply([.centerY], ofView: view, toView: container)
113 | }
114 |
115 | internal func apply(_ attributes: [NSLayoutAttribute], ofView childView: UIView, toView containerView: UIView) {
116 | let constraints = attributes.map {
117 | return NSLayoutConstraint(item: childView, attribute: $0, relatedBy: .equal,
118 | toItem: containerView, attribute: $0, multiplier: 1, constant: 0)
119 | }
120 |
121 | containerView.addConstraints(constraints)
122 | }
123 | }
124 |
125 | internal extension UIView {
126 | internal func setWidthConstraintToCurrent() {
127 | setWidthConstraint(bounds.width)
128 | }
129 |
130 | internal func setHeightConstraintToCurrent() {
131 | setHeightConstraint(bounds.height)
132 | }
133 |
134 | internal func setWidthConstraint(_ width: CGFloat) {
135 | addConstraint(NSLayoutConstraint(item: self, attribute: .width, relatedBy: .equal, toItem: nil,
136 | attribute: .notAnAttribute, multiplier: 1, constant: width))
137 | }
138 |
139 | internal func setHeightConstraint(_ height: CGFloat) {
140 | addConstraint(NSLayoutConstraint(item: self, attribute: .height, relatedBy: .equal, toItem: nil,
141 | attribute: .notAnAttribute, multiplier: 1, constant: height))
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/Demo/Demo.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 2CBFD961A70C9B1577F0286B /* Pods_Demo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E18E2637AAF070943DDCB0A9 /* Pods_Demo.framework */; };
11 | 8C78531E50789A0105B4072D /* Pods_DemoTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AA9251D21C9D91E90A5327F2 /* Pods_DemoTests.framework */; };
12 | 906D171C1CE4BF99001B30EF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 906D171B1CE4BF99001B30EF /* AppDelegate.swift */; };
13 | 906D171E1CE4BF99001B30EF /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 906D171D1CE4BF99001B30EF /* ViewController.swift */; };
14 | 906D17211CE4BF99001B30EF /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 906D171F1CE4BF99001B30EF /* Main.storyboard */; };
15 | 906D17231CE4BF99001B30EF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 906D17221CE4BF99001B30EF /* Assets.xcassets */; };
16 | 906D17261CE4BF99001B30EF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 906D17241CE4BF99001B30EF /* LaunchScreen.storyboard */; };
17 | 906D17311CE4BF99001B30EF /* DemoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 906D17301CE4BF99001B30EF /* DemoTests.swift */; };
18 | 906D173C1CE4BF99001B30EF /* DemoUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 906D173B1CE4BF99001B30EF /* DemoUITests.swift */; };
19 | D884A08032E51E35936B1A02 /* Pods_DemoUITests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 49C819E087348795B5CBB37E /* Pods_DemoUITests.framework */; };
20 | /* End PBXBuildFile section */
21 |
22 | /* Begin PBXContainerItemProxy section */
23 | 906D172D1CE4BF99001B30EF /* PBXContainerItemProxy */ = {
24 | isa = PBXContainerItemProxy;
25 | containerPortal = 906D17101CE4BF99001B30EF /* Project object */;
26 | proxyType = 1;
27 | remoteGlobalIDString = 906D17171CE4BF99001B30EF;
28 | remoteInfo = Demo;
29 | };
30 | 906D17381CE4BF99001B30EF /* PBXContainerItemProxy */ = {
31 | isa = PBXContainerItemProxy;
32 | containerPortal = 906D17101CE4BF99001B30EF /* Project object */;
33 | proxyType = 1;
34 | remoteGlobalIDString = 906D17171CE4BF99001B30EF;
35 | remoteInfo = Demo;
36 | };
37 | /* End PBXContainerItemProxy section */
38 |
39 | /* Begin PBXFileReference section */
40 | 0A563EA3A78DC7437869051D /* Pods-DemoTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DemoTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-DemoTests/Pods-DemoTests.debug.xcconfig"; sourceTree = ""; };
41 | 1EAF6CDFC38F75E74127F13C /* Pods-Demo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Demo.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Demo/Pods-Demo.debug.xcconfig"; sourceTree = ""; };
42 | 210E8BF35549B671ABAA71B0 /* Pods-DemoTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DemoTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-DemoTests/Pods-DemoTests.release.xcconfig"; sourceTree = ""; };
43 | 49C819E087348795B5CBB37E /* Pods_DemoUITests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_DemoUITests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
44 | 6CC9EC0297CBB38896C7B7E1 /* Pods-Demo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Demo.release.xcconfig"; path = "Pods/Target Support Files/Pods-Demo/Pods-Demo.release.xcconfig"; sourceTree = ""; };
45 | 906D17181CE4BF99001B30EF /* Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Demo.app; sourceTree = BUILT_PRODUCTS_DIR; };
46 | 906D171B1CE4BF99001B30EF /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
47 | 906D171D1CE4BF99001B30EF /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; };
48 | 906D17201CE4BF99001B30EF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
49 | 906D17221CE4BF99001B30EF /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
50 | 906D17251CE4BF99001B30EF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
51 | 906D17271CE4BF99001B30EF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
52 | 906D172C1CE4BF99001B30EF /* DemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
53 | 906D17301CE4BF99001B30EF /* DemoTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoTests.swift; sourceTree = ""; };
54 | 906D17321CE4BF99001B30EF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
55 | 906D17371CE4BF99001B30EF /* DemoUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DemoUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
56 | 906D173B1CE4BF99001B30EF /* DemoUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoUITests.swift; sourceTree = ""; };
57 | 906D173D1CE4BF99001B30EF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
58 | AA9251D21C9D91E90A5327F2 /* Pods_DemoTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_DemoTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
59 | CC164385BCFA6CC7DDC9D1F9 /* Pods-DemoUITests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DemoUITests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-DemoUITests/Pods-DemoUITests.debug.xcconfig"; sourceTree = ""; };
60 | E179559F3B224B0B84374FAE /* Pods-DemoUITests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DemoUITests.release.xcconfig"; path = "Pods/Target Support Files/Pods-DemoUITests/Pods-DemoUITests.release.xcconfig"; sourceTree = ""; };
61 | E18E2637AAF070943DDCB0A9 /* Pods_Demo.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Demo.framework; sourceTree = BUILT_PRODUCTS_DIR; };
62 | /* End PBXFileReference section */
63 |
64 | /* Begin PBXFrameworksBuildPhase section */
65 | 906D17151CE4BF99001B30EF /* Frameworks */ = {
66 | isa = PBXFrameworksBuildPhase;
67 | buildActionMask = 2147483647;
68 | files = (
69 | 2CBFD961A70C9B1577F0286B /* Pods_Demo.framework in Frameworks */,
70 | );
71 | runOnlyForDeploymentPostprocessing = 0;
72 | };
73 | 906D17291CE4BF99001B30EF /* Frameworks */ = {
74 | isa = PBXFrameworksBuildPhase;
75 | buildActionMask = 2147483647;
76 | files = (
77 | 8C78531E50789A0105B4072D /* Pods_DemoTests.framework in Frameworks */,
78 | );
79 | runOnlyForDeploymentPostprocessing = 0;
80 | };
81 | 906D17341CE4BF99001B30EF /* Frameworks */ = {
82 | isa = PBXFrameworksBuildPhase;
83 | buildActionMask = 2147483647;
84 | files = (
85 | D884A08032E51E35936B1A02 /* Pods_DemoUITests.framework in Frameworks */,
86 | );
87 | runOnlyForDeploymentPostprocessing = 0;
88 | };
89 | /* End PBXFrameworksBuildPhase section */
90 |
91 | /* Begin PBXGroup section */
92 | 906D170F1CE4BF99001B30EF = {
93 | isa = PBXGroup;
94 | children = (
95 | 906D171A1CE4BF99001B30EF /* Demo */,
96 | 906D172F1CE4BF99001B30EF /* DemoTests */,
97 | 906D173A1CE4BF99001B30EF /* DemoUITests */,
98 | 906D17191CE4BF99001B30EF /* Products */,
99 | 99D9B40D95A3F5108647351E /* Pods */,
100 | C545F881EFF8BB3EAD6954EA /* Frameworks */,
101 | );
102 | sourceTree = "";
103 | };
104 | 906D17191CE4BF99001B30EF /* Products */ = {
105 | isa = PBXGroup;
106 | children = (
107 | 906D17181CE4BF99001B30EF /* Demo.app */,
108 | 906D172C1CE4BF99001B30EF /* DemoTests.xctest */,
109 | 906D17371CE4BF99001B30EF /* DemoUITests.xctest */,
110 | );
111 | name = Products;
112 | sourceTree = "";
113 | };
114 | 906D171A1CE4BF99001B30EF /* Demo */ = {
115 | isa = PBXGroup;
116 | children = (
117 | 906D171B1CE4BF99001B30EF /* AppDelegate.swift */,
118 | 906D171D1CE4BF99001B30EF /* ViewController.swift */,
119 | 906D171F1CE4BF99001B30EF /* Main.storyboard */,
120 | 906D17221CE4BF99001B30EF /* Assets.xcassets */,
121 | 906D17241CE4BF99001B30EF /* LaunchScreen.storyboard */,
122 | 906D17271CE4BF99001B30EF /* Info.plist */,
123 | );
124 | path = Demo;
125 | sourceTree = "";
126 | };
127 | 906D172F1CE4BF99001B30EF /* DemoTests */ = {
128 | isa = PBXGroup;
129 | children = (
130 | 906D17301CE4BF99001B30EF /* DemoTests.swift */,
131 | 906D17321CE4BF99001B30EF /* Info.plist */,
132 | );
133 | path = DemoTests;
134 | sourceTree = "";
135 | };
136 | 906D173A1CE4BF99001B30EF /* DemoUITests */ = {
137 | isa = PBXGroup;
138 | children = (
139 | 906D173B1CE4BF99001B30EF /* DemoUITests.swift */,
140 | 906D173D1CE4BF99001B30EF /* Info.plist */,
141 | );
142 | path = DemoUITests;
143 | sourceTree = "";
144 | };
145 | 99D9B40D95A3F5108647351E /* Pods */ = {
146 | isa = PBXGroup;
147 | children = (
148 | 1EAF6CDFC38F75E74127F13C /* Pods-Demo.debug.xcconfig */,
149 | 6CC9EC0297CBB38896C7B7E1 /* Pods-Demo.release.xcconfig */,
150 | 0A563EA3A78DC7437869051D /* Pods-DemoTests.debug.xcconfig */,
151 | 210E8BF35549B671ABAA71B0 /* Pods-DemoTests.release.xcconfig */,
152 | CC164385BCFA6CC7DDC9D1F9 /* Pods-DemoUITests.debug.xcconfig */,
153 | E179559F3B224B0B84374FAE /* Pods-DemoUITests.release.xcconfig */,
154 | );
155 | name = Pods;
156 | sourceTree = "";
157 | };
158 | C545F881EFF8BB3EAD6954EA /* Frameworks */ = {
159 | isa = PBXGroup;
160 | children = (
161 | E18E2637AAF070943DDCB0A9 /* Pods_Demo.framework */,
162 | AA9251D21C9D91E90A5327F2 /* Pods_DemoTests.framework */,
163 | 49C819E087348795B5CBB37E /* Pods_DemoUITests.framework */,
164 | );
165 | name = Frameworks;
166 | sourceTree = "";
167 | };
168 | /* End PBXGroup section */
169 |
170 | /* Begin PBXNativeTarget section */
171 | 906D17171CE4BF99001B30EF /* Demo */ = {
172 | isa = PBXNativeTarget;
173 | buildConfigurationList = 906D17401CE4BF99001B30EF /* Build configuration list for PBXNativeTarget "Demo" */;
174 | buildPhases = (
175 | FEB65081904F90F064C377A1 /* 📦 Check Pods Manifest.lock */,
176 | 906D17141CE4BF99001B30EF /* Sources */,
177 | 906D17151CE4BF99001B30EF /* Frameworks */,
178 | 906D17161CE4BF99001B30EF /* Resources */,
179 | 86804F2319AE8F80CD8FFAF9 /* 📦 Embed Pods Frameworks */,
180 | 5F6248DCDFF53F5D059D2D5E /* 📦 Copy Pods Resources */,
181 | );
182 | buildRules = (
183 | );
184 | dependencies = (
185 | );
186 | name = Demo;
187 | productName = Demo;
188 | productReference = 906D17181CE4BF99001B30EF /* Demo.app */;
189 | productType = "com.apple.product-type.application";
190 | };
191 | 906D172B1CE4BF99001B30EF /* DemoTests */ = {
192 | isa = PBXNativeTarget;
193 | buildConfigurationList = 906D17431CE4BF99001B30EF /* Build configuration list for PBXNativeTarget "DemoTests" */;
194 | buildPhases = (
195 | 22ADE59AA9009ED6D5DB509B /* 📦 Check Pods Manifest.lock */,
196 | 906D17281CE4BF99001B30EF /* Sources */,
197 | 906D17291CE4BF99001B30EF /* Frameworks */,
198 | 906D172A1CE4BF99001B30EF /* Resources */,
199 | 389F862436469705440E9EC9 /* 📦 Embed Pods Frameworks */,
200 | 6A4D202622A93480BA8D8468 /* 📦 Copy Pods Resources */,
201 | );
202 | buildRules = (
203 | );
204 | dependencies = (
205 | 906D172E1CE4BF99001B30EF /* PBXTargetDependency */,
206 | );
207 | name = DemoTests;
208 | productName = DemoTests;
209 | productReference = 906D172C1CE4BF99001B30EF /* DemoTests.xctest */;
210 | productType = "com.apple.product-type.bundle.unit-test";
211 | };
212 | 906D17361CE4BF99001B30EF /* DemoUITests */ = {
213 | isa = PBXNativeTarget;
214 | buildConfigurationList = 906D17461CE4BF99001B30EF /* Build configuration list for PBXNativeTarget "DemoUITests" */;
215 | buildPhases = (
216 | D6D4202278DCFD55DD82F020 /* 📦 Check Pods Manifest.lock */,
217 | 906D17331CE4BF99001B30EF /* Sources */,
218 | 906D17341CE4BF99001B30EF /* Frameworks */,
219 | 906D17351CE4BF99001B30EF /* Resources */,
220 | 6A9D1057246E8E96D8E1F3BD /* 📦 Copy Pods Resources */,
221 | );
222 | buildRules = (
223 | );
224 | dependencies = (
225 | 906D17391CE4BF99001B30EF /* PBXTargetDependency */,
226 | );
227 | name = DemoUITests;
228 | productName = DemoUITests;
229 | productReference = 906D17371CE4BF99001B30EF /* DemoUITests.xctest */;
230 | productType = "com.apple.product-type.bundle.ui-testing";
231 | };
232 | /* End PBXNativeTarget section */
233 |
234 | /* Begin PBXProject section */
235 | 906D17101CE4BF99001B30EF /* Project object */ = {
236 | isa = PBXProject;
237 | attributes = {
238 | LastSwiftUpdateCheck = 0730;
239 | LastUpgradeCheck = 0730;
240 | ORGANIZATIONNAME = timominous;
241 | TargetAttributes = {
242 | 906D17171CE4BF99001B30EF = {
243 | CreatedOnToolsVersion = 7.3;
244 | };
245 | 906D172B1CE4BF99001B30EF = {
246 | CreatedOnToolsVersion = 7.3;
247 | TestTargetID = 906D17171CE4BF99001B30EF;
248 | };
249 | 906D17361CE4BF99001B30EF = {
250 | CreatedOnToolsVersion = 7.3;
251 | TestTargetID = 906D17171CE4BF99001B30EF;
252 | };
253 | };
254 | };
255 | buildConfigurationList = 906D17131CE4BF99001B30EF /* Build configuration list for PBXProject "Demo" */;
256 | compatibilityVersion = "Xcode 3.2";
257 | developmentRegion = English;
258 | hasScannedForEncodings = 0;
259 | knownRegions = (
260 | en,
261 | Base,
262 | );
263 | mainGroup = 906D170F1CE4BF99001B30EF;
264 | productRefGroup = 906D17191CE4BF99001B30EF /* Products */;
265 | projectDirPath = "";
266 | projectRoot = "";
267 | targets = (
268 | 906D17171CE4BF99001B30EF /* Demo */,
269 | 906D172B1CE4BF99001B30EF /* DemoTests */,
270 | 906D17361CE4BF99001B30EF /* DemoUITests */,
271 | );
272 | };
273 | /* End PBXProject section */
274 |
275 | /* Begin PBXResourcesBuildPhase section */
276 | 906D17161CE4BF99001B30EF /* Resources */ = {
277 | isa = PBXResourcesBuildPhase;
278 | buildActionMask = 2147483647;
279 | files = (
280 | 906D17261CE4BF99001B30EF /* LaunchScreen.storyboard in Resources */,
281 | 906D17231CE4BF99001B30EF /* Assets.xcassets in Resources */,
282 | 906D17211CE4BF99001B30EF /* Main.storyboard in Resources */,
283 | );
284 | runOnlyForDeploymentPostprocessing = 0;
285 | };
286 | 906D172A1CE4BF99001B30EF /* Resources */ = {
287 | isa = PBXResourcesBuildPhase;
288 | buildActionMask = 2147483647;
289 | files = (
290 | );
291 | runOnlyForDeploymentPostprocessing = 0;
292 | };
293 | 906D17351CE4BF99001B30EF /* Resources */ = {
294 | isa = PBXResourcesBuildPhase;
295 | buildActionMask = 2147483647;
296 | files = (
297 | );
298 | runOnlyForDeploymentPostprocessing = 0;
299 | };
300 | /* End PBXResourcesBuildPhase section */
301 |
302 | /* Begin PBXShellScriptBuildPhase section */
303 | 22ADE59AA9009ED6D5DB509B /* 📦 Check Pods Manifest.lock */ = {
304 | isa = PBXShellScriptBuildPhase;
305 | buildActionMask = 2147483647;
306 | files = (
307 | );
308 | inputPaths = (
309 | );
310 | name = "📦 Check Pods Manifest.lock";
311 | outputPaths = (
312 | );
313 | runOnlyForDeploymentPostprocessing = 0;
314 | shellPath = /bin/sh;
315 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n";
316 | showEnvVarsInLog = 0;
317 | };
318 | 389F862436469705440E9EC9 /* 📦 Embed Pods Frameworks */ = {
319 | isa = PBXShellScriptBuildPhase;
320 | buildActionMask = 2147483647;
321 | files = (
322 | );
323 | inputPaths = (
324 | );
325 | name = "📦 Embed Pods Frameworks";
326 | outputPaths = (
327 | );
328 | runOnlyForDeploymentPostprocessing = 0;
329 | shellPath = /bin/sh;
330 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-DemoTests/Pods-DemoTests-frameworks.sh\"\n";
331 | showEnvVarsInLog = 0;
332 | };
333 | 5F6248DCDFF53F5D059D2D5E /* 📦 Copy Pods Resources */ = {
334 | isa = PBXShellScriptBuildPhase;
335 | buildActionMask = 2147483647;
336 | files = (
337 | );
338 | inputPaths = (
339 | );
340 | name = "📦 Copy Pods Resources";
341 | outputPaths = (
342 | );
343 | runOnlyForDeploymentPostprocessing = 0;
344 | shellPath = /bin/sh;
345 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Demo/Pods-Demo-resources.sh\"\n";
346 | showEnvVarsInLog = 0;
347 | };
348 | 6A4D202622A93480BA8D8468 /* 📦 Copy Pods Resources */ = {
349 | isa = PBXShellScriptBuildPhase;
350 | buildActionMask = 2147483647;
351 | files = (
352 | );
353 | inputPaths = (
354 | );
355 | name = "📦 Copy Pods Resources";
356 | outputPaths = (
357 | );
358 | runOnlyForDeploymentPostprocessing = 0;
359 | shellPath = /bin/sh;
360 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-DemoTests/Pods-DemoTests-resources.sh\"\n";
361 | showEnvVarsInLog = 0;
362 | };
363 | 6A9D1057246E8E96D8E1F3BD /* 📦 Copy Pods Resources */ = {
364 | isa = PBXShellScriptBuildPhase;
365 | buildActionMask = 2147483647;
366 | files = (
367 | );
368 | inputPaths = (
369 | );
370 | name = "📦 Copy Pods Resources";
371 | outputPaths = (
372 | );
373 | runOnlyForDeploymentPostprocessing = 0;
374 | shellPath = /bin/sh;
375 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-DemoUITests/Pods-DemoUITests-resources.sh\"\n";
376 | showEnvVarsInLog = 0;
377 | };
378 | 86804F2319AE8F80CD8FFAF9 /* 📦 Embed Pods Frameworks */ = {
379 | isa = PBXShellScriptBuildPhase;
380 | buildActionMask = 2147483647;
381 | files = (
382 | );
383 | inputPaths = (
384 | );
385 | name = "📦 Embed Pods Frameworks";
386 | outputPaths = (
387 | );
388 | runOnlyForDeploymentPostprocessing = 0;
389 | shellPath = /bin/sh;
390 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Demo/Pods-Demo-frameworks.sh\"\n";
391 | showEnvVarsInLog = 0;
392 | };
393 | D6D4202278DCFD55DD82F020 /* 📦 Check Pods Manifest.lock */ = {
394 | isa = PBXShellScriptBuildPhase;
395 | buildActionMask = 2147483647;
396 | files = (
397 | );
398 | inputPaths = (
399 | );
400 | name = "📦 Check Pods Manifest.lock";
401 | outputPaths = (
402 | );
403 | runOnlyForDeploymentPostprocessing = 0;
404 | shellPath = /bin/sh;
405 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n";
406 | showEnvVarsInLog = 0;
407 | };
408 | FEB65081904F90F064C377A1 /* 📦 Check Pods Manifest.lock */ = {
409 | isa = PBXShellScriptBuildPhase;
410 | buildActionMask = 2147483647;
411 | files = (
412 | );
413 | inputPaths = (
414 | );
415 | name = "📦 Check Pods Manifest.lock";
416 | outputPaths = (
417 | );
418 | runOnlyForDeploymentPostprocessing = 0;
419 | shellPath = /bin/sh;
420 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n";
421 | showEnvVarsInLog = 0;
422 | };
423 | /* End PBXShellScriptBuildPhase section */
424 |
425 | /* Begin PBXSourcesBuildPhase section */
426 | 906D17141CE4BF99001B30EF /* Sources */ = {
427 | isa = PBXSourcesBuildPhase;
428 | buildActionMask = 2147483647;
429 | files = (
430 | 906D171E1CE4BF99001B30EF /* ViewController.swift in Sources */,
431 | 906D171C1CE4BF99001B30EF /* AppDelegate.swift in Sources */,
432 | );
433 | runOnlyForDeploymentPostprocessing = 0;
434 | };
435 | 906D17281CE4BF99001B30EF /* Sources */ = {
436 | isa = PBXSourcesBuildPhase;
437 | buildActionMask = 2147483647;
438 | files = (
439 | 906D17311CE4BF99001B30EF /* DemoTests.swift in Sources */,
440 | );
441 | runOnlyForDeploymentPostprocessing = 0;
442 | };
443 | 906D17331CE4BF99001B30EF /* Sources */ = {
444 | isa = PBXSourcesBuildPhase;
445 | buildActionMask = 2147483647;
446 | files = (
447 | 906D173C1CE4BF99001B30EF /* DemoUITests.swift in Sources */,
448 | );
449 | runOnlyForDeploymentPostprocessing = 0;
450 | };
451 | /* End PBXSourcesBuildPhase section */
452 |
453 | /* Begin PBXTargetDependency section */
454 | 906D172E1CE4BF99001B30EF /* PBXTargetDependency */ = {
455 | isa = PBXTargetDependency;
456 | target = 906D17171CE4BF99001B30EF /* Demo */;
457 | targetProxy = 906D172D1CE4BF99001B30EF /* PBXContainerItemProxy */;
458 | };
459 | 906D17391CE4BF99001B30EF /* PBXTargetDependency */ = {
460 | isa = PBXTargetDependency;
461 | target = 906D17171CE4BF99001B30EF /* Demo */;
462 | targetProxy = 906D17381CE4BF99001B30EF /* PBXContainerItemProxy */;
463 | };
464 | /* End PBXTargetDependency section */
465 |
466 | /* Begin PBXVariantGroup section */
467 | 906D171F1CE4BF99001B30EF /* Main.storyboard */ = {
468 | isa = PBXVariantGroup;
469 | children = (
470 | 906D17201CE4BF99001B30EF /* Base */,
471 | );
472 | name = Main.storyboard;
473 | sourceTree = "";
474 | };
475 | 906D17241CE4BF99001B30EF /* LaunchScreen.storyboard */ = {
476 | isa = PBXVariantGroup;
477 | children = (
478 | 906D17251CE4BF99001B30EF /* Base */,
479 | );
480 | name = LaunchScreen.storyboard;
481 | sourceTree = "";
482 | };
483 | /* End PBXVariantGroup section */
484 |
485 | /* Begin XCBuildConfiguration section */
486 | 906D173E1CE4BF99001B30EF /* Debug */ = {
487 | isa = XCBuildConfiguration;
488 | buildSettings = {
489 | ALWAYS_SEARCH_USER_PATHS = NO;
490 | CLANG_ANALYZER_NONNULL = YES;
491 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
492 | CLANG_CXX_LIBRARY = "libc++";
493 | CLANG_ENABLE_MODULES = YES;
494 | CLANG_ENABLE_OBJC_ARC = YES;
495 | CLANG_WARN_BOOL_CONVERSION = YES;
496 | CLANG_WARN_CONSTANT_CONVERSION = YES;
497 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
498 | CLANG_WARN_EMPTY_BODY = YES;
499 | CLANG_WARN_ENUM_CONVERSION = YES;
500 | CLANG_WARN_INT_CONVERSION = YES;
501 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
502 | CLANG_WARN_UNREACHABLE_CODE = YES;
503 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
504 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
505 | COPY_PHASE_STRIP = NO;
506 | DEBUG_INFORMATION_FORMAT = dwarf;
507 | ENABLE_STRICT_OBJC_MSGSEND = YES;
508 | ENABLE_TESTABILITY = YES;
509 | GCC_C_LANGUAGE_STANDARD = gnu99;
510 | GCC_DYNAMIC_NO_PIC = NO;
511 | GCC_NO_COMMON_BLOCKS = YES;
512 | GCC_OPTIMIZATION_LEVEL = 0;
513 | GCC_PREPROCESSOR_DEFINITIONS = (
514 | "DEBUG=1",
515 | "$(inherited)",
516 | );
517 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
518 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
519 | GCC_WARN_UNDECLARED_SELECTOR = YES;
520 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
521 | GCC_WARN_UNUSED_FUNCTION = YES;
522 | GCC_WARN_UNUSED_VARIABLE = YES;
523 | IPHONEOS_DEPLOYMENT_TARGET = 9.3;
524 | MTL_ENABLE_DEBUG_INFO = YES;
525 | ONLY_ACTIVE_ARCH = YES;
526 | SDKROOT = iphoneos;
527 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
528 | TARGETED_DEVICE_FAMILY = "1,2";
529 | };
530 | name = Debug;
531 | };
532 | 906D173F1CE4BF99001B30EF /* Release */ = {
533 | isa = XCBuildConfiguration;
534 | buildSettings = {
535 | ALWAYS_SEARCH_USER_PATHS = NO;
536 | CLANG_ANALYZER_NONNULL = YES;
537 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
538 | CLANG_CXX_LIBRARY = "libc++";
539 | CLANG_ENABLE_MODULES = YES;
540 | CLANG_ENABLE_OBJC_ARC = YES;
541 | CLANG_WARN_BOOL_CONVERSION = YES;
542 | CLANG_WARN_CONSTANT_CONVERSION = YES;
543 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
544 | CLANG_WARN_EMPTY_BODY = YES;
545 | CLANG_WARN_ENUM_CONVERSION = YES;
546 | CLANG_WARN_INT_CONVERSION = YES;
547 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
548 | CLANG_WARN_UNREACHABLE_CODE = YES;
549 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
550 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
551 | COPY_PHASE_STRIP = NO;
552 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
553 | ENABLE_NS_ASSERTIONS = NO;
554 | ENABLE_STRICT_OBJC_MSGSEND = YES;
555 | GCC_C_LANGUAGE_STANDARD = gnu99;
556 | GCC_NO_COMMON_BLOCKS = YES;
557 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
558 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
559 | GCC_WARN_UNDECLARED_SELECTOR = YES;
560 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
561 | GCC_WARN_UNUSED_FUNCTION = YES;
562 | GCC_WARN_UNUSED_VARIABLE = YES;
563 | IPHONEOS_DEPLOYMENT_TARGET = 9.3;
564 | MTL_ENABLE_DEBUG_INFO = NO;
565 | SDKROOT = iphoneos;
566 | TARGETED_DEVICE_FAMILY = "1,2";
567 | VALIDATE_PRODUCT = YES;
568 | };
569 | name = Release;
570 | };
571 | 906D17411CE4BF99001B30EF /* Debug */ = {
572 | isa = XCBuildConfiguration;
573 | baseConfigurationReference = 1EAF6CDFC38F75E74127F13C /* Pods-Demo.debug.xcconfig */;
574 | buildSettings = {
575 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
576 | INFOPLIST_FILE = Demo/Info.plist;
577 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
578 | PRODUCT_BUNDLE_IDENTIFIER = com.timomnious.Demo;
579 | PRODUCT_NAME = "$(TARGET_NAME)";
580 | };
581 | name = Debug;
582 | };
583 | 906D17421CE4BF99001B30EF /* Release */ = {
584 | isa = XCBuildConfiguration;
585 | baseConfigurationReference = 6CC9EC0297CBB38896C7B7E1 /* Pods-Demo.release.xcconfig */;
586 | buildSettings = {
587 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
588 | INFOPLIST_FILE = Demo/Info.plist;
589 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
590 | PRODUCT_BUNDLE_IDENTIFIER = com.timomnious.Demo;
591 | PRODUCT_NAME = "$(TARGET_NAME)";
592 | };
593 | name = Release;
594 | };
595 | 906D17441CE4BF99001B30EF /* Debug */ = {
596 | isa = XCBuildConfiguration;
597 | baseConfigurationReference = 0A563EA3A78DC7437869051D /* Pods-DemoTests.debug.xcconfig */;
598 | buildSettings = {
599 | BUNDLE_LOADER = "$(TEST_HOST)";
600 | INFOPLIST_FILE = DemoTests/Info.plist;
601 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
602 | PRODUCT_BUNDLE_IDENTIFIER = com.timomnious.DemoTests;
603 | PRODUCT_NAME = "$(TARGET_NAME)";
604 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Demo.app/Demo";
605 | };
606 | name = Debug;
607 | };
608 | 906D17451CE4BF99001B30EF /* Release */ = {
609 | isa = XCBuildConfiguration;
610 | baseConfigurationReference = 210E8BF35549B671ABAA71B0 /* Pods-DemoTests.release.xcconfig */;
611 | buildSettings = {
612 | BUNDLE_LOADER = "$(TEST_HOST)";
613 | INFOPLIST_FILE = DemoTests/Info.plist;
614 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
615 | PRODUCT_BUNDLE_IDENTIFIER = com.timomnious.DemoTests;
616 | PRODUCT_NAME = "$(TARGET_NAME)";
617 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Demo.app/Demo";
618 | };
619 | name = Release;
620 | };
621 | 906D17471CE4BF99001B30EF /* Debug */ = {
622 | isa = XCBuildConfiguration;
623 | baseConfigurationReference = CC164385BCFA6CC7DDC9D1F9 /* Pods-DemoUITests.debug.xcconfig */;
624 | buildSettings = {
625 | INFOPLIST_FILE = DemoUITests/Info.plist;
626 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
627 | PRODUCT_BUNDLE_IDENTIFIER = com.timomnious.DemoUITests;
628 | PRODUCT_NAME = "$(TARGET_NAME)";
629 | TEST_TARGET_NAME = Demo;
630 | };
631 | name = Debug;
632 | };
633 | 906D17481CE4BF99001B30EF /* Release */ = {
634 | isa = XCBuildConfiguration;
635 | baseConfigurationReference = E179559F3B224B0B84374FAE /* Pods-DemoUITests.release.xcconfig */;
636 | buildSettings = {
637 | INFOPLIST_FILE = DemoUITests/Info.plist;
638 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
639 | PRODUCT_BUNDLE_IDENTIFIER = com.timomnious.DemoUITests;
640 | PRODUCT_NAME = "$(TARGET_NAME)";
641 | TEST_TARGET_NAME = Demo;
642 | };
643 | name = Release;
644 | };
645 | /* End XCBuildConfiguration section */
646 |
647 | /* Begin XCConfigurationList section */
648 | 906D17131CE4BF99001B30EF /* Build configuration list for PBXProject "Demo" */ = {
649 | isa = XCConfigurationList;
650 | buildConfigurations = (
651 | 906D173E1CE4BF99001B30EF /* Debug */,
652 | 906D173F1CE4BF99001B30EF /* Release */,
653 | );
654 | defaultConfigurationIsVisible = 0;
655 | defaultConfigurationName = Release;
656 | };
657 | 906D17401CE4BF99001B30EF /* Build configuration list for PBXNativeTarget "Demo" */ = {
658 | isa = XCConfigurationList;
659 | buildConfigurations = (
660 | 906D17411CE4BF99001B30EF /* Debug */,
661 | 906D17421CE4BF99001B30EF /* Release */,
662 | );
663 | defaultConfigurationIsVisible = 0;
664 | defaultConfigurationName = Release;
665 | };
666 | 906D17431CE4BF99001B30EF /* Build configuration list for PBXNativeTarget "DemoTests" */ = {
667 | isa = XCConfigurationList;
668 | buildConfigurations = (
669 | 906D17441CE4BF99001B30EF /* Debug */,
670 | 906D17451CE4BF99001B30EF /* Release */,
671 | );
672 | defaultConfigurationIsVisible = 0;
673 | defaultConfigurationName = Release;
674 | };
675 | 906D17461CE4BF99001B30EF /* Build configuration list for PBXNativeTarget "DemoUITests" */ = {
676 | isa = XCConfigurationList;
677 | buildConfigurations = (
678 | 906D17471CE4BF99001B30EF /* Debug */,
679 | 906D17481CE4BF99001B30EF /* Release */,
680 | );
681 | defaultConfigurationIsVisible = 0;
682 | defaultConfigurationName = Release;
683 | };
684 | /* End XCConfigurationList section */
685 | };
686 | rootObject = 906D17101CE4BF99001B30EF /* Project object */;
687 | }
688 |
--------------------------------------------------------------------------------
/Sources/StatefulTableView+UITableView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StatefulTableView+UITableView.swift
3 | // Pods
4 | //
5 | // Created by Tim on 23/06/2016.
6 | //
7 | //
8 |
9 | import UIKit
10 |
11 | extension StatefulTableView {
12 | // MARK: - Configuring a Table View
13 |
14 | /**
15 | Returns the number of rows (table cells) in a specified section.
16 |
17 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instm/UITableView/numberOfRowsInSection:) for more details.
18 | */
19 | public func numberOfRowsInSection(_ section: Int) -> Int {
20 | return tableView.numberOfRows(inSection: section)
21 | }
22 |
23 | /**
24 | The number of sections in the table view. (read-only)
25 |
26 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instp/UITableView/numberOfSections) for more details.
27 | */
28 | public var numberOfSections: Int {
29 | return tableView.numberOfSections
30 | }
31 |
32 | /**
33 | The height of each row (that is, table cell) in the table view.
34 |
35 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instp/UITableView/rowHeight) for more details
36 | */
37 | public var rowHeight: CGFloat {
38 | set { tableView.rowHeight = newValue }
39 | get { return tableView.rowHeight }
40 | }
41 |
42 | /**
43 | The style for table cells used as separators.
44 |
45 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instp/UITableView/separatorStyle) for more details.
46 | */
47 | public var separatorStyle: UITableViewCellSeparatorStyle {
48 | set { tableView.separatorStyle = newValue }
49 | get { return tableView.separatorStyle }
50 | }
51 |
52 | /**
53 | The color of separator rows in the table view.
54 |
55 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instp/UITableView/separatorColor) for more details.
56 | */
57 | public var separatorColor: UIColor? {
58 | set { tableView.separatorColor = newValue }
59 | get { return tableView.separatorColor }
60 | }
61 |
62 | /**
63 | The effect applied to table separators.
64 |
65 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instp/UITableView/separatorEffect) for more details.
66 | */
67 | @available(iOS 8.0, *)
68 | public var separatorEffect: UIVisualEffect? {
69 | set { tableView.separatorEffect = newValue }
70 | get { return tableView.separatorEffect }
71 | }
72 |
73 | /**
74 | Specifies the default inset of cell separators.
75 |
76 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instp/UITableView/separatorInset) for more details.
77 | */
78 | @available(iOS 7.0, *)
79 | public var separatorInset: UIEdgeInsets {
80 | set { tableView.separatorInset = newValue }
81 | get { return tableView.separatorInset }
82 | }
83 |
84 | /**
85 | A Boolean value that indicates whether the cell margins are derived from the width of the readable content guide.
86 |
87 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instp/UITableView/cellLayoutMarginsFollowReadableWidth) for more details.
88 | */
89 | @available(iOS 9.0, *)
90 | public var cellLayoutMarginsFollowReadableWidth: Bool {
91 | set { tableView.cellLayoutMarginsFollowReadableWidth = newValue }
92 | get { return tableView.cellLayoutMarginsFollowReadableWidth }
93 | }
94 | }
95 |
96 | extension StatefulTableView {
97 | // MARK: - Creating Table View Cells
98 |
99 | /**
100 | Registers a nib object containing a cell with the table view under a specified identifier.
101 |
102 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instm/UITableView/registerNib:forCellReuseIdentifier:) for more details.
103 | */
104 | @available(iOS 5.0, *)
105 | public func registerNib(_ nib: UINib?, forCellReuseIdentifier identifier: String) {
106 | tableView.register(nib, forCellReuseIdentifier: identifier)
107 | }
108 |
109 | /**
110 | Registers a class for use in creating new table cells.
111 |
112 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instm/UITableView/registerClass:forCellReuseIdentifier:) for more details.
113 | */
114 | @available(iOS 6.0, *)
115 | public func registerClass(_ cellClass: AnyClass?, forCellReuseIdentifier identifier: String) {
116 | tableView.register(cellClass, forCellReuseIdentifier: identifier)
117 | }
118 |
119 | /**
120 | Returns a reusable table-view cell object for the specified reuse identifier and adds it to the table.
121 |
122 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instm/UITableView/dequeueReusableCellWithIdentifier:forIndexPath:) for more details.
123 | */
124 | @available(iOS 6.0, *)
125 | public func dequeueReusableCellWithIdentifier(_ identifier: String, forIndexPath indexPath: IndexPath) -> UITableViewCell {
126 | return tableView.dequeueReusableCell(withIdentifier: identifier, for: indexPath)
127 | }
128 |
129 | /**
130 | Returns a reusable table-view cell object located by its identifier.
131 |
132 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instm/UITableView/dequeueReusableCellWithIdentifier:) for more details.
133 | */
134 | public func dequeueReusableCellWithIdentifier(_ identifier: String) -> UITableViewCell? {
135 | return tableView.dequeueReusableCell(withIdentifier: identifier)
136 | }
137 | }
138 |
139 | extension StatefulTableView {
140 | // MARK: - Accessing Header and Footer Views
141 |
142 | /**
143 | Registers a nib object containing a header or footer with the table view under a specified identifier.
144 |
145 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instm/UITableView/registerNib:forHeaderFooterViewReuseIdentifier:) for more details.
146 | */
147 | @available(iOS 6.0, *)
148 | public func registerNib(_ nib: UINib?, forHeaderFooterViewReuseIdentifier identifier: String) {
149 | tableView.register(nib, forHeaderFooterViewReuseIdentifier: identifier)
150 | }
151 |
152 | /**
153 | Registers a class for use in creating new table header or footer views.
154 |
155 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instm/UITableView/registerClass:forHeaderFooterViewReuseIdentifier:) for more details.
156 | */
157 | @available(iOS 6.0, *)
158 | public func registerClass(_ aClass: AnyClass?, forHeaderFooterViewReuseIdentifier identifier: String) {
159 | tableView.register(aClass, forHeaderFooterViewReuseIdentifier: identifier)
160 | }
161 |
162 | /**
163 | Returns a reusable header or footer view located by its identifier.
164 |
165 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instm/UITableView/dequeueReusableHeaderFooterViewWithIdentifier:) for more details.
166 | */
167 | @available(iOS 6.0, *)
168 | public func dequeueReusableHeaderFooterViewWithIdentifier(_ identifier: String) -> UITableViewHeaderFooterView? {
169 | return tableView.dequeueReusableHeaderFooterView(withIdentifier: identifier)
170 | }
171 |
172 | /**
173 | Returns an accessory view that is displayed above the table.
174 |
175 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instp/UITableView/tableHeaderView) for more details.
176 | */
177 | public var tableHeaderView: UIView? {
178 | set { tableView.tableHeaderView = newValue }
179 | get { return tableView.tableHeaderView }
180 | }
181 |
182 | /**
183 | Returns an accessory view that is displayed below the table.
184 |
185 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instp/UITableView/tableFooterView) for more details.
186 | */
187 | public var tableFooterView: UIView? {
188 | set { tableView.tableFooterView = newValue }
189 | get { return tableView.tableFooterView }
190 | }
191 |
192 | /**
193 | The height of section headers in the table view.
194 |
195 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instp/UITableView/sectionHeaderHeight) for more details.
196 | */
197 | public var sectionHeaderHeight: CGFloat {
198 | set { tableView.sectionHeaderHeight = newValue }
199 | get { return tableView.sectionHeaderHeight }
200 | }
201 |
202 | /**
203 | The height of section footers in the table view.
204 |
205 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instp/UITableView/sectionFooterHeight) for more details.
206 | */
207 | public var sectionFooterHeight: CGFloat {
208 | set { tableView.sectionFooterHeight = newValue }
209 | get { return tableView.sectionFooterHeight }
210 | }
211 |
212 | /**
213 | Returns the header view associated with the specified section.
214 |
215 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instm/UITableView/headerViewForSection:) for more details.
216 | */
217 | @available(iOS 6.0, *)
218 | public func headerViewForSection(_ section: Int) -> UITableViewHeaderFooterView? {
219 | return tableView.headerView(forSection: section)
220 | }
221 |
222 | /**
223 | Returns the footer view associated with the specified section.
224 |
225 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instm/UITableView/footerViewForSection:) for more details.
226 | */
227 | @available(iOS 6.0, *)
228 | public func footerViewForSection(_ section: Int) -> UITableViewHeaderFooterView? {
229 | return tableView.footerView(forSection: section)
230 | }
231 | }
232 |
233 | extension StatefulTableView {
234 | // MARK: - Accessing Cells and Sections
235 |
236 | /**
237 | Returns the table cell at the specified index path.
238 |
239 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instm/UITableView/cellForRowAtIndexPath:) for more details.
240 | */
241 | public func cellForRowAtIndexPath(_ indexPath: IndexPath) -> UITableViewCell? {
242 | return tableView.cellForRow(at: indexPath)
243 | }
244 |
245 | /**
246 | Returns an index path representing the row and section of a given table-view cell.
247 |
248 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instm/UITableView/indexPathForCell:) for more details.
249 | */
250 | public func indexPathForCell(_ cell: UITableViewCell) -> IndexPath? {
251 | return tableView.indexPath(for: cell)
252 | }
253 |
254 | /**
255 | Returns an index path identifying the row and section at the given point.
256 |
257 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instm/UITableView/indexPathForRowAtPoint:) for more details.
258 | */
259 | public func indexPathForRowAtPoint(_ point: CGPoint) -> IndexPath? {
260 | return tableView.indexPathForRow(at: point)
261 | }
262 |
263 | /**
264 | An array of index paths each representing a row enclosed by a given rectangle.
265 |
266 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instm/UITableView/indexPathsForRowsInRect:) for more details.
267 | */
268 | public func indexPathsForRowsInRect(_ rect: CGRect) -> [IndexPath]? {
269 | return tableView.indexPathsForRows(in: rect)
270 | }
271 |
272 | /**
273 | The table cells that are visible in the table view. (read-only)
274 |
275 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instp/UITableView/visibleCells) for more details.
276 | */
277 | public var visibleCells: [UITableViewCell] {
278 | return tableView.visibleCells
279 | }
280 |
281 | /**
282 | An array of index paths each identifying a visible row in the table view. (read-only)
283 |
284 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instp/UITableView/indexPathsForVisibleRows) for more details.
285 | */
286 | public var indexPathsForVisibleRows: [IndexPath]? {
287 | return tableView.indexPathsForVisibleRows;
288 | }
289 | }
290 |
291 | extension StatefulTableView {
292 | // MARK: - Estimating Element Heights
293 |
294 | /**
295 | The estimated height of rows in the table view.
296 |
297 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instp/UITableView/estimatedRowHeight) for more details.
298 | */
299 | @available(iOS 7.0, *)
300 | public var estimatedRowHeight: CGFloat {
301 | set { tableView.estimatedRowHeight = newValue }
302 | get { return tableView.estimatedRowHeight }
303 | }
304 |
305 | /**
306 | The estimated height of section headers in the table view.
307 |
308 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instp/UITableView/estimatedSectionHeaderHeight) for more details.
309 | */
310 | @available(iOS 7.0, *)
311 | public var estimatedSectionHeaderHeight: CGFloat {
312 | set { tableView.estimatedSectionHeaderHeight = newValue }
313 | get { return tableView.estimatedSectionHeaderHeight }
314 | }
315 |
316 | /**
317 | The estimated height of section footers in the table view.
318 |
319 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instp/UITableView/estimatedSectionFooterHeight) for more details.
320 | */
321 | @available(iOS 7.0, *)
322 | public var estimatedSectionFooterHeight: CGFloat {
323 | set { tableView.estimatedSectionFooterHeight = newValue }
324 | get { return tableView.estimatedSectionHeaderHeight }
325 | }
326 | }
327 |
328 | extension StatefulTableView {
329 | // MARK: - Scrolling the Table View
330 |
331 | /**
332 | Scrolls through the table view until a row identified by index path is at a particular location on the screen.
333 |
334 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instm/UITableView/scrollToRowAtIndexPath:atScrollPosition:animated:) for more details.
335 | */
336 | public func scrollToRowAtIndexPath(_ indexPath: IndexPath, atScrollPosition scrollPosition: UITableViewScrollPosition, animated: Bool) {
337 | tableView.scrollToRow(at: indexPath, at: scrollPosition, animated: animated)
338 | }
339 |
340 | /**
341 | Scrolls the table view so that the selected row nearest to a specified position in the table view is at that position.
342 |
343 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instm/UITableView/scrollToNearestSelectedRowAtScrollPosition:animated:) for more details.
344 | */
345 | public func scrollToNearestSelectedRowAtScrollPosition(_ scrollPosition: UITableViewScrollPosition, animated: Bool) {
346 | tableView.scrollToNearestSelectedRow(at: scrollPosition, animated: animated)
347 | }
348 | }
349 |
350 | extension StatefulTableView {
351 | // MARK: - Managing Selections
352 |
353 | /**
354 | An index path identifying the row and section of the selected row. (read-only)
355 |
356 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instp/UITableView/indexPathForSelectedRow) for more details.
357 | */
358 | public var indexPathForSelectedRow: IndexPath? {
359 | return tableView.indexPathForSelectedRow
360 | }
361 |
362 | /**
363 | The index paths representing the selected rows. (read-only)
364 |
365 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instp/UITableView/indexPathsForSelectedRows) for more details.
366 | */
367 | @available(iOS 5.0, *)
368 | public var indexPathsForSelectedRows: [IndexPath]? {
369 | return tableView.indexPathsForSelectedRows
370 | }
371 |
372 | /**
373 | Selects a row in the table view identified by index path, optionally scrolling the row to a location in the table view.
374 |
375 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instm/UITableView/selectRowAtIndexPath:animated:scrollPosition:) for more details.
376 | */
377 | public func selectRowAtIndexPath(_ indexPath: IndexPath?, animated: Bool, scrollPosition: UITableViewScrollPosition) {
378 | tableView.selectRow(at: indexPath, animated: animated, scrollPosition: scrollPosition)
379 | }
380 |
381 | /**
382 | Deselects a given row identified by index path, with an option to animate the deselection.
383 |
384 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instm/UITableView/deselectRowAtIndexPath:animated:) for more details.
385 | */
386 | public func deselectRowAtIndexPath(_ indexPath: IndexPath, animated: Bool) {
387 | tableView.deselectRow(at: indexPath, animated: animated)
388 | }
389 |
390 | /**
391 | A Boolean value that determines whether users can select a row.
392 |
393 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instp/UITableView/allowsSelection) for more details.
394 | */
395 | @available(iOS 3.0, *)
396 | public var allowsSelection: Bool {
397 | set { tableView.allowsSelection = newValue }
398 | get { return tableView.allowsSelection }
399 | }
400 |
401 | /**
402 | A Boolean value that determines whether users can select more than one row outside of editing mode.
403 |
404 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instp/UITableView/allowsMultipleSelectionhttps://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instp/UITableView/allowsMultipleSelection) for more details.
405 | */
406 | @available(iOS 5.0, *)
407 | public var allowsMultipleSelection: Bool {
408 | set { tableView.allowsMultipleSelection = newValue }
409 | get { return tableView.allowsMultipleSelection }
410 | }
411 |
412 | /**
413 | A Boolean value that determines whether users can select cells while the table view is in editing mode.
414 |
415 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instp/UITableView/allowsSelectionDuringEditing) for more details.
416 | */
417 | public var allowsSelectionDuringEditing: Bool {
418 | set { tableView.allowsSelectionDuringEditing = newValue }
419 | get { return tableView.allowsSelectionDuringEditing }
420 | }
421 |
422 | /**
423 | A Boolean value that controls whether users can select more than one cell simultaneously in editing mode.
424 |
425 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instp/UITableView/allowsMultipleSelectionDuringEditing) for more details.
426 | */
427 | @available(iOS 5.0, *)
428 | public var allowsMultipleSelectionDuringEditing: Bool {
429 | set { tableView.allowsMultipleSelectionDuringEditing = newValue }
430 | get { return tableView.allowsMultipleSelectionDuringEditing }
431 | }
432 | }
433 |
434 | extension StatefulTableView {
435 | // MARK: - Inserting, Deleting, and Moving Rows and Sections
436 |
437 | /**
438 | Begins a series of method calls that insert, delete, or select rows and sections of the table view.
439 |
440 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instm/UITableView/beginUpdates) for more details.
441 | */
442 | public func beginUpdates() {
443 | tableView.beginUpdates()
444 | }
445 |
446 | /**
447 | Concludes a series of method calls that insert, delete, select, or reload rows and sections of the table view.
448 |
449 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instm/UITableView/endUpdates) for more details.
450 | */
451 | public func endUpdates() {
452 | tableView.endUpdates()
453 | }
454 |
455 | /**
456 | Inserts rows in the table view at the locations identified by an array of index paths, with an option to animate the insertion.
457 |
458 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instm/UITableView/insertRowsAtIndexPaths:withRowAnimation:) for more details.
459 | */
460 | public func insertRowsAtIndexPaths(_ indexPaths: [IndexPath], withRowAnimation animation: UITableViewRowAnimation) {
461 | tableView.insertRows(at: indexPaths, with: animation)
462 | }
463 |
464 | /**
465 | Deletes the rows specified by an array of index paths, with an option to animate the deletion.
466 |
467 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instm/UITableView/deleteRowsAtIndexPaths:withRowAnimation:) for more details.
468 | */
469 | public func deleteRowsAtIndexPaths(indexPaths: [IndexPath], withRowAnimation animation: UITableViewRowAnimation) {
470 | tableView.deleteRows(at: indexPaths, with: animation)
471 | }
472 |
473 | /**
474 | Moves the row at a specified location to a destination location.
475 |
476 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instm/UITableView/moveRowAtIndexPath:toIndexPath:) for more details.
477 | */
478 | @available(iOS 5.0, *)
479 | public func moveRowAtIndexPath(_ indexPath: IndexPath, toIndexPath newIndexPath: IndexPath) {
480 | tableView.moveRow(at: indexPath, to: newIndexPath)
481 | }
482 |
483 | /**
484 | Inserts one or more sections in the table view, with an option to animate the insertion.
485 |
486 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instm/UITableView/insertSections:withRowAnimation:) for more details.
487 | */
488 | public func insertSections(_ sections: IndexSet, withRowAnimation animation: UITableViewRowAnimation) {
489 | tableView.insertSections(sections, with: animation)
490 |
491 | }
492 |
493 | /**
494 | Deletes one or more sections in the table view, with an option to animate the deletion.
495 |
496 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instm/UITableView/deleteSections:withRowAnimation:) for more details.
497 | */
498 | public func deleteSections(_ sections: IndexSet, withRowAnimation animation: UITableViewRowAnimation) {
499 | tableView.deleteSections(sections, with: animation)
500 | }
501 |
502 | /**
503 | Moves a section to a new location in the table view.
504 |
505 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instm/UITableView/moveSection:toSection:) for more details.
506 | */
507 | @available(iOS 5.0, *)
508 | public func moveSection(_ section: Int, toSection newSection: Int) {
509 | tableView.moveSection(section, toSection: newSection)
510 | }
511 | }
512 |
513 | extension StatefulTableView {
514 | // MARK: - Managing the Editing of Table Cells
515 |
516 | /**
517 | A Boolean value that determines whether the table view is in editing mode.
518 |
519 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instp/UITableView/editing) for more details.
520 | */
521 | public var editing: Bool {
522 | set { tableView.isEditing = newValue }
523 | get { return tableView.isEditing }
524 | }
525 |
526 | /**
527 | Toggles the table view into and out of editing mode.
528 |
529 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instm/UITableView/setEditing:animated:) for more details.
530 | */
531 | public func setEditing(_ editing: Bool, animated: Bool) {
532 | tableView.setEditing(editing, animated: animated)
533 | }
534 | }
535 |
536 | extension StatefulTableView {
537 | // MARK: - Reloading the Table View
538 |
539 | /**
540 | Reloads the rows and sections of the table view.
541 |
542 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instm/UITableView/reloadData) for more details.
543 | */
544 | public func reloadData() {
545 | DispatchQueue.main.async {
546 | self.tableView.reloadData()
547 | }
548 | }
549 |
550 | /**
551 | Reloads the specified rows using an animation effect.
552 |
553 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instm/UITableView/reloadRowsAtIndexPaths:withRowAnimation:) for more details.
554 | */
555 | @available(iOS 3.0, *)
556 | public func reloadRowsAtIndexPaths(_ indexPaths: [IndexPath], withRowAnimation animation: UITableViewRowAnimation) {
557 | tableView.reloadRows(at: indexPaths, with: animation)
558 | }
559 |
560 | /**
561 | Reloads the specified sections using a given animation effect.
562 |
563 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instm/UITableView/reloadSections:withRowAnimation:) for more details.
564 | */
565 | @available(iOS 3.0, *)
566 | public func reloadSections(_ sections: IndexSet, withRowAnimation animation: UITableViewRowAnimation) {
567 | tableView.reloadSections(sections, with: animation)
568 | }
569 |
570 | /**
571 | Reloads the items in the index bar along the right side of the table view.
572 |
573 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instm/UITableView/reloadSectionIndexTitles) for more details.
574 | */
575 | @available(iOS 3.0, *)
576 | public func reloadSectionIndexTitles() {
577 | DispatchQueue.main.async {
578 | self.tableView.reloadSectionIndexTitles()
579 | }
580 | }
581 | }
582 |
583 | extension StatefulTableView {
584 | // MARK: - Accessing Drawing Areas of the Table View
585 |
586 | /**
587 | Returns the drawing area for a specified section of the table view.
588 |
589 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instm/UITableView/rectForSection:) for more details.
590 | */
591 | public func rectForSection(_ section: Int) -> CGRect {
592 | return tableView.rect(forSection: section)
593 | }
594 |
595 | /**
596 | Returns the drawing area for a row identified by index path.
597 |
598 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instm/UITableView/rectForRowAtIndexPath:) for more details.
599 | */
600 | public func rectForRowRowAtIndexPath(_ indexPath: IndexPath) -> CGRect {
601 | return tableView.rectForRow(at: indexPath)
602 | }
603 |
604 | /**
605 | Returns the drawing area for the footer of the specified section.
606 |
607 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instm/UITableView/rectForFooterInSection:) for more details.
608 | */
609 | public func rectForFooterInSection(_ section: Int) -> CGRect {
610 | return tableView.rectForFooter(inSection: section)
611 | }
612 |
613 | /**
614 | Returns the drawing area for the header of the specified section.
615 |
616 | - Discussion: Visit this [link](Returns the drawing area for the header of the specified section.) for more details.
617 | */
618 | public func rectFotHeaderInSection(_ section: Int) -> CGRect {
619 | return tableView.rectForHeader(inSection: section)
620 | }
621 | }
622 |
623 | extension StatefulTableView {
624 | // MARK: - Managing the Delegate and the Data Source
625 |
626 | /**
627 | The object that acts as the data source of the table view.
628 |
629 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instp/UITableView/dataSource) for more details.
630 | */
631 | public var dataSource: UITableViewDataSource? {
632 | set { tableView.dataSource = newValue }
633 | get { return tableView.dataSource }
634 | }
635 |
636 | /**
637 | The object that acts as the delegate of the table view.
638 |
639 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instp/UITableView/delegate) for more details
640 | */
641 | public var delegate: UITableViewDelegate? {
642 | set { tableView.delegate = newValue }
643 | get { return tableView.delegate }
644 | }
645 | }
646 |
647 | extension StatefulTableView {
648 | // MARK: - Configuring the Table Index
649 |
650 | /**
651 | The number of table rows at which to display the index list on the right edge of the table.
652 |
653 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instp/UITableView/sectionIndexMinimumDisplayRowCount) for more details.
654 | */
655 | public var sectionIndexMinimumDisplayRowCount: Int {
656 | set { tableView.sectionIndexMinimumDisplayRowCount = newValue }
657 | get { return tableView.sectionIndexMinimumDisplayRowCount }
658 | }
659 |
660 | /**
661 | The color to use for the table view’s index text.
662 |
663 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instp/UITableView/sectionIndexColor) for more details.
664 | */
665 | @available(iOS 6.0, *)
666 | public var sectionIndexColor: UIColor? {
667 | set { tableView.sectionIndexColor = newValue }
668 | get { return tableView.sectionIndexColor }
669 | }
670 |
671 | /**
672 | The color to use for the background of the table view’s section index while not being touched.
673 |
674 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instp/UITableView/sectionIndexBackgroundColor) for more details.
675 | */
676 | @available(iOS 7.0, *)
677 | public var sectionIndexBackgroundColor: UIColor? {
678 | set { tableView.sectionIndexBackgroundColor = newValue }
679 | get { return tableView.sectionIndexBackgroundColor }
680 | }
681 |
682 | /**
683 | The color to use for the table view’s index background area.
684 |
685 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instp/UITableView/sectionIndexTrackingBackgroundColor) for more details.
686 | */
687 | @available(iOS 6.0, *)
688 | public var sectionIndexTrackingBackgroundColor: UIColor? {
689 | set { tableView.sectionIndexTrackingBackgroundColor = newValue }
690 | get { return tableView.sectionIndexTrackingBackgroundColor }
691 | }
692 | }
693 |
694 | extension StatefulTableView {
695 | // MARK: - Managing Focus
696 |
697 | /**
698 | A Boolean value that indicates whether the table view should automatically return the focus to the cell at the last focused index path.
699 |
700 | - Discussion: Visit this [link](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instp/UITableView/remembersLastFocusedIndexPath) for more details.
701 | */
702 | @available(iOS 9.0, *)
703 | public var remembersLastFocusedIndexPath: Bool {
704 | set { tableView.remembersLastFocusedIndexPath = newValue }
705 | get { return tableView.remembersLastFocusedIndexPath }
706 | }
707 | }
708 |
--------------------------------------------------------------------------------