Full Code of teambition/TBEmptyDataSet for AI

master b1e704d33322 cached
37 files
109.0 KB
30.5k tokens
1 requests
Download .txt
Repository: teambition/TBEmptyDataSet
Branch: master
Commit: b1e704d33322
Files: 37
Total size: 109.0 KB

Directory structure:
gitextract_9vpatnox/

├── .gitignore
├── .swift-version
├── .swiftlint.yml
├── Example/
│   ├── .swiftlint.yml
│   ├── AppDelegate.swift
│   ├── Assets.xcassets/
│   │   ├── AppIcon.appiconset/
│   │   │   └── Contents.json
│   │   ├── Contents.json
│   │   ├── icon-empty-events.imageset/
│   │   │   └── Contents.json
│   │   ├── icon-empty-message.imageset/
│   │   │   └── Contents.json
│   │   ├── icon-empty-photos.imageset/
│   │   │   └── Contents.json
│   │   └── loading.imageset/
│   │       └── Contents.json
│   ├── Base.lproj/
│   │   └── Main.storyboard
│   ├── DemoViewController.swift
│   ├── EmptyDataDemoCollectionViewController.swift
│   ├── EmptyDataDemoTableViewController.swift
│   └── Info.plist
├── LICENSE.md
├── README.md
├── TBEmptyDataSet/
│   ├── Constants.swift
│   ├── EmptyDataView.swift
│   ├── Protocols.swift
│   ├── Supporting Files/
│   │   └── Info.plist
│   ├── TBEmptyDataSet.swift
│   └── WeakObjectContainer.swift
├── TBEmptyDataSet.podspec
├── TBEmptyDataSet.xcodeproj/
│   ├── project.pbxproj
│   ├── xcshareddata/
│   │   └── xcschemes/
│   │       └── TBEmptyDataSet.xcscheme
│   └── xcuserdata/
│       ├── hongxin.xcuserdatad/
│       │   └── xcschemes/
│       │       └── xcschememanagement.plist
│       └── zetasq.xcuserdatad/
│           └── xcschemes/
│               └── xcschememanagement.plist
├── TBEmptyDataSet.xcworkspace/
│   ├── contents.xcworkspacedata
│   └── xcshareddata/
│       └── IDEWorkspaceChecks.plist
└── TBEmptyDataSetExample.xcodeproj/
    ├── project.pbxproj
    ├── project.xcworkspace/
    │   └── contents.xcworkspacedata
    └── xcuserdata/
        ├── hongxin.xcuserdatad/
        │   └── xcschemes/
        │       ├── TBEmptyDataSetExample.xcscheme
        │       └── xcschememanagement.plist
        └── zetasq.xcuserdatad/
            └── xcschemes/
                ├── TBEmptyDataSetExample.xcscheme
                └── xcschememanagement.plist

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
TBEmptyDataSet.xcworkspace/xcuserdata


================================================
FILE: .swift-version
================================================
5.0


================================================
FILE: .swiftlint.yml
================================================
disabled_rules: # rule identifiers to exclude from running
  - line_length
  - nesting
  - variable_name_min_length
  - cyclomatic_complexity
  - class_delegate_protocol
  - multiple_closures_with_trailing_closure
included: # paths to include during linting. `--path` is ignored if present. takes precendence over `excluded`.
excluded: # paths to ignore during linting. overridden by `included`.
  - Carthage
# parameterized rules are first parameterized as a warning level, then error level.
line_length: 150
type_name:
  min_length: 3 # only warning
  max_length: # warning and error
    warning: 100
    error: 200

function_parameter_count: 10
function_body_length:
  - 100
  - 200
type_body_length:
  - 300 # warning
  - 400 # error
file_length:
  - 1000 # warning
  - 1500 # error
variable_name:
  min_length: 2
  max_length: 40


================================================
FILE: Example/.swiftlint.yml
================================================
disabled_rules: # rule identifiers to exclude from running
  - line_length
  - nesting
included: # paths to include during linting. `--path` is ignored if present. takes precendence over `excluded`.
excluded: # paths to ignore during linting. overridden by `included`.
  - Carthage
# parameterized rules can be customized from this configuration file
line_length: 150
# parameterized rules are first parameterized as a warning level, then error level.
type_body_length:
  - 300 # warning
  - 400 # error


================================================
FILE: Example/AppDelegate.swift
================================================
//
//  AppDelegate.swift
//  TBEmptyDataSetExample
//
//  Created by 洪鑫 on 15/11/19.
//  Copyright © 2015年 Teambition. All rights reserved.
//

import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
        return true
    }

    func applicationWillResignActive(_ application: UIApplication) {

    }

    func applicationDidEnterBackground(_ application: UIApplication) {

    }

    func applicationWillEnterForeground(_ application: UIApplication) {

    }

    func applicationDidBecomeActive(_ application: UIApplication) {

    }

    func applicationWillTerminate(_ application: UIApplication) {

    }
}


================================================
FILE: Example/Assets.xcassets/AppIcon.appiconset/Contents.json
================================================
{
  "images" : [
    {
      "idiom" : "iphone",
      "size" : "20x20",
      "scale" : "2x"
    },
    {
      "idiom" : "iphone",
      "size" : "20x20",
      "scale" : "3x"
    },
    {
      "idiom" : "iphone",
      "size" : "29x29",
      "scale" : "2x"
    },
    {
      "idiom" : "iphone",
      "size" : "29x29",
      "scale" : "3x"
    },
    {
      "idiom" : "iphone",
      "size" : "40x40",
      "scale" : "2x"
    },
    {
      "idiom" : "iphone",
      "size" : "40x40",
      "scale" : "3x"
    },
    {
      "idiom" : "iphone",
      "size" : "60x60",
      "scale" : "2x"
    },
    {
      "idiom" : "iphone",
      "size" : "60x60",
      "scale" : "3x"
    },
    {
      "idiom" : "ipad",
      "size" : "20x20",
      "scale" : "1x"
    },
    {
      "idiom" : "ipad",
      "size" : "20x20",
      "scale" : "2x"
    },
    {
      "idiom" : "ipad",
      "size" : "29x29",
      "scale" : "1x"
    },
    {
      "idiom" : "ipad",
      "size" : "29x29",
      "scale" : "2x"
    },
    {
      "idiom" : "ipad",
      "size" : "40x40",
      "scale" : "1x"
    },
    {
      "idiom" : "ipad",
      "size" : "40x40",
      "scale" : "2x"
    },
    {
      "idiom" : "ipad",
      "size" : "76x76",
      "scale" : "1x"
    },
    {
      "idiom" : "ipad",
      "size" : "76x76",
      "scale" : "2x"
    },
    {
      "idiom" : "ipad",
      "size" : "83.5x83.5",
      "scale" : "2x"
    },
    {
      "idiom" : "ios-marketing",
      "size" : "1024x1024",
      "scale" : "1x"
    }
  ],
  "info" : {
    "version" : 1,
    "author" : "xcode"
  }
}

================================================
FILE: Example/Assets.xcassets/Contents.json
================================================
{
  "info" : {
    "version" : 1,
    "author" : "xcode"
  }
}

================================================
FILE: Example/Assets.xcassets/icon-empty-events.imageset/Contents.json
================================================
{
  "images" : [
    {
      "idiom" : "universal",
      "filename" : "icon-empty-events.pdf"
    }
  ],
  "info" : {
    "version" : 1,
    "author" : "xcode"
  }
}

================================================
FILE: Example/Assets.xcassets/icon-empty-message.imageset/Contents.json
================================================
{
  "images" : [
    {
      "idiom" : "universal",
      "filename" : "icon-empty-message.pdf"
    }
  ],
  "info" : {
    "version" : 1,
    "author" : "xcode"
  }
}

================================================
FILE: Example/Assets.xcassets/icon-empty-photos.imageset/Contents.json
================================================
{
  "images" : [
    {
      "idiom" : "universal",
      "filename" : "icon-empty-photos.pdf"
    }
  ],
  "info" : {
    "version" : 1,
    "author" : "xcode"
  }
}

================================================
FILE: Example/Assets.xcassets/loading.imageset/Contents.json
================================================
{
  "images" : [
    {
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "idiom" : "universal",
      "filename" : "loading@2x.png",
      "scale" : "2x"
    },
    {
      "idiom" : "universal",
      "filename" : "loading.png",
      "scale" : "3x"
    }
  ],
  "info" : {
    "version" : 1,
    "author" : "xcode"
  }
}

================================================
FILE: Example/Base.lproj/Main.storyboard
================================================
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13196" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="kbE-4i-otZ">
    <device id="retina4_7" orientation="portrait">
        <adaptation id="fullscreen"/>
    </device>
    <dependencies>
        <deployment identifier="iOS"/>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13173"/>
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    </dependencies>
    <scenes>
        <!--Empty Data Demo Table View Controller-->
        <scene sceneID="5qx-c9-kQh">
            <objects>
                <tableViewController id="Lch-ZU-4Kb" customClass="EmptyDataDemoTableViewController" customModule="TBEmptyDataSetExample" customModuleProvider="target" sceneMemberID="viewController">
                    <tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="28" sectionFooterHeight="28" id="qN0-9C-fAP">
                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                        <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                        <connections>
                            <outlet property="dataSource" destination="Lch-ZU-4Kb" id="7Ht-DJ-jpu"/>
                            <outlet property="delegate" destination="Lch-ZU-4Kb" id="Ehn-JM-S7A"/>
                        </connections>
                    </tableView>
                    <navigationItem key="navigationItem" id="ScQ-bM-CdO"/>
                    <refreshControl key="refreshControl" opaque="NO" multipleTouchEnabled="YES" contentMode="center" enabled="NO" contentHorizontalAlignment="center" contentVerticalAlignment="center" id="p9X-og-oGO">
                        <autoresizingMask key="autoresizingMask"/>
                    </refreshControl>
                </tableViewController>
                <placeholder placeholderIdentifier="IBFirstResponder" id="LQ9-uv-mnW" userLabel="First Responder" sceneMemberID="firstResponder"/>
            </objects>
            <point key="canvasLocation" x="2372" y="220"/>
        </scene>
        <!--Empty Data Demo Collection View Controller-->
        <scene sceneID="OLg-ZA-g8n">
            <objects>
                <collectionViewController id="FIk-rG-V0x" customClass="EmptyDataDemoCollectionViewController" customModule="TBEmptyDataSetExample" customModuleProvider="target" sceneMemberID="viewController">
                    <collectionView key="view" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="prototypes" id="WoI-bx-7tf">
                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                        <collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="10" minimumInteritemSpacing="10" id="AN4-8s-pSk">
                            <size key="itemSize" width="160" height="118"/>
                            <size key="headerReferenceSize" width="0.0" height="0.0"/>
                            <size key="footerReferenceSize" width="0.0" height="0.0"/>
                            <inset key="sectionInset" minX="0.0" minY="0.0" maxX="0.0" maxY="0.0"/>
                        </collectionViewFlowLayout>
                        <cells>
                            <collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="Cell" id="hIW-pB-eH7">
                                <rect key="frame" x="0.0" y="0.0" width="160" height="118"/>
                                <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
                                <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
                                    <rect key="frame" x="0.0" y="0.0" width="160" height="118"/>
                                    <autoresizingMask key="autoresizingMask"/>
                                    <subviews>
                                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Click to delete" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="BEj-6c-73Q">
                                            <rect key="frame" x="25.5" y="48" width="109.5" height="21"/>
                                            <fontDescription key="fontDescription" type="system" pointSize="17"/>
                                            <color key="textColor" red="0.2196078431372549" green="0.2196078431372549" blue="0.2196078431372549" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                            <nil key="highlightedColor"/>
                                        </label>
                                    </subviews>
                                </view>
                                <color key="backgroundColor" red="0.86611149850000002" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                <constraints>
                                    <constraint firstItem="BEj-6c-73Q" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="hIW-pB-eH7" secondAttribute="leading" id="JOd-4d-wJP"/>
                                    <constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="BEj-6c-73Q" secondAttribute="trailing" id="dv6-Kt-w7I"/>
                                    <constraint firstItem="BEj-6c-73Q" firstAttribute="centerY" secondItem="hIW-pB-eH7" secondAttribute="centerY" id="qrx-KL-dJl"/>
                                    <constraint firstItem="BEj-6c-73Q" firstAttribute="centerX" secondItem="hIW-pB-eH7" secondAttribute="centerX" id="uh9-Xc-Hvo"/>
                                </constraints>
                            </collectionViewCell>
                        </cells>
                        <connections>
                            <outlet property="dataSource" destination="FIk-rG-V0x" id="Ie5-4i-wFd"/>
                            <outlet property="delegate" destination="FIk-rG-V0x" id="p57-cM-0vu"/>
                        </connections>
                    </collectionView>
                </collectionViewController>
                <placeholder placeholderIdentifier="IBFirstResponder" id="CUf-YB-2wt" userLabel="First Responder" sceneMemberID="firstResponder"/>
            </objects>
            <point key="canvasLocation" x="2372" y="969"/>
        </scene>
        <!--Demo View Controller-->
        <scene sceneID="KuL-DV-c3x">
            <objects>
                <tableViewController id="0UU-b8-JN0" customClass="DemoViewController" customModule="TBEmptyDataSetExample" customModuleProvider="target" sceneMemberID="viewController">
                    <tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="28" sectionFooterHeight="28" id="6n3-iw-5uM">
                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                        <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                        <connections>
                            <outlet property="dataSource" destination="0UU-b8-JN0" id="kMW-9Q-Sq7"/>
                            <outlet property="delegate" destination="0UU-b8-JN0" id="QxC-zl-gDt"/>
                        </connections>
                    </tableView>
                    <navigationItem key="navigationItem" id="XiQ-GK-l0D"/>
                    <connections>
                        <segue destination="Lch-ZU-4Kb" kind="show" identifier="ShowEmptyDataDemoTableView" id="roH-Lu-khf"/>
                        <segue destination="FIk-rG-V0x" kind="show" identifier="ShowEmptyDataDemoCollectionView" id="pHA-i3-d8D"/>
                    </connections>
                </tableViewController>
                <placeholder placeholderIdentifier="IBFirstResponder" id="4v7-ty-bFt" userLabel="First Responder" sceneMemberID="firstResponder"/>
            </objects>
            <point key="canvasLocation" x="1533" y="562"/>
        </scene>
        <!--Navigation Controller-->
        <scene sceneID="Dg0-w9-0qB">
            <objects>
                <navigationController automaticallyAdjustsScrollViewInsets="NO" id="kbE-4i-otZ" sceneMemberID="viewController">
                    <toolbarItems/>
                    <navigationBar key="navigationBar" contentMode="scaleToFill" id="aL2-C2-A5C">
                        <rect key="frame" x="0.0" y="20" width="375" height="44"/>
                        <autoresizingMask key="autoresizingMask"/>
                    </navigationBar>
                    <nil name="viewControllers"/>
                    <connections>
                        <segue destination="0UU-b8-JN0" kind="relationship" relationship="rootViewController" id="FIc-3B-1LF"/>
                    </connections>
                </navigationController>
                <placeholder placeholderIdentifier="IBFirstResponder" id="tDd-z5-ifx" userLabel="First Responder" sceneMemberID="firstResponder"/>
            </objects>
            <point key="canvasLocation" x="721" y="562"/>
        </scene>
    </scenes>
</document>


================================================
FILE: Example/DemoViewController.swift
================================================
//
//  DemoViewController.swift
//  TBEmptyDataSetExample
//
//  Created by 洪鑫 on 15/11/26.
//  Copyright © 2015年 Teambition. All rights reserved.
//

import UIKit

public struct EmptyData {
    static let images = [#imageLiteral(resourceName: "icon-empty-photos"), #imageLiteral(resourceName: "icon-empty-events"), #imageLiteral(resourceName: "icon-empty-message")]
    static let titles = ["无照片", "无日程", "无新消息"]
    static let descriptions = ["你可以添加一些照片哦,让生活更精彩!", "暂时没有日程哦,添加一些日程吧!", "没有新消息哦,去找朋友聊聊天吧!"]
}

class DemoViewController: UITableViewController {
    // MARK: - Structs
    fileprivate struct Data {
        static let examples = ["Empty Photos", "Empty Events", "Empty Message"]
        static let sectionTitles = ["TableView", "CollectionView"]
    }

    fileprivate struct SegueIdentifier {
        static let showTableView = "ShowEmptyDataDemoTableView"
        static let showCollectionView = "ShowEmptyDataDemoCollectionView"
    }

    fileprivate struct CellIdentifier {
        static let reuseIdentifier = "Cell"
    }

    // MARK: - Properties
    var selectedIndexPath = IndexPath()

    // MARK: - View life cycle
    override func viewDidLoad() {
        super.viewDidLoad()

        navigationItem.title = "EmptyDataSet Example"
        let backButton = UIBarButtonItem()
        backButton.title = "Back"
        navigationItem.backBarButtonItem = backButton
        tableView.tableFooterView = UIView()
    }

    // MARK: - Table view data source and delegate
    override func numberOfSections(in tableView: UITableView) -> Int {
        return Data.sectionTitles.count
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return Data.examples.count
    }

    override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        return Data.sectionTitles[section]
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        var cell = tableView.dequeueReusableCell(withIdentifier: CellIdentifier.reuseIdentifier)
        if cell == nil {
            cell = UITableViewCell(style: .default, reuseIdentifier: CellIdentifier.reuseIdentifier)
        }
        cell!.accessoryType = .disclosureIndicator
        cell!.textLabel!.text = Data.examples[indexPath.row]

        return cell!
    }

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        selectedIndexPath = indexPath
        if indexPath.section == 0 {
            performSegue(withIdentifier: SegueIdentifier.showTableView, sender: self)
        } else if indexPath.section == 1 {
            performSegue(withIdentifier: SegueIdentifier.showCollectionView, sender: self)
        }

        tableView.deselectRow(at: indexPath, animated: true)
    }

    // MARK: - Navigation
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == SegueIdentifier.showTableView {
            if let emptyDataDemoTableViewController = segue.destination as? EmptyDataDemoTableViewController {
                emptyDataDemoTableViewController.indexPath = selectedIndexPath
            }
        } else if segue.identifier == SegueIdentifier.showCollectionView {
            if let emptyDataDemoCollectionViewController = segue.destination as? EmptyDataDemoCollectionViewController {
                emptyDataDemoCollectionViewController.indexPath = selectedIndexPath
            }
        }
    }
}


================================================
FILE: Example/EmptyDataDemoCollectionViewController.swift
================================================
//
//  EmptyDataDemoCollectionViewController.swift
//  TBEmptyDataSetExample
//
//  Created by 洪鑫 on 15/11/24.
//  Copyright © 2015年 Teambition. All rights reserved.
//

import UIKit
import TBEmptyDataSet

class EmptyDataDemoCollectionViewController: UICollectionViewController {
    // MARK: - Structs
    fileprivate struct CellIdentifier {
        static let reuseIdentifier = "Cell"
    }

    // MARK: - Properties
    var indexPath = IndexPath()
    fileprivate var isLoading = false
    fileprivate var dataCount = 0

    // MARK: - View life cycle
    override func viewDidLoad() {
        super.viewDidLoad()

        navigationItem.title = "CollectionView"
        collectionView?.backgroundColor = .white

        collectionView?.emptyDataSetDataSource = self
        collectionView?.emptyDataSetDelegate = self

        if indexPath.row != 0 {
            loadData(self)
        }
    }

    // MARK: - Helper
    func loadData(_ sender: Any) {
        isLoading = true
        let delayTime = DispatchTime.now() + Double(Int64(2 * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC)
        DispatchQueue.main.asyncAfter(deadline: delayTime) { () -> Void in
            self.dataCount = 4
            self.isLoading = false
            self.collectionView?.reloadData()
        }
    }

    // MARK: - Collection view data source
    override func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 1
    }

    override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return dataCount
    }

    override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CellIdentifier.reuseIdentifier, for: indexPath)

        let maskLayer = CAShapeLayer()
        let maskRect = cell.bounds
        maskLayer.frame = maskRect
        let cornerRadii = CGSize(width: 5, height: 5)
        let maskPath = UIBezierPath(roundedRect: maskRect, byRoundingCorners: .allCorners, cornerRadii: cornerRadii)
        maskLayer.path = maskPath.cgPath
        cell.layer.mask = maskLayer

        return cell
    }

    override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        self.dataCount -= 1
        collectionView.performBatchUpdates({
            self.collectionView?.deleteItems(at: [indexPath])
            }) { (_) in

        }
    }
}

extension EmptyDataDemoCollectionViewController: TBEmptyDataSetDataSource, TBEmptyDataSetDelegate {
    // MARK: - TBEmptyDataSet data source
    func imageForEmptyDataSet(in scrollView: UIScrollView) -> UIImage? {
        return EmptyData.images[indexPath.row]
    }

    func titleForEmptyDataSet(in scrollView: UIScrollView) -> NSAttributedString? {
        let title = EmptyData.titles[indexPath.row]
        var attributes: [NSAttributedString.Key: Any]?
        if indexPath.row == 1 {
            attributes = [.font: UIFont.systemFont(ofSize: 22),
                          .foregroundColor: UIColor.gray]
        } else if indexPath.row == 2 {
            attributes = [.font: UIFont.systemFont(ofSize: 24),
                          .foregroundColor: UIColor.gray]
        }
        return NSAttributedString(string: title, attributes: attributes)
    }

    func descriptionForEmptyDataSet(in scrollView: UIScrollView) -> NSAttributedString? {
        let description = EmptyData.descriptions[indexPath.row]
        var attributes: [NSAttributedString.Key: Any]?
        if indexPath.row == 1 {
            attributes = [.font: UIFont.systemFont(ofSize: 17),
                          .foregroundColor: UIColor(red: 3 / 255, green: 169 / 255, blue: 244 / 255, alpha: 1)]
        } else if indexPath.row == 2 {
            attributes = [.font: UIFont.systemFont(ofSize: 18),
                          .foregroundColor: UIColor.purple]
        }
        return NSAttributedString(string: description, attributes: attributes)
    }

    func backgroundColorForEmptyDataSet(in scrollView: UIScrollView) -> UIColor? {
        return UIColor(white: 0.95, alpha: 1)
    }

    func verticalOffsetForEmptyDataSet(in scrollView: UIScrollView) -> CGFloat {
        if let navigationBar = navigationController?.navigationBar {
            return -navigationBar.frame.height * 0.75
        }
        return 0
    }

    func customViewForEmptyDataSet(in scrollView: UIScrollView) -> UIView? {
        let loadingView: UIView = {
            if indexPath.row == 2 {
                return ExampleCustomView(frame: CGRect.zero)
            }

            let loadingImageView = UIImageView(image: #imageLiteral(resourceName: "loading"))
            let view = UIView(frame: loadingImageView.frame)
            view.addSubview(loadingImageView)

            let animation: CABasicAnimation = {
                let animation = CABasicAnimation(keyPath: "transform")
                animation.fromValue = NSValue(caTransform3D: CATransform3DIdentity)
                animation.toValue = NSValue(caTransform3D: CATransform3DMakeRotation(.pi / 2, 0, 0, 1))
                animation.duration = 0.3
                animation.isCumulative = true
                animation.repeatCount = .greatestFiniteMagnitude
                return animation
            }()
            loadingImageView.layer.add(animation, forKey: "loading")

            return view
        }()

        if isLoading {
            return loadingView
        } else {
            return nil
        }
    }

    // MARK: - TBEmptyDataSet delegate
    func emptyDataSetShouldDisplay(in scrollView: UIScrollView) -> Bool {
        return true
    }

    func emptyDataSetTapEnabled(in scrollView: UIScrollView) -> Bool {
        return true
    }

    func emptyDataSetScrollEnabled(in scrollView: UIScrollView) -> Bool {
        return true
    }

    func emptyDataSetDidTapEmptyView(in scrollView: UIScrollView) {
        let alert = UIAlertController(title: nil, message: "Did Tap EmptyDataView!", preferredStyle: .alert)
        let cancelAction = UIAlertAction(title: "OK", style: .cancel, handler: nil)
        alert.addAction(cancelAction)
        present(alert, animated: true, completion: nil)
    }

    func emptyDataSetWillAppear(in scrollView: UIScrollView) {
        print("EmptyDataSet Will Appear!")
    }

    func emptyDataSetDidAppear(in scrollView: UIScrollView) {
        print("EmptyDataSet Did Appear!")
    }

    func emptyDataSetWillDisappear(in scrollView: UIScrollView) {
        print("EmptyDataSet Will Disappear!")
    }

    func emptyDataSetDidDisappear(in scrollView: UIScrollView) {
        print("EmptyDataSet Did Disappear!")
    }
}

extension EmptyDataDemoCollectionViewController: UICollectionViewDelegateFlowLayout {
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        return CGSize(width: 150, height: 90)
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
        return UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20)
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
        return 20
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
        return 20
    }

    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        super.viewWillTransition(to: size, with: coordinator)

        collectionViewLayout.invalidateLayout()
        coordinator.animate(alongsideTransition: { (_) -> Void in

            }) { (_) -> Void in

        }
    }
}

class ExampleCustomView: UIView {
    fileprivate lazy var contentLabel: UILabel = {
        let contentLabel = UILabel()
        contentLabel.numberOfLines = 0
        contentLabel.textColor = .white
        contentLabel.translatesAutoresizingMaskIntoConstraints = false
        return contentLabel
    }()

    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        commonInit()
    }

    fileprivate func commonInit() {
        backgroundColor = .lightGray
        layer.cornerRadius = 6

        let activityIndicatorView = UIActivityIndicatorView(style: .whiteLarge)
        activityIndicatorView.translatesAutoresizingMaskIntoConstraints = false
        activityIndicatorView.startAnimating()
        contentLabel.text = "Loading... Please wait a moment...\n\n(This is a custom empty data view, which is using pure AutoLayout)"
        addSubview(activityIndicatorView)
        addSubview(contentLabel)

        addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-15-[activityIndicatorView]-20-[contentLabel]-15-|", options: [], metrics: nil, views: ["activityIndicatorView": activityIndicatorView, "contentLabel": contentLabel]))
        addConstraint(NSLayoutConstraint(item: activityIndicatorView, attribute: .centerX, relatedBy: .equal, toItem: self, attribute: .centerX, multiplier: 1, constant: 0))
        addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-15-[contentLabel]-15-|", options: [], metrics: nil, views: ["contentLabel": contentLabel]))

        translatesAutoresizingMaskIntoConstraints = false
    }
}


================================================
FILE: Example/EmptyDataDemoTableViewController.swift
================================================
//
//  EmptyDataDemoTableViewController.swift
//  TBEmptyDataSetExample
//
//  Created by 洪鑫 on 15/11/24.
//  Copyright © 2015年 Teambition. All rights reserved.
//

import UIKit
import TBEmptyDataSet

class EmptyDataDemoTableViewController: UITableViewController {
    // MARK: - Structs
    fileprivate struct CellIdentifier {
        static let reuseIdentifier = "Cell"
    }

    // MARK: - Properties
    var indexPath = IndexPath()
    fileprivate var isLoading = false
    fileprivate var dataCount = 0

    // MARK: - View life cycle
    override func viewDidLoad() {
        super.viewDidLoad()

        navigationItem.title = "TableView"
        tableView.tableFooterView = UIView()
        refreshControl?.addTarget(self, action: #selector(fetchData(_:)), for: .valueChanged)

        tableView.emptyDataSetDataSource = self
        tableView.emptyDataSetDelegate = self

        if indexPath.row != 0 {
            loadData(self)
        }
    }

    // MARK: - Helper
    @objc func fetchData(_ sender: Any) {
        let delayTime = DispatchTime.now() + Double(Int64(1.5 * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC)
        DispatchQueue.main.asyncAfter(deadline: delayTime) { () -> Void in
            self.dataCount = 7
            self.tableView.reloadData()
            self.refreshControl?.endRefreshing()
        }
    }

    func loadData(_ sender: Any) {
        isLoading = true
        let delayTime = DispatchTime.now() + Double(Int64(1.75 * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC)
        DispatchQueue.main.asyncAfter(deadline: delayTime) { () -> Void in
            self.isLoading = false
            self.tableView.reloadData()
        }
    }

    // MARK: - Table view data source and delegate
    override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return dataCount
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        var cell = tableView.dequeueReusableCell(withIdentifier: CellIdentifier.reuseIdentifier)
        if cell == nil {
            cell = UITableViewCell(style: .value1, reuseIdentifier: CellIdentifier.reuseIdentifier)
        }
        cell!.textLabel?.text = "Cell"
        cell!.detailTextLabel?.text = "Click to delete"
        return cell!
    }

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)
        tableView.beginUpdates()
        dataCount -= 1
        tableView.deleteRows(at: [indexPath], with: .fade)
        tableView.endUpdates()
    }
}

extension EmptyDataDemoTableViewController: TBEmptyDataSetDataSource, TBEmptyDataSetDelegate {
    // MARK: - TBEmptyDataSet data source
    func imageForEmptyDataSet(in scrollView: UIScrollView) -> UIImage? {
        return EmptyData.images[indexPath.row]
    }

    func titleForEmptyDataSet(in scrollView: UIScrollView) -> NSAttributedString? {
        let title = EmptyData.titles[indexPath.row]
        var attributes: [NSAttributedString.Key: Any]?
        if indexPath.row == 1 {
            attributes = [.font: UIFont.systemFont(ofSize: 22),
                          .foregroundColor: UIColor.gray]
        } else if indexPath.row == 2 {
            attributes = [.font: UIFont.systemFont(ofSize: 24),
                          .foregroundColor: UIColor.gray]
        }
        return NSAttributedString(string: title, attributes: attributes)
    }

    func descriptionForEmptyDataSet(in scrollView: UIScrollView) -> NSAttributedString? {
        let description = EmptyData.descriptions[indexPath.row]
        var attributes: [NSAttributedString.Key: Any]?
        if indexPath.row == 1 {
            attributes = [.font: UIFont.systemFont(ofSize: 17),
                          .foregroundColor: UIColor(red: 3 / 255, green: 169 / 255, blue: 244 / 255, alpha: 1)]
        } else if indexPath.row == 2 {
            attributes = [.font: UIFont.systemFont(ofSize: 18),
                          .foregroundColor: UIColor.purple]
        }
        return NSAttributedString(string: description, attributes: attributes)
    }

    func verticalOffsetForEmptyDataSet(in scrollView: UIScrollView) -> CGFloat {
        if let navigationBar = navigationController?.navigationBar {
            return -navigationBar.frame.height * 0.75
        }
        return 0
    }

    func verticalSpacesForEmptyDataSet(in scrollView: UIScrollView) -> [CGFloat] {
        return [25, 8]
    }

    func customViewForEmptyDataSet(in scrollView: UIScrollView) -> UIView? {
        if isLoading {
            let activityIndicator = UIActivityIndicatorView(style: .gray)
            activityIndicator.startAnimating()
            return activityIndicator
        }
        return nil
    }

    // MARK: - TBEmptyDataSet delegate
    func emptyDataSetShouldDisplay(in scrollView: UIScrollView) -> Bool {
        return true
    }

    func emptyDataSetTapEnabled(in scrollView: UIScrollView) -> Bool {
        return true
    }

    func emptyDataSetScrollEnabled(in scrollView: UIScrollView) -> Bool {
        return true
    }

    func emptyDataSetDidTapEmptyView(in scrollView: UIScrollView) {
        let alert = UIAlertController(title: nil, message: "Did Tap EmptyDataView!", preferredStyle: .alert)
        let cancelAction = UIAlertAction(title: "OK", style: .cancel, handler: nil)
        alert.addAction(cancelAction)
        present(alert, animated: true, completion: nil)
    }

    func emptyDataSetWillAppear(in scrollView: UIScrollView) {
        print("EmptyDataSet Will Appear!")
    }

    func emptyDataSetDidAppear(in scrollView: UIScrollView) {
        print("EmptyDataSet Did Appear!")
    }

    func emptyDataSetWillDisappear(in scrollView: UIScrollView) {
        print("EmptyDataSet Will Disappear!")
    }

    func emptyDataSetDidDisappear(in scrollView: UIScrollView) {
        print("EmptyDataSet Did Disappear!")
    }
}


================================================
FILE: Example/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>CFBundleDevelopmentRegion</key>
	<string>en</string>
	<key>CFBundleExecutable</key>
	<string>$(EXECUTABLE_NAME)</string>
	<key>CFBundleIdentifier</key>
	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
	<key>CFBundleInfoDictionaryVersion</key>
	<string>6.0</string>
	<key>CFBundleName</key>
	<string>$(PRODUCT_NAME)</string>
	<key>CFBundlePackageType</key>
	<string>APPL</string>
	<key>CFBundleShortVersionString</key>
	<string>3.0.2</string>
	<key>CFBundleSignature</key>
	<string>????</string>
	<key>CFBundleVersion</key>
	<string>23</string>
	<key>LSRequiresIPhoneOS</key>
	<true/>
	<key>UILaunchStoryboardName</key>
	<string>LaunchScreen</string>
	<key>UIMainStoryboardFile</key>
	<string>Main</string>
	<key>UIRequiredDeviceCapabilities</key>
	<array>
		<string>armv7</string>
	</array>
	<key>UISupportedInterfaceOrientations</key>
	<array>
		<string>UIInterfaceOrientationPortrait</string>
		<string>UIInterfaceOrientationLandscapeLeft</string>
		<string>UIInterfaceOrientationLandscapeRight</string>
	</array>
	<key>UISupportedInterfaceOrientations~ipad</key>
	<array>
		<string>UIInterfaceOrientationPortrait</string>
		<string>UIInterfaceOrientationPortraitUpsideDown</string>
		<string>UIInterfaceOrientationLandscapeLeft</string>
		<string>UIInterfaceOrientationLandscapeRight</string>
	</array>
</dict>
</plist>


================================================
FILE: LICENSE.md
================================================
The MIT License (MIT)

Copyright (c) 2015 Teambition

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.



================================================
FILE: README.md
================================================
# TBEmptyDataSet
TBEmptyDataSet is an extension of UITableView/UICollectionView's super class, it will  display a placeholder emptyDataSet when the data of tableView/collectionView is empty.

TBEmptyDataSet can be composed of an image, a title and a description, or you can set it as a custom view.


![Example](Screenshots/Example.gif "Example")


## How To Get Started
### Carthage
Specify "TBEmptyDataSet" in your ```Cartfile```:
```ogdl 
github "teambition/TBEmptyDataSet"
```

### CocoaPods
Specify "TBEmptyDataSet" in your ```Podfile```:
```ruby 
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.0'
use_frameworks!

pod 'TBEmptyDataSet'
```

### Usage
##### 1.  Assign the data source and delegate
```swift
tableView.emptyDataSetDataSource = self
tableView.emptyDataSetDelegate = self
```
##### 2.  Implement the data source and delegate
Data source functions:
```swift 
func imageForEmptyDataSet(in scrollView: UIScrollView) -> UIImage? {
    // return the image for EmptyDataSet
}

func titleForEmptyDataSet(in scrollView: UIScrollView) -> NSAttributedString? {
    // return the title for EmptyDataSet
}

func descriptionForEmptyDataSet(in scrollView: UIScrollView) -> NSAttributedString? {
    // return the description for EmptyDataSet
}

func imageTintColorForEmptyDataSet(in scrollView: UIScrollView) -> UIColor? {
    // return the image tint color for EmptyDataSet
}

func backgroundColorForEmptyDataSet(in scrollView: UIScrollView) -> UIColor? {
    // return the backgroundColor for EmptyDataSet
}

func verticalOffsetForEmptyDataSet(in scrollView: UIScrollView) -> CGFloat {
    // return the vertical offset for EmptyDataSet, default is 0
}

func verticalSpacesForEmptyDataSet(in scrollView: UIScrollView) -> [CGFloat] {
    // return the vertical spaces from top to bottom for EmptyDataSet, default is [12, 12]
}

func titleMarginForEmptyDataSet(in scrollView: UIScrollView) -> CGFloat {
    // return the minimum horizontal margin space for title, default is 15
}

func descriptionMarginForEmptyDataSet(in scrollView: UIScrollView) -> CGFloat {
    // return the minimum horizontal margin space for description, default is 15
}

func customViewForEmptyDataSet(in scrollView: UIScrollView) -> UIView? {
    // return an UIView instance for EmptyDataSet
}
``` 
Delegate functions:
```swift
func emptyDataSetShouldDisplay(in scrollView: UIScrollView) -> Bool {
    // should display EmptyDataSet or not, default is true
}

func emptyDataSetTapEnabled(in scrollView: UIScrollView) -> Bool {
    // enable tap gesture or not, default is true
}

func emptyDataSetScrollEnabled(in scrollView: UIScrollView) -> Bool {
    // scrollView can scroll or not, default is false
}

func emptyDataSetDidTapEmptyView(in scrollView: UIScrollView) {
    // do something
}

func emptyDataSetWillAppear(in scrollView: UIScrollView) {
    // do something
}

func emptyDataSetDidAppear(in scrollView: UIScrollView) {
    // do something
}

func emptyDataSetWillDisappear(in scrollView: UIScrollView) {
    // do something
}

func emptyDataSetDidDisappear(in scrollView: UIScrollView) {
    // do something
}
```
##### 3.  Data source events (inserting, deleting, reloading, etc.)
TBEmptyDataSet will update automatically when the data source of table view or collection view changes.

To be specific:
* For UITableView, it updates when ```endUpdates()``` is called.
* For both UITableView and UICollectionView, it updates when ```performBatchUpdates(_:completion:)``` is completed.
* For both UITableView and UICollectionView, it updates when ```reloadData()``` is called.

## Minimum Requirement
iOS 8.0

## Release Notes
* [Release Notes](https://github.com/teambition/TBEmptyDataSet/releases)

## License
TBEmptyDataSet is released under the MIT license. See [LICENSE](https://github.com/teambition/TBEmptyDataSet/blob/master/LICENSE.md) for details.

## More Info
Have a question? Please [open an issue](https://github.com/teambition/TBEmptyDataSet/issues/new)!


================================================
FILE: TBEmptyDataSet/Constants.swift
================================================
//
//  Constants.swift
//  TBEmptyDataSet
//
//  Created by Xin Hong on 15/11/19.
//  Copyright © 2015年 Teambition. All rights reserved.
//

import UIKit

// swiftlint:disable variable_name
internal struct AssociatedKeys {
    static var emptyDataSetDataSource = "emptyDataSetDataSource"
    static var emptyDataSetDelegate = "emptyDataSetDelegate"
    static var emptyDataView = "emptyDataView"
}

internal struct Selectors {
    static let tableViewSwizzledReloadData = #selector(UIScrollView.tb_tableViewSwizzledReloadData)
    static let tableViewSwizzledEndUpdates = #selector(UIScrollView.tb_tableViewSwizzledEndUpdates)
    @available(iOS 11.0, *)
    static let tableViewSwizzledPerformBatchUpdates = #selector(UIScrollView.tb_tableViewSwizzledPerformBatchUpdates(_:completion:))
    static let collectionViewSwizzledReloadData = #selector(UIScrollView.tb_collectionViewSwizzledReloadData)
    static let collectionViewSwizzledPerformBatchUpdates = #selector(UIScrollView.tb_collectionViewSwizzledPerformBatchUpdates(_:completion:))
}

internal struct TableViewSelectors {
    static let reloadData = #selector(UITableView.reloadData)
    static let endUpdates = #selector(UITableView.endUpdates)
    static let numberOfSections = #selector(UITableViewDataSource.numberOfSections(in:))
    @available(iOS 11.0, *)
    static let performBatchUpdates = #selector(UITableView.performBatchUpdates(_:completion:))
}

internal struct CollectionViewSelectors {
    static let reloadData = #selector(UICollectionView.reloadData)
    static let numberOfSections = #selector(UICollectionViewDataSource.numberOfSections(in:))
    static let performBatchUpdates = #selector(UICollectionView.performBatchUpdates(_:completion:))
}

internal struct DefaultValues {
    static let verticalOffset: CGFloat = 0
    static let verticalSpace: CGFloat = 12
    static let verticalSpaces = [verticalSpace, verticalSpace]
    static let titleMargin: CGFloat = 15
    static let descriptionMargin: CGFloat = 15
}


================================================
FILE: TBEmptyDataSet/EmptyDataView.swift
================================================
//
//  EmptyDataView.swift
//  TBEmptyDataSet
//
//  Created by Xin Hong on 15/11/19.
//  Copyright © 2015年 Teambition. All rights reserved.
//

import UIKit

internal class EmptyDataView: UIView {
    fileprivate struct ViewStrings {
        static let contentView = "contentView"
        static let imageView = "imageView"
        static let titleLabel = "titleLabel"
        static let descriptionLabel = "descriptionLabel"
        static let customView = "customView"
    }

    // MARK: - Properties
    internal lazy var contentView: UIView = {
        let contentView = UIView()
        contentView.translatesAutoresizingMaskIntoConstraints = false
        contentView.backgroundColor = .clear
        contentView.alpha = 0
        return contentView
    }()

    internal lazy var imageView: UIImageView = { [unowned self] in
        let imageView = UIImageView()
        imageView.translatesAutoresizingMaskIntoConstraints = false
        imageView.backgroundColor = .clear
        imageView.contentMode = .scaleAspectFill
        self.contentView.addSubview(imageView)
        return imageView
    }()

    internal lazy var titleLabel: UILabel = { [unowned self] in
        let titleLabel = UILabel()
        titleLabel.translatesAutoresizingMaskIntoConstraints = false
        titleLabel.backgroundColor = .clear
        titleLabel.font = .systemFont(ofSize: 27)
        titleLabel.textColor = UIColor(white: 0.6, alpha: 1)
        titleLabel.textAlignment = .center
        titleLabel.lineBreakMode = .byWordWrapping
        titleLabel.numberOfLines = 0
        self.contentView.addSubview(titleLabel)
        return titleLabel
    }()

    internal lazy var descriptionLabel: UILabel = { [unowned self] in
        let descriptionLabel = UILabel()
        descriptionLabel.translatesAutoresizingMaskIntoConstraints = false
        descriptionLabel.backgroundColor = .clear
        descriptionLabel.font = .systemFont(ofSize: 17)
        descriptionLabel.textColor = UIColor(white: 0.6, alpha: 1)
        descriptionLabel.textAlignment = .center
        descriptionLabel.lineBreakMode = .byWordWrapping
        descriptionLabel.numberOfLines = 0
        self.contentView.addSubview(descriptionLabel)
        return descriptionLabel
    }()

    internal var customView: UIView? {
        willSet {
            if let customView = customView {
                customView.removeFromSuperview()
            }
        }
        didSet {
            if let customView = customView {
                contentView.addSubview(customView)
            }
        }
    }
    internal var tapGesture: UITapGestureRecognizer?
    internal var verticalOffset = DefaultValues.verticalOffset
    internal var verticalSpaces = DefaultValues.verticalSpaces
    internal var titleMargin = DefaultValues.titleMargin
    internal var descriptionMargin = DefaultValues.descriptionMargin

    // MARK: - Helper
    fileprivate func shouldShowImageView() -> Bool {
        return imageView.image != nil
    }

    fileprivate func shouldShowTitleLabel() -> Bool {
        if let title = titleLabel.attributedText {
            return title.length > 0
        }
        return false
    }

    fileprivate func shouldShowDescriptionLabel() -> Bool {
        if let description = descriptionLabel.attributedText {
            return description.length > 0
        }
        return false
    }

    fileprivate func removeAllConstraints() {
        contentView.removeConstraints(contentView.constraints)
        removeConstraints(constraints)
    }

    // MARK: - View life cycle
    override init(frame: CGRect) {
        super.init(frame: frame)
        addSubview(self.contentView)
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }

    override func didMoveToSuperview() {
        frame = super.bounds
        UIView.animate(withDuration: 0.25, animations: { () -> Void in
            self.contentView.alpha = 1
        })
    }

    // MARK: - Actions
    internal func setConstraints() {
        let centerX = NSLayoutConstraint(item: contentView, attribute: .centerX, relatedBy: .equal, toItem: self, attribute: .centerX, multiplier: 1, constant: 0)
        let centerY = NSLayoutConstraint(item: contentView, attribute: .centerY, relatedBy: .equal, toItem: self, attribute: .centerY, multiplier: 1, constant: verticalOffset)
        addConstraint(centerX)
        addConstraint(centerY)
        addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[\(ViewStrings.contentView)]|", options: [], metrics: nil, views: [ViewStrings.contentView: contentView]))

        if let customView = customView {
            let translatesFrameIntoConstraints = customView.translatesAutoresizingMaskIntoConstraints
            customView.translatesAutoresizingMaskIntoConstraints = false

            if translatesFrameIntoConstraints {
                contentView.addConstraint(NSLayoutConstraint(item: customView, attribute: .width, relatedBy: .equal, toItem: .none, attribute: .notAnAttribute, multiplier: 1, constant: customView.frame.width))
                contentView.addConstraint(NSLayoutConstraint(item: customView, attribute: .height, relatedBy: .equal, toItem: .none, attribute: .notAnAttribute, multiplier: 1, constant: customView.frame.height))

                contentView.addConstraint(NSLayoutConstraint(item: customView, attribute: .centerX, relatedBy: .equal, toItem: contentView, attribute: .centerX, multiplier: 1, constant: 0))
                contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[\(ViewStrings.customView)]|", options: [], metrics: nil, views: [ViewStrings.customView: customView]))
            } else {
                contentView.addConstraint(NSLayoutConstraint(item: customView, attribute: .centerX, relatedBy: .equal, toItem: contentView, attribute: .centerX, multiplier: 1, constant: 0))
                contentView.addConstraint(NSLayoutConstraint(item: customView, attribute: .leading, relatedBy: .greaterThanOrEqual, toItem: contentView, attribute: .leading, multiplier: 1, constant: 0))
                contentView.addConstraint(NSLayoutConstraint(item: customView, attribute: .trailing, relatedBy: .lessThanOrEqual, toItem: contentView, attribute: .trailing, multiplier: 1, constant: 0))

                contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[\(ViewStrings.customView)]|", options: [], metrics: nil, views: [ViewStrings.customView: customView]))
            }
        } else {
            var viewStrings = [String]()
            var views = [String: UIView]()

            if shouldShowImageView() {
                if imageView.superview == nil {
                    contentView.addSubview(imageView)
                }
                let viewString = ViewStrings.imageView
                viewStrings.append(viewString)
                views[viewString] = imageView

                contentView.addConstraint(NSLayoutConstraint(item: imageView, attribute: .centerX, relatedBy: .equal, toItem: contentView, attribute: .centerX, multiplier: 1, constant: 0))
            } else {
                imageView.removeFromSuperview()
            }

            if shouldShowTitleLabel() {
                if titleLabel.superview == nil {
                    contentView.addSubview(titleLabel)
                }
                let viewString = ViewStrings.titleLabel
                viewStrings.append(viewString)
                views[viewString] = titleLabel
                contentView.addConstraint(NSLayoutConstraint(item: titleLabel, attribute: .centerX, relatedBy: .equal, toItem: contentView, attribute: .centerX, multiplier: 1, constant: 0))
                contentView.addConstraint(NSLayoutConstraint(item: titleLabel, attribute: .leading, relatedBy: .greaterThanOrEqual, toItem: contentView, attribute: .leading, multiplier: 1, constant: titleMargin))
                contentView.addConstraint(NSLayoutConstraint(item: contentView, attribute: .trailing, relatedBy: .greaterThanOrEqual, toItem: titleLabel, attribute: .trailing, multiplier: 1, constant: titleMargin))
            } else {
                titleLabel.removeFromSuperview()
            }

            if shouldShowDescriptionLabel() {
                if descriptionLabel.superview == nil {
                    contentView.addSubview(descriptionLabel)
                }
                let viewString = ViewStrings.descriptionLabel
                viewStrings.append(viewString)
                views[viewString] = descriptionLabel
                contentView.addConstraint(NSLayoutConstraint(item: descriptionLabel, attribute: .centerX, relatedBy: .equal, toItem: contentView, attribute: .centerX, multiplier: 1, constant: 0))
                contentView.addConstraint(NSLayoutConstraint(item: descriptionLabel, attribute: .leading, relatedBy: .greaterThanOrEqual, toItem: contentView, attribute: .leading, multiplier: 1, constant: descriptionMargin))
                contentView.addConstraint(NSLayoutConstraint(item: contentView, attribute: .trailing, relatedBy: .greaterThanOrEqual, toItem: descriptionLabel, attribute: .trailing, multiplier: 1, constant: descriptionMargin))
            } else {
                descriptionLabel.removeFromSuperview()
            }

            var verticalFormat = String()
            for (index, viewString) in viewStrings.enumerated() {
                verticalFormat += "[\(viewString)]"
                if index != viewStrings.count - 1 {
                    let verticalSpace = index < verticalSpaces.count ? verticalSpaces[index] : DefaultValues.verticalSpace
                    verticalFormat += "-(\(verticalSpace))-"
                }
            }

            if !verticalFormat.isEmpty {
                contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|\(verticalFormat)|", options: [], metrics: nil, views: views))
            }
        }
    }

    internal func prepareForDisplay() {
        imageView.image = nil
        titleLabel.attributedText = nil
        descriptionLabel.attributedText = nil
        customView = nil
        removeAllConstraints()
    }

    internal func reset() {
        imageView.image = nil
        titleLabel.attributedText = nil
        descriptionLabel.attributedText = nil
        customView = nil
        tapGesture = nil
        removeAllConstraints()
    }
}


================================================
FILE: TBEmptyDataSet/Protocols.swift
================================================
//
//  Protocols.swift
//  TBEmptyDataSet
//
//  Created by Xin Hong on 15/11/19.
//  Copyright © 2015年 Teambition. All rights reserved.
//

import UIKit

public protocol TBEmptyDataSetDataSource {
    func imageForEmptyDataSet(in scrollView: UIScrollView) -> UIImage?
    func titleForEmptyDataSet(in scrollView: UIScrollView) -> NSAttributedString?
    func descriptionForEmptyDataSet(in scrollView: UIScrollView) -> NSAttributedString?

    func imageTintColorForEmptyDataSet(in scrollView: UIScrollView) -> UIColor?
    func backgroundColorForEmptyDataSet(in scrollView: UIScrollView) -> UIColor?

    func verticalOffsetForEmptyDataSet(in scrollView: UIScrollView) -> CGFloat
    func verticalSpacesForEmptyDataSet(in scrollView: UIScrollView) -> [CGFloat]
    func titleMarginForEmptyDataSet(in scrollView: UIScrollView) -> CGFloat
    func descriptionMarginForEmptyDataSet(in scrollView: UIScrollView) -> CGFloat

    func customViewForEmptyDataSet(in scrollView: UIScrollView) -> UIView?
}

public protocol TBEmptyDataSetDelegate {
    func emptyDataSetShouldDisplay(in scrollView: UIScrollView) -> Bool
    func emptyDataSetTapEnabled(in scrollView: UIScrollView) -> Bool
    func emptyDataSetScrollEnabled(in scrollView: UIScrollView) -> Bool

    func emptyDataSetDidTapEmptyView(in scrollView: UIScrollView)

    func emptyDataSetWillAppear(in scrollView: UIScrollView)
    func emptyDataSetDidAppear(in scrollView: UIScrollView)
    func emptyDataSetWillDisappear(in scrollView: UIScrollView)
    func emptyDataSetDidDisappear(in scrollView: UIScrollView)
}

public extension TBEmptyDataSetDataSource {
    func imageForEmptyDataSet(in scrollView: UIScrollView) -> UIImage? {
        return nil
    }

    func titleForEmptyDataSet(in scrollView: UIScrollView) -> NSAttributedString? {
        return nil
    }

    func descriptionForEmptyDataSet(in scrollView: UIScrollView) -> NSAttributedString? {
        return nil
    }

    func imageTintColorForEmptyDataSet(in scrollView: UIScrollView) -> UIColor? {
        return nil
    }

    func backgroundColorForEmptyDataSet(in scrollView: UIScrollView) -> UIColor? {
        return nil
    }

    func verticalOffsetForEmptyDataSet(in scrollView: UIScrollView) -> CGFloat {
        return DefaultValues.verticalOffset
    }

    func verticalSpacesForEmptyDataSet(in scrollView: UIScrollView) -> [CGFloat] {
        return DefaultValues.verticalSpaces
    }

    func titleMarginForEmptyDataSet(in scrollView: UIScrollView) -> CGFloat {
        return DefaultValues.titleMargin
    }

    func descriptionMarginForEmptyDataSet(in scrollView: UIScrollView) -> CGFloat {
        return DefaultValues.descriptionMargin
    }

    func customViewForEmptyDataSet(in scrollView: UIScrollView) -> UIView? {
        return nil
    }
}

public extension TBEmptyDataSetDelegate {
    func emptyDataSetShouldDisplay(in scrollView: UIScrollView) -> Bool {
        return true
    }

    func emptyDataSetTapEnabled(in scrollView: UIScrollView) -> Bool {
        return true
    }

    func emptyDataSetScrollEnabled(in scrollView: UIScrollView) -> Bool {
        return false
    }

    func emptyDataSetDidTapEmptyView(in scrollView: UIScrollView) {

    }

    func emptyDataSetWillAppear(in scrollView: UIScrollView) {

    }

    func emptyDataSetDidAppear(in scrollView: UIScrollView) {

    }

    func emptyDataSetWillDisappear(in scrollView: UIScrollView) {

    }

    func emptyDataSetDidDisappear(in scrollView: UIScrollView) {

    }
}


================================================
FILE: TBEmptyDataSet/Supporting Files/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>CFBundleDevelopmentRegion</key>
	<string>en</string>
	<key>CFBundleExecutable</key>
	<string>$(EXECUTABLE_NAME)</string>
	<key>CFBundleIdentifier</key>
	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
	<key>CFBundleInfoDictionaryVersion</key>
	<string>6.0</string>
	<key>CFBundleName</key>
	<string>$(PRODUCT_NAME)</string>
	<key>CFBundlePackageType</key>
	<string>FMWK</string>
	<key>CFBundleShortVersionString</key>
	<string>3.0.2</string>
	<key>CFBundleSignature</key>
	<string>????</string>
	<key>CFBundleVersion</key>
	<string>$(CURRENT_PROJECT_VERSION)</string>
	<key>NSPrincipalClass</key>
	<string></string>
</dict>
</plist>


================================================
FILE: TBEmptyDataSet/TBEmptyDataSet.swift
================================================
//
//  TBEmptyDataSet.swift
//  TBEmptyDataSet
//
//  Created by Xin Hong on 15/11/19.
//  Copyright © 2015年 Teambition. All rights reserved.
//

import UIKit

extension UIScrollView {
    // MARK: - Properties
    public var emptyDataSetDataSource: TBEmptyDataSetDataSource? {
        get {
            let container = objc_getAssociatedObject(self, &AssociatedKeys.emptyDataSetDataSource) as? WeakObjectContainer
            return container?.object as? TBEmptyDataSetDataSource
        }
        set {
            if let newValue = newValue {
                objc_setAssociatedObject(self, &AssociatedKeys.emptyDataSetDataSource, WeakObjectContainer(object: newValue), .OBJC_ASSOCIATION_RETAIN_NONATOMIC)

                switch self {
                case is UITableView:
                    UITableView.tb_swizzleTableViewReloadData
                    UITableView.tb_swizzleTableViewEndUpdates
                    if #available(iOS 11.0, *) {
                        UITableView.tb_swizzleTableViewPerformBatchUpdates
                    }
                case is UICollectionView:
                    UICollectionView.tb_swizzleCollectionViewReloadData
                    UICollectionView.tb_swizzleCollectionViewPerformBatchUpdates
                default:
                    break
                }
            } else {
                handlingInvalidEmptyDataSet()
            }
        }
    }

    public var emptyDataSetDelegate: TBEmptyDataSetDelegate? {
        get {
            let container = objc_getAssociatedObject(self, &AssociatedKeys.emptyDataSetDelegate) as? WeakObjectContainer
            return container?.object as? TBEmptyDataSetDelegate
        }
        set {
            if let newValue = newValue {
                objc_setAssociatedObject(self, &AssociatedKeys.emptyDataSetDelegate, WeakObjectContainer(object: newValue), .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
            } else {
                handlingInvalidEmptyDataSet()
            }
        }
    }

    internal var emptyDataView: EmptyDataView? {
        get {
            return objc_getAssociatedObject(self, &AssociatedKeys.emptyDataView) as? EmptyDataView
        }
        set {
            objc_setAssociatedObject(self, &AssociatedKeys.emptyDataView, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }

    // MARK: - Public
    public var emptyDataViewVisible: Bool {
        if let emptyDataView = emptyDataView {
            return !emptyDataView.isHidden
        }
        return false
    }

    public func updateEmptyDataSetIfNeeded() {
        reloadEmptyDataSet()
    }

    // MARK: - Data source and delegate getters
    fileprivate func emptyDataSetImage() -> UIImage? {
        return emptyDataSetDataSource?.imageForEmptyDataSet(in: self)
    }

    fileprivate func emptyDataSetTitle() -> NSAttributedString? {
        return emptyDataSetDataSource?.titleForEmptyDataSet(in: self)
    }

    fileprivate func emptyDataSetDescription() -> NSAttributedString? {
        return emptyDataSetDataSource?.descriptionForEmptyDataSet(in: self)
    }

    fileprivate func emptyDataSetImageTintColor() -> UIColor? {
        return emptyDataSetDataSource?.imageTintColorForEmptyDataSet(in: self)
    }

    fileprivate func emptyDataSetBackgroundColor() -> UIColor? {
        return emptyDataSetDataSource?.backgroundColorForEmptyDataSet(in: self)
    }

    fileprivate func emptyDataSetVerticalOffset() -> CGFloat {
        return emptyDataSetDataSource?.verticalOffsetForEmptyDataSet(in: self) ?? DefaultValues.verticalOffset
    }

    fileprivate func emptyDataSetVerticalSpaces() -> [CGFloat] {
        return emptyDataSetDataSource?.verticalSpacesForEmptyDataSet(in: self) ?? DefaultValues.verticalSpaces
    }

    fileprivate func emptyDataSetTitleMargin() -> CGFloat {
        return emptyDataSetDataSource?.titleMarginForEmptyDataSet(in: self) ?? DefaultValues.titleMargin
    }

    fileprivate func emptyDataSetDescriptionMargin() -> CGFloat {
        return emptyDataSetDataSource?.descriptionMarginForEmptyDataSet(in: self) ?? DefaultValues.descriptionMargin
    }

    fileprivate func emptyDataSetCustomView() -> UIView? {
        return emptyDataSetDataSource?.customViewForEmptyDataSet(in: self)
    }

    fileprivate func emptyDataSetShouldDisplay() -> Bool {
        return emptyDataSetDelegate?.emptyDataSetShouldDisplay(in: self) ?? true
    }

    fileprivate func emptyDataSetTapEnabled() -> Bool {
        return emptyDataSetDelegate?.emptyDataSetTapEnabled(in: self) ?? true
    }

    fileprivate func emptyDataSetScrollEnabled() -> Bool {
        return emptyDataSetDelegate?.emptyDataSetScrollEnabled(in: self) ?? false
    }

    // MARK: - Helper
    @objc func didTapEmptyDataView(_ sender: Any) {
        emptyDataSetDelegate?.emptyDataSetDidTapEmptyView(in: self)
    }

    fileprivate func makeEmptyDataView() -> EmptyDataView {
        let emptyDataView = EmptyDataView(frame: frame)
        emptyDataView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
        emptyDataView.isHidden = true
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(didTapEmptyDataView(_:)))
        emptyDataView.addGestureRecognizer(tapGesture)
        emptyDataView.tapGesture = tapGesture
        return emptyDataView
    }

    fileprivate func emptyDataSetAvailable() -> Bool {
        if emptyDataSetDataSource != nil {
            return (self is UITableView) || (self is UICollectionView)
        }
        return false
    }

    fileprivate func cellsCount() -> Int {
        var count = 0
        if let tableView = self as? UITableView {
            if let dataSource = tableView.dataSource {
                if dataSource.responds(to: TableViewSelectors.numberOfSections) {
                    let sections = dataSource.numberOfSections?(in: tableView) ?? 0
                    for section in 0..<sections {
                        count += dataSource.tableView(tableView, numberOfRowsInSection: section)
                    }
                }
            }
        } else if let collectionView = self as? UICollectionView {
            if let dataSource = collectionView.dataSource {
                if dataSource.responds(to: CollectionViewSelectors.numberOfSections) {
                    let sections = dataSource.numberOfSections?(in: collectionView) ?? 0
                    for section in 0..<sections {
                        count += dataSource.collectionView(collectionView, numberOfItemsInSection: section)
                    }
                }
            }
        }

        return count
    }

    fileprivate func handlingInvalidEmptyDataSet() {
        emptyDataSetDelegate?.emptyDataSetWillDisappear(in: self)

        emptyDataView?.reset()
        emptyDataView?.removeFromSuperview()
        emptyDataView = nil
        isScrollEnabled = true

        emptyDataSetDelegate?.emptyDataSetDidDisappear(in: self)
    }

    // MARK: - Display
    fileprivate func reloadEmptyDataSet() {
        guard emptyDataSetAvailable() else {
            return
        }

        guard emptyDataSetShouldDisplay() && cellsCount() == 0 else {
            if emptyDataViewVisible {
                handlingInvalidEmptyDataSet()
            }
            return
        }

        let emptyDataView: EmptyDataView = {
            if let emptyDataView = self.emptyDataView {
                return emptyDataView
            } else {
                let emptyDataView = makeEmptyDataView()
                self.emptyDataView = emptyDataView
                return emptyDataView
            }
        }()
        emptyDataSetDelegate?.emptyDataSetWillAppear(in: self)

        if emptyDataView.superview == nil {
            if (self is UITableView || self is UICollectionView) && subviews.count > 1 {
                insertSubview(emptyDataView, at: 0)
            } else {
                addSubview(emptyDataView)
            }
        }

        emptyDataView.prepareForDisplay()

        emptyDataView.verticalOffset = emptyDataSetVerticalOffset()
        emptyDataView.verticalSpaces = emptyDataSetVerticalSpaces()
        emptyDataView.titleMargin = emptyDataSetTitleMargin()
        emptyDataView.descriptionMargin = emptyDataSetDescriptionMargin()

        if let customView = emptyDataSetCustomView() {
            emptyDataView.customView = customView
        } else {
            if let imageTintColor = emptyDataSetImageTintColor() {
                emptyDataView.imageView.image = emptyDataSetImage()?.withRenderingMode(.alwaysTemplate)
                emptyDataView.imageView.tintColor = imageTintColor
            } else {
                emptyDataView.imageView.image = emptyDataSetImage()?.withRenderingMode(.alwaysOriginal)
            }

            emptyDataView.titleLabel.attributedText = emptyDataSetTitle()
            emptyDataView.descriptionLabel.attributedText = emptyDataSetDescription()
        }

        emptyDataView.backgroundColor = emptyDataSetBackgroundColor()
        emptyDataView.isHidden = false
        emptyDataView.clipsToBounds = true
        emptyDataView.tapGesture?.isEnabled = emptyDataSetTapEnabled()
        isScrollEnabled = emptyDataSetScrollEnabled()

        emptyDataView.setConstraints()
        emptyDataView.layoutIfNeeded()

        emptyDataSetDelegate?.emptyDataSetDidAppear(in: self)
    }

    // MARK: - Method swizzling
    fileprivate class func tb_swizzleMethod(for aClass: AnyClass, originalSelector: Selector, swizzledSelector: Selector) {
        guard let originalMethod = class_getInstanceMethod(aClass, originalSelector), let swizzledMethod = class_getInstanceMethod(aClass, swizzledSelector) else {
            return
        }

        let didAddMethod = class_addMethod(aClass, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))

        if didAddMethod {
            class_replaceMethod(aClass, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod))
        } else {
            method_exchangeImplementations(originalMethod, swizzledMethod)
        }
    }

    // swiftlint:disable variable_name
    fileprivate static let tb_swizzleTableViewReloadData: () = {
        let originalSelector = TableViewSelectors.reloadData
        let swizzledSelector = Selectors.tableViewSwizzledReloadData

        tb_swizzleMethod(for: UITableView.self, originalSelector: originalSelector, swizzledSelector: swizzledSelector)
    }()

    // swiftlint:disable variable_name
    fileprivate static let tb_swizzleTableViewEndUpdates: () = {
        let originalSelector = TableViewSelectors.endUpdates
        let swizzledSelector = Selectors.tableViewSwizzledEndUpdates

        tb_swizzleMethod(for: UITableView.self, originalSelector: originalSelector, swizzledSelector: swizzledSelector)
    }()

    // swiftlint:disable variable_name
    @available(iOS 11.0, *)
    fileprivate static let tb_swizzleTableViewPerformBatchUpdates: () = {
        let originalSelector = TableViewSelectors.performBatchUpdates
        let swizzledSelector = Selectors.tableViewSwizzledPerformBatchUpdates

        tb_swizzleMethod(for: UITableView.self, originalSelector: originalSelector, swizzledSelector: swizzledSelector)
    }()

    // swiftlint:disable variable_name
    fileprivate static let tb_swizzleCollectionViewReloadData: () = {
        let originalSelector = CollectionViewSelectors.reloadData
        let swizzledSelector = Selectors.collectionViewSwizzledReloadData

        tb_swizzleMethod(for: UICollectionView.self, originalSelector: originalSelector, swizzledSelector: swizzledSelector)
    }()

    // swiftlint:disable variable_name
    fileprivate static let tb_swizzleCollectionViewPerformBatchUpdates: () = {
        let originalSelector = CollectionViewSelectors.performBatchUpdates
        let swizzledSelector = Selectors.collectionViewSwizzledPerformBatchUpdates

        tb_swizzleMethod(for: UICollectionView.self, originalSelector: originalSelector, swizzledSelector: swizzledSelector)
    }()

    @objc
    func tb_tableViewSwizzledReloadData() {
        tb_tableViewSwizzledReloadData()
        reloadEmptyDataSet()
    }

    @objc
    func tb_tableViewSwizzledEndUpdates() {
        tb_tableViewSwizzledEndUpdates()
        reloadEmptyDataSet()
    }

    @available(iOS 11.0, *)
    @objc
    func tb_tableViewSwizzledPerformBatchUpdates(_ updates: (() -> Void)?, completion: ((Bool) -> Void)?) {
        tb_tableViewSwizzledPerformBatchUpdates(updates) { [weak self](completed) in
            completion?(completed)
            self?.reloadEmptyDataSet()
        }
    }

    @objc
    func tb_collectionViewSwizzledReloadData() {
        tb_collectionViewSwizzledReloadData()
        reloadEmptyDataSet()
    }

    @objc
    func tb_collectionViewSwizzledPerformBatchUpdates(_ updates: (() -> Void)?, completion: ((Bool) -> Void)?) {
        tb_collectionViewSwizzledPerformBatchUpdates(updates) { [weak self](completed) in
            completion?(completed)
            self?.reloadEmptyDataSet()
        }
    }
}


================================================
FILE: TBEmptyDataSet/WeakObjectContainer.swift
================================================
//
//  WeakObjectContainer.swift
//  TBEmptyDataSet
//
//  Created by Xin Hong on 2016/11/4.
//  Copyright © 2016年 Teambition. All rights reserved.
//

import UIKit

class WeakObjectContainer: NSObject {
    weak var object: AnyObject?

    init(object: Any) {
        super.init()
        self.object = object as AnyObject
    }
}


================================================
FILE: TBEmptyDataSet.podspec
================================================
Pod::Spec.new do |s|

  s.name         = "TBEmptyDataSet"
  s.version      = "3.0.2"
  s.summary      = "An extension of UITableView/UICollectionView's super class, it will display a placeholder when the data is empty."

  s.homepage     = "https://github.com/teambition/TBEmptyDataSet"
  s.license      = { :type => "MIT", :file => "LICENSE.md" }
  s.author       = "Xin Hong"

  s.source       = { :git => "https://github.com/teambition/TBEmptyDataSet.git", :tag => s.version.to_s }
  s.source_files = "TBEmptyDataSet/*.swift"

  s.platform     = :ios, "8.0"
  s.requires_arc = true

  s.frameworks   = "Foundation", "UIKit"

end


================================================
FILE: TBEmptyDataSet.xcodeproj/project.pbxproj
================================================
// !$*UTF8*$!
{
	archiveVersion = 1;
	classes = {
	};
	objectVersion = 46;
	objects = {

/* Begin PBXBuildFile section */
		D331287B1DCC259C00773E72 /* WeakObjectContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D331287A1DCC259C00773E72 /* WeakObjectContainer.swift */; };
		D33C7C741C44DCA000E1687A /* TBEmptyDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = D33C7C731C44DCA000E1687A /* TBEmptyDataSet.swift */; };
		D33C7C781C44DF0A00E1687A /* EmptyDataView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D33C7C771C44DF0A00E1687A /* EmptyDataView.swift */; };
		D33C7C7A1C44E17000E1687A /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = D33C7C791C44E17000E1687A /* Constants.swift */; };
		D33C7C7C1C44E21700E1687A /* Protocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = D33C7C7B1C44E21700E1687A /* Protocols.swift */; };
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
		D331287A1DCC259C00773E72 /* WeakObjectContainer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WeakObjectContainer.swift; sourceTree = "<group>"; };
		D33C7C671C44DBED00E1687A /* TBEmptyDataSet.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = TBEmptyDataSet.framework; sourceTree = BUILT_PRODUCTS_DIR; };
		D33C7C731C44DCA000E1687A /* TBEmptyDataSet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TBEmptyDataSet.swift; sourceTree = "<group>"; };
		D33C7C751C44DCC200E1687A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
		D33C7C771C44DF0A00E1687A /* EmptyDataView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmptyDataView.swift; sourceTree = "<group>"; };
		D33C7C791C44E17000E1687A /* Constants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; };
		D33C7C7B1C44E21700E1687A /* Protocols.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Protocols.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
		D33C7C631C44DBED00E1687A /* Frameworks */ = {
			isa = PBXFrameworksBuildPhase;
			buildActionMask = 2147483647;
			files = (
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXFrameworksBuildPhase section */

/* Begin PBXGroup section */
		D33C7C5D1C44DBED00E1687A = {
			isa = PBXGroup;
			children = (
				D33C7C691C44DBED00E1687A /* TBEmptyDataSet */,
				D33C7C721C44DC8B00E1687A /* Supporting Files */,
				D33C7C681C44DBED00E1687A /* Products */,
			);
			sourceTree = "<group>";
		};
		D33C7C681C44DBED00E1687A /* Products */ = {
			isa = PBXGroup;
			children = (
				D33C7C671C44DBED00E1687A /* TBEmptyDataSet.framework */,
			);
			name = Products;
			sourceTree = "<group>";
		};
		D33C7C691C44DBED00E1687A /* TBEmptyDataSet */ = {
			isa = PBXGroup;
			children = (
				D33C7C791C44E17000E1687A /* Constants.swift */,
				D33C7C7B1C44E21700E1687A /* Protocols.swift */,
				D33C7C771C44DF0A00E1687A /* EmptyDataView.swift */,
				D33C7C731C44DCA000E1687A /* TBEmptyDataSet.swift */,
				D331287A1DCC259C00773E72 /* WeakObjectContainer.swift */,
			);
			path = TBEmptyDataSet;
			sourceTree = "<group>";
		};
		D33C7C721C44DC8B00E1687A /* Supporting Files */ = {
			isa = PBXGroup;
			children = (
				D33C7C751C44DCC200E1687A /* Info.plist */,
			);
			name = "Supporting Files";
			path = "TBEmptyDataSet/Supporting Files";
			sourceTree = "<group>";
		};
/* End PBXGroup section */

/* Begin PBXHeadersBuildPhase section */
		D33C7C641C44DBED00E1687A /* Headers */ = {
			isa = PBXHeadersBuildPhase;
			buildActionMask = 2147483647;
			files = (
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXHeadersBuildPhase section */

/* Begin PBXNativeTarget section */
		D33C7C661C44DBED00E1687A /* TBEmptyDataSet */ = {
			isa = PBXNativeTarget;
			buildConfigurationList = D33C7C6F1C44DBED00E1687A /* Build configuration list for PBXNativeTarget "TBEmptyDataSet" */;
			buildPhases = (
				D33C7C621C44DBED00E1687A /* Sources */,
				D33C7C631C44DBED00E1687A /* Frameworks */,
				D33C7C641C44DBED00E1687A /* Headers */,
				D33C7C651C44DBED00E1687A /* Resources */,
			);
			buildRules = (
			);
			dependencies = (
			);
			name = TBEmptyDataSet;
			productName = TBEmptyDataSet;
			productReference = D33C7C671C44DBED00E1687A /* TBEmptyDataSet.framework */;
			productType = "com.apple.product-type.framework";
		};
/* End PBXNativeTarget section */

/* Begin PBXProject section */
		D33C7C5E1C44DBED00E1687A /* Project object */ = {
			isa = PBXProject;
			attributes = {
				LastUpgradeCheck = 1020;
				ORGANIZATIONNAME = Teambition;
				TargetAttributes = {
					D33C7C661C44DBED00E1687A = {
						CreatedOnToolsVersion = 7.2;
						LastSwiftMigration = 1020;
					};
				};
			};
			buildConfigurationList = D33C7C611C44DBED00E1687A /* Build configuration list for PBXProject "TBEmptyDataSet" */;
			compatibilityVersion = "Xcode 3.2";
			developmentRegion = en;
			hasScannedForEncodings = 0;
			knownRegions = (
				en,
				Base,
			);
			mainGroup = D33C7C5D1C44DBED00E1687A;
			productRefGroup = D33C7C681C44DBED00E1687A /* Products */;
			projectDirPath = "";
			projectRoot = "";
			targets = (
				D33C7C661C44DBED00E1687A /* TBEmptyDataSet */,
			);
		};
/* End PBXProject section */

/* Begin PBXResourcesBuildPhase section */
		D33C7C651C44DBED00E1687A /* Resources */ = {
			isa = PBXResourcesBuildPhase;
			buildActionMask = 2147483647;
			files = (
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXResourcesBuildPhase section */

/* Begin PBXSourcesBuildPhase section */
		D33C7C621C44DBED00E1687A /* Sources */ = {
			isa = PBXSourcesBuildPhase;
			buildActionMask = 2147483647;
			files = (
				D33C7C781C44DF0A00E1687A /* EmptyDataView.swift in Sources */,
				D331287B1DCC259C00773E72 /* WeakObjectContainer.swift in Sources */,
				D33C7C7C1C44E21700E1687A /* Protocols.swift in Sources */,
				D33C7C741C44DCA000E1687A /* TBEmptyDataSet.swift in Sources */,
				D33C7C7A1C44E17000E1687A /* Constants.swift in Sources */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXSourcesBuildPhase section */

/* Begin XCBuildConfiguration section */
		D33C7C6D1C44DBED00E1687A /* Debug */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				ALWAYS_SEARCH_USER_PATHS = NO;
				CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
				CLANG_CXX_LIBRARY = "libc++";
				CLANG_ENABLE_MODULES = YES;
				CLANG_ENABLE_OBJC_ARC = YES;
				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
				CLANG_WARN_BOOL_CONVERSION = YES;
				CLANG_WARN_COMMA = YES;
				CLANG_WARN_CONSTANT_CONVERSION = YES;
				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
				CLANG_WARN_EMPTY_BODY = YES;
				CLANG_WARN_ENUM_CONVERSION = YES;
				CLANG_WARN_INFINITE_RECURSION = YES;
				CLANG_WARN_INT_CONVERSION = YES;
				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
				CLANG_WARN_STRICT_PROTOTYPES = YES;
				CLANG_WARN_SUSPICIOUS_MOVE = YES;
				CLANG_WARN_UNREACHABLE_CODE = YES;
				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
				COPY_PHASE_STRIP = NO;
				CURRENT_PROJECT_VERSION = 1;
				DEBUG_INFORMATION_FORMAT = dwarf;
				ENABLE_STRICT_OBJC_MSGSEND = YES;
				ENABLE_TESTABILITY = YES;
				GCC_C_LANGUAGE_STANDARD = gnu99;
				GCC_DYNAMIC_NO_PIC = NO;
				GCC_NO_COMMON_BLOCKS = YES;
				GCC_OPTIMIZATION_LEVEL = 0;
				GCC_PREPROCESSOR_DEFINITIONS = (
					"DEBUG=1",
					"$(inherited)",
				);
				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
				GCC_WARN_UNDECLARED_SELECTOR = YES;
				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
				GCC_WARN_UNUSED_FUNCTION = YES;
				GCC_WARN_UNUSED_VARIABLE = YES;
				IPHONEOS_DEPLOYMENT_TARGET = 8.0;
				MTL_ENABLE_DEBUG_INFO = YES;
				ONLY_ACTIVE_ARCH = YES;
				SDKROOT = iphoneos;
				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
				TARGETED_DEVICE_FAMILY = "1,2";
				VERSIONING_SYSTEM = "apple-generic";
				VERSION_INFO_PREFIX = "";
			};
			name = Debug;
		};
		D33C7C6E1C44DBED00E1687A /* Release */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				ALWAYS_SEARCH_USER_PATHS = NO;
				CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
				CLANG_CXX_LIBRARY = "libc++";
				CLANG_ENABLE_MODULES = YES;
				CLANG_ENABLE_OBJC_ARC = YES;
				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
				CLANG_WARN_BOOL_CONVERSION = YES;
				CLANG_WARN_COMMA = YES;
				CLANG_WARN_CONSTANT_CONVERSION = YES;
				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
				CLANG_WARN_EMPTY_BODY = YES;
				CLANG_WARN_ENUM_CONVERSION = YES;
				CLANG_WARN_INFINITE_RECURSION = YES;
				CLANG_WARN_INT_CONVERSION = YES;
				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
				CLANG_WARN_STRICT_PROTOTYPES = YES;
				CLANG_WARN_SUSPICIOUS_MOVE = YES;
				CLANG_WARN_UNREACHABLE_CODE = YES;
				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
				COPY_PHASE_STRIP = NO;
				CURRENT_PROJECT_VERSION = 1;
				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
				ENABLE_NS_ASSERTIONS = NO;
				ENABLE_STRICT_OBJC_MSGSEND = YES;
				GCC_C_LANGUAGE_STANDARD = gnu99;
				GCC_NO_COMMON_BLOCKS = YES;
				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
				GCC_WARN_UNDECLARED_SELECTOR = YES;
				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
				GCC_WARN_UNUSED_FUNCTION = YES;
				GCC_WARN_UNUSED_VARIABLE = YES;
				IPHONEOS_DEPLOYMENT_TARGET = 8.0;
				MTL_ENABLE_DEBUG_INFO = NO;
				SDKROOT = iphoneos;
				SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
				TARGETED_DEVICE_FAMILY = "1,2";
				VALIDATE_PRODUCT = YES;
				VERSIONING_SYSTEM = "apple-generic";
				VERSION_INFO_PREFIX = "";
			};
			name = Release;
		};
		D33C7C701C44DBED00E1687A /* Debug */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				CLANG_ENABLE_MODULES = YES;
				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
				DEFINES_MODULE = YES;
				DYLIB_COMPATIBILITY_VERSION = 1;
				DYLIB_CURRENT_VERSION = 1;
				DYLIB_INSTALL_NAME_BASE = "@rpath";
				INFOPLIST_FILE = "$(SRCROOT)/TBEmptyDataSet/Supporting Files/Info.plist";
				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
				IPHONEOS_DEPLOYMENT_TARGET = 8.0;
				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
				PRODUCT_BUNDLE_IDENTIFIER = Teambition.TBEmptyDataSet;
				PRODUCT_NAME = "$(TARGET_NAME)";
				SKIP_INSTALL = YES;
				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
				SWIFT_VERSION = 5.0;
			};
			name = Debug;
		};
		D33C7C711C44DBED00E1687A /* Release */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				CLANG_ENABLE_MODULES = YES;
				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
				DEFINES_MODULE = YES;
				DYLIB_COMPATIBILITY_VERSION = 1;
				DYLIB_CURRENT_VERSION = 1;
				DYLIB_INSTALL_NAME_BASE = "@rpath";
				INFOPLIST_FILE = "$(SRCROOT)/TBEmptyDataSet/Supporting Files/Info.plist";
				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
				IPHONEOS_DEPLOYMENT_TARGET = 8.0;
				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
				PRODUCT_BUNDLE_IDENTIFIER = Teambition.TBEmptyDataSet;
				PRODUCT_NAME = "$(TARGET_NAME)";
				SKIP_INSTALL = YES;
				SWIFT_VERSION = 5.0;
			};
			name = Release;
		};
/* End XCBuildConfiguration section */

/* Begin XCConfigurationList section */
		D33C7C611C44DBED00E1687A /* Build configuration list for PBXProject "TBEmptyDataSet" */ = {
			isa = XCConfigurationList;
			buildConfigurations = (
				D33C7C6D1C44DBED00E1687A /* Debug */,
				D33C7C6E1C44DBED00E1687A /* Release */,
			);
			defaultConfigurationIsVisible = 0;
			defaultConfigurationName = Release;
		};
		D33C7C6F1C44DBED00E1687A /* Build configuration list for PBXNativeTarget "TBEmptyDataSet" */ = {
			isa = XCConfigurationList;
			buildConfigurations = (
				D33C7C701C44DBED00E1687A /* Debug */,
				D33C7C711C44DBED00E1687A /* Release */,
			);
			defaultConfigurationIsVisible = 0;
			defaultConfigurationName = Release;
		};
/* End XCConfigurationList section */
	};
	rootObject = D33C7C5E1C44DBED00E1687A /* Project object */;
}


================================================
FILE: TBEmptyDataSet.xcodeproj/xcshareddata/xcschemes/TBEmptyDataSet.xcscheme
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
   LastUpgradeVersion = "1020"
   version = "1.3">
   <BuildAction
      parallelizeBuildables = "YES"
      buildImplicitDependencies = "YES">
      <BuildActionEntries>
         <BuildActionEntry
            buildForTesting = "YES"
            buildForRunning = "YES"
            buildForProfiling = "YES"
            buildForArchiving = "YES"
            buildForAnalyzing = "YES">
            <BuildableReference
               BuildableIdentifier = "primary"
               BlueprintIdentifier = "D33C7C661C44DBED00E1687A"
               BuildableName = "TBEmptyDataSet.framework"
               BlueprintName = "TBEmptyDataSet"
               ReferencedContainer = "container:TBEmptyDataSet.xcodeproj">
            </BuildableReference>
         </BuildActionEntry>
      </BuildActionEntries>
   </BuildAction>
   <TestAction
      buildConfiguration = "Debug"
      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
      shouldUseLaunchSchemeArgsEnv = "YES">
      <Testables>
      </Testables>
      <AdditionalOptions>
      </AdditionalOptions>
   </TestAction>
   <LaunchAction
      buildConfiguration = "Debug"
      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
      launchStyle = "0"
      useCustomWorkingDirectory = "NO"
      ignoresPersistentStateOnLaunch = "NO"
      debugDocumentVersioning = "YES"
      debugServiceExtension = "internal"
      allowLocationSimulation = "YES">
      <MacroExpansion>
         <BuildableReference
            BuildableIdentifier = "primary"
            BlueprintIdentifier = "D33C7C661C44DBED00E1687A"
            BuildableName = "TBEmptyDataSet.framework"
            BlueprintName = "TBEmptyDataSet"
            ReferencedContainer = "container:TBEmptyDataSet.xcodeproj">
         </BuildableReference>
      </MacroExpansion>
      <AdditionalOptions>
      </AdditionalOptions>
   </LaunchAction>
   <ProfileAction
      buildConfiguration = "Release"
      shouldUseLaunchSchemeArgsEnv = "YES"
      savedToolIdentifier = ""
      useCustomWorkingDirectory = "NO"
      debugDocumentVersioning = "YES">
      <MacroExpansion>
         <BuildableReference
            BuildableIdentifier = "primary"
            BlueprintIdentifier = "D33C7C661C44DBED00E1687A"
            BuildableName = "TBEmptyDataSet.framework"
            BlueprintName = "TBEmptyDataSet"
            ReferencedContainer = "container:TBEmptyDataSet.xcodeproj">
         </BuildableReference>
      </MacroExpansion>
   </ProfileAction>
   <AnalyzeAction
      buildConfiguration = "Debug">
   </AnalyzeAction>
   <ArchiveAction
      buildConfiguration = "Release"
      revealArchiveInOrganizer = "YES">
   </ArchiveAction>
</Scheme>


================================================
FILE: TBEmptyDataSet.xcodeproj/xcuserdata/hongxin.xcuserdatad/xcschemes/xcschememanagement.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>SchemeUserState</key>
	<dict>
		<key>TBEmptyDataSet.xcscheme_^#shared#^_</key>
		<dict>
			<key>orderHint</key>
			<integer>0</integer>
		</dict>
	</dict>
	<key>SuppressBuildableAutocreation</key>
	<dict>
		<key>D33C7C661C44DBED00E1687A</key>
		<dict>
			<key>primary</key>
			<true/>
		</dict>
	</dict>
</dict>
</plist>


================================================
FILE: TBEmptyDataSet.xcodeproj/xcuserdata/zetasq.xcuserdatad/xcschemes/xcschememanagement.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>SchemeUserState</key>
	<dict>
		<key>TBEmptyDataSet.xcscheme_^#shared#^_</key>
		<dict>
			<key>orderHint</key>
			<integer>0</integer>
		</dict>
	</dict>
	<key>SuppressBuildableAutocreation</key>
	<dict>
		<key>D33C7C661C44DBED00E1687A</key>
		<dict>
			<key>primary</key>
			<true/>
		</dict>
	</dict>
</dict>
</plist>


================================================
FILE: TBEmptyDataSet.xcworkspace/contents.xcworkspacedata
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
   version = "1.0">
   <FileRef
      location = "group:TBEmptyDataSet.xcodeproj">
   </FileRef>
   <FileRef
      location = "group:TBEmptyDataSetExample.xcodeproj">
   </FileRef>
</Workspace>


================================================
FILE: TBEmptyDataSet.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>IDEDidComputeMac32BitWarning</key>
	<true/>
</dict>
</plist>


================================================
FILE: TBEmptyDataSetExample.xcodeproj/project.pbxproj
================================================
// !$*UTF8*$!
{
	archiveVersion = 1;
	classes = {
	};
	objectVersion = 46;
	objects = {

/* Begin PBXBuildFile section */
		D3013DFB1C06B7FA00266EE2 /* DemoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3013DFA1C06B7FA00266EE2 /* DemoViewController.swift */; };
		D33C7C801C44E9A800E1687A /* TBEmptyDataSet.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D33C7C7F1C44E9A800E1687A /* TBEmptyDataSet.framework */; };
		D33C7C811C44E9A800E1687A /* TBEmptyDataSet.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D33C7C7F1C44E9A800E1687A /* TBEmptyDataSet.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
		D3565AA31BFD9449002CFA16 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3565AA21BFD9449002CFA16 /* AppDelegate.swift */; };
		D3565AA81BFD9449002CFA16 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D3565AA61BFD9449002CFA16 /* Main.storyboard */; };
		D3565AAA1BFD9449002CFA16 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D3565AA91BFD9449002CFA16 /* Assets.xcassets */; };
		D39EBFCA1C040C040089B394 /* EmptyDataDemoTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D39EBFC91C040C040089B394 /* EmptyDataDemoTableViewController.swift */; };
		D39EBFCC1C040F220089B394 /* EmptyDataDemoCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D39EBFCB1C040F220089B394 /* EmptyDataDemoCollectionViewController.swift */; };
/* End PBXBuildFile section */

/* Begin PBXCopyFilesBuildPhase section */
		D33C7C821C44E9A800E1687A /* Embed Frameworks */ = {
			isa = PBXCopyFilesBuildPhase;
			buildActionMask = 2147483647;
			dstPath = "";
			dstSubfolderSpec = 10;
			files = (
				D33C7C811C44E9A800E1687A /* TBEmptyDataSet.framework in Embed Frameworks */,
			);
			name = "Embed Frameworks";
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXCopyFilesBuildPhase section */

/* Begin PBXFileReference section */
		D3013DFA1C06B7FA00266EE2 /* DemoViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DemoViewController.swift; sourceTree = "<group>"; };
		D33C7C7F1C44E9A800E1687A /* TBEmptyDataSet.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; name = TBEmptyDataSet.framework; path = "/Users/hongxin/Library/Developer/Xcode/DerivedData/TBEmptyDataSet-cgdivxseqtlgnzctplvlotcjtyby/Build/Products/Debug-iphoneos/TBEmptyDataSet.framework"; sourceTree = "<absolute>"; };
		D3565A9F1BFD9449002CFA16 /* TBEmptyDataSetExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TBEmptyDataSetExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
		D3565AA21BFD9449002CFA16 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
		D3565AA71BFD9449002CFA16 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
		D3565AA91BFD9449002CFA16 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
		D3565AAE1BFD9449002CFA16 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
		D39EBFC91C040C040089B394 /* EmptyDataDemoTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmptyDataDemoTableViewController.swift; sourceTree = "<group>"; };
		D39EBFCB1C040F220089B394 /* EmptyDataDemoCollectionViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmptyDataDemoCollectionViewController.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
		D3565A9C1BFD9449002CFA16 /* Frameworks */ = {
			isa = PBXFrameworksBuildPhase;
			buildActionMask = 2147483647;
			files = (
				D33C7C801C44E9A800E1687A /* TBEmptyDataSet.framework in Frameworks */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXFrameworksBuildPhase section */

/* Begin PBXGroup section */
		D3565A961BFD9449002CFA16 = {
			isa = PBXGroup;
			children = (
				D33C7C7F1C44E9A800E1687A /* TBEmptyDataSet.framework */,
				D3565AA11BFD9449002CFA16 /* Example */,
				D3565AA01BFD9449002CFA16 /* Products */,
			);
			sourceTree = "<group>";
		};
		D3565AA01BFD9449002CFA16 /* Products */ = {
			isa = PBXGroup;
			children = (
				D3565A9F1BFD9449002CFA16 /* TBEmptyDataSetExample.app */,
			);
			name = Products;
			sourceTree = "<group>";
		};
		D3565AA11BFD9449002CFA16 /* Example */ = {
			isa = PBXGroup;
			children = (
				D3565AA21BFD9449002CFA16 /* AppDelegate.swift */,
				D3013DFA1C06B7FA00266EE2 /* DemoViewController.swift */,
				D39EBFC91C040C040089B394 /* EmptyDataDemoTableViewController.swift */,
				D39EBFCB1C040F220089B394 /* EmptyDataDemoCollectionViewController.swift */,
				D3565AA61BFD9449002CFA16 /* Main.storyboard */,
				D3565AA91BFD9449002CFA16 /* Assets.xcassets */,
				D3565AAE1BFD9449002CFA16 /* Info.plist */,
			);
			path = Example;
			sourceTree = "<group>";
		};
/* End PBXGroup section */

/* Begin PBXNativeTarget section */
		D3565A9E1BFD9449002CFA16 /* TBEmptyDataSetExample */ = {
			isa = PBXNativeTarget;
			buildConfigurationList = D3565AB11BFD9449002CFA16 /* Build configuration list for PBXNativeTarget "TBEmptyDataSetExample" */;
			buildPhases = (
				D3565A9B1BFD9449002CFA16 /* Sources */,
				D3565A9C1BFD9449002CFA16 /* Frameworks */,
				D3565A9D1BFD9449002CFA16 /* Resources */,
				D3013DFC1C06E0D200266EE2 /* ShellScript */,
				D33C7C821C44E9A800E1687A /* Embed Frameworks */,
			);
			buildRules = (
			);
			dependencies = (
			);
			name = TBEmptyDataSetExample;
			productName = TBEmptyDataSet;
			productReference = D3565A9F1BFD9449002CFA16 /* TBEmptyDataSetExample.app */;
			productType = "com.apple.product-type.application";
		};
/* End PBXNativeTarget section */

/* Begin PBXProject section */
		D3565A971BFD9449002CFA16 /* Project object */ = {
			isa = PBXProject;
			attributes = {
				LastSwiftUpdateCheck = 0710;
				LastUpgradeCheck = 1020;
				ORGANIZATIONNAME = Teambition;
				TargetAttributes = {
					D3565A9E1BFD9449002CFA16 = {
						CreatedOnToolsVersion = 7.1.1;
						LastSwiftMigration = 1020;
					};
				};
			};
			buildConfigurationList = D3565A9A1BFD9449002CFA16 /* Build configuration list for PBXProject "TBEmptyDataSetExample" */;
			compatibilityVersion = "Xcode 3.2";
			developmentRegion = en;
			hasScannedForEncodings = 0;
			knownRegions = (
				en,
				Base,
			);
			mainGroup = D3565A961BFD9449002CFA16;
			productRefGroup = D3565AA01BFD9449002CFA16 /* Products */;
			projectDirPath = "";
			projectRoot = "";
			targets = (
				D3565A9E1BFD9449002CFA16 /* TBEmptyDataSetExample */,
			);
		};
/* End PBXProject section */

/* Begin PBXResourcesBuildPhase section */
		D3565A9D1BFD9449002CFA16 /* Resources */ = {
			isa = PBXResourcesBuildPhase;
			buildActionMask = 2147483647;
			files = (
				D3565AAA1BFD9449002CFA16 /* Assets.xcassets in Resources */,
				D3565AA81BFD9449002CFA16 /* Main.storyboard in Resources */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXResourcesBuildPhase section */

/* Begin PBXShellScriptBuildPhase section */
		D3013DFC1C06E0D200266EE2 /* ShellScript */ = {
			isa = PBXShellScriptBuildPhase;
			buildActionMask = 2147483647;
			files = (
			);
			inputPaths = (
			);
			outputPaths = (
			);
			runOnlyForDeploymentPostprocessing = 0;
			shellPath = /bin/sh;
			shellScript = "if which swiftlint >/dev/null; then\n    swiftlint\nelse\n    echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n";
		};
/* End PBXShellScriptBuildPhase section */

/* Begin PBXSourcesBuildPhase section */
		D3565A9B1BFD9449002CFA16 /* Sources */ = {
			isa = PBXSourcesBuildPhase;
			buildActionMask = 2147483647;
			files = (
				D3565AA31BFD9449002CFA16 /* AppDelegate.swift in Sources */,
				D39EBFCA1C040C040089B394 /* EmptyDataDemoTableViewController.swift in Sources */,
				D39EBFCC1C040F220089B394 /* EmptyDataDemoCollectionViewController.swift in Sources */,
				D3013DFB1C06B7FA00266EE2 /* DemoViewController.swift in Sources */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXSourcesBuildPhase section */

/* Begin PBXVariantGroup section */
		D3565AA61BFD9449002CFA16 /* Main.storyboard */ = {
			isa = PBXVariantGroup;
			children = (
				D3565AA71BFD9449002CFA16 /* Base */,
			);
			name = Main.storyboard;
			sourceTree = "<group>";
		};
/* End PBXVariantGroup section */

/* Begin XCBuildConfiguration section */
		D3565AAF1BFD9449002CFA16 /* Debug */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				ALWAYS_SEARCH_USER_PATHS = NO;
				CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
				CLANG_CXX_LIBRARY = "libc++";
				CLANG_ENABLE_MODULES = YES;
				CLANG_ENABLE_OBJC_ARC = YES;
				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
				CLANG_WARN_BOOL_CONVERSION = YES;
				CLANG_WARN_COMMA = YES;
				CLANG_WARN_CONSTANT_CONVERSION = YES;
				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
				CLANG_WARN_EMPTY_BODY = YES;
				CLANG_WARN_ENUM_CONVERSION = YES;
				CLANG_WARN_INFINITE_RECURSION = YES;
				CLANG_WARN_INT_CONVERSION = YES;
				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
				CLANG_WARN_STRICT_PROTOTYPES = YES;
				CLANG_WARN_SUSPICIOUS_MOVE = YES;
				CLANG_WARN_UNREACHABLE_CODE = YES;
				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
				COPY_PHASE_STRIP = NO;
				DEBUG_INFORMATION_FORMAT = dwarf;
				ENABLE_STRICT_OBJC_MSGSEND = YES;
				ENABLE_TESTABILITY = YES;
				GCC_C_LANGUAGE_STANDARD = gnu99;
				GCC_DYNAMIC_NO_PIC = NO;
				GCC_NO_COMMON_BLOCKS = YES;
				GCC_OPTIMIZATION_LEVEL = 0;
				GCC_PREPROCESSOR_DEFINITIONS = (
					"DEBUG=1",
					"$(inherited)",
				);
				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
				GCC_WARN_UNDECLARED_SELECTOR = YES;
				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
				GCC_WARN_UNUSED_FUNCTION = YES;
				GCC_WARN_UNUSED_VARIABLE = YES;
				IPHONEOS_DEPLOYMENT_TARGET = 8.0;
				MTL_ENABLE_DEBUG_INFO = YES;
				ONLY_ACTIVE_ARCH = YES;
				SDKROOT = iphoneos;
				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
				TARGETED_DEVICE_FAMILY = "1,2";
			};
			name = Debug;
		};
		D3565AB01BFD9449002CFA16 /* Release */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				ALWAYS_SEARCH_USER_PATHS = NO;
				CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
				CLANG_CXX_LIBRARY = "libc++";
				CLANG_ENABLE_MODULES = YES;
				CLANG_ENABLE_OBJC_ARC = YES;
				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
				CLANG_WARN_BOOL_CONVERSION = YES;
				CLANG_WARN_COMMA = YES;
				CLANG_WARN_CONSTANT_CONVERSION = YES;
				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
				CLANG_WARN_EMPTY_BODY = YES;
				CLANG_WARN_ENUM_CONVERSION = YES;
				CLANG_WARN_INFINITE_RECURSION = YES;
				CLANG_WARN_INT_CONVERSION = YES;
				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
				CLANG_WARN_STRICT_PROTOTYPES = YES;
				CLANG_WARN_SUSPICIOUS_MOVE = YES;
				CLANG_WARN_UNREACHABLE_CODE = YES;
				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
				COPY_PHASE_STRIP = NO;
				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
				ENABLE_NS_ASSERTIONS = NO;
				ENABLE_STRICT_OBJC_MSGSEND = YES;
				GCC_C_LANGUAGE_STANDARD = gnu99;
				GCC_NO_COMMON_BLOCKS = YES;
				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
				GCC_WARN_UNDECLARED_SELECTOR = YES;
				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
				GCC_WARN_UNUSED_FUNCTION = YES;
				GCC_WARN_UNUSED_VARIABLE = YES;
				IPHONEOS_DEPLOYMENT_TARGET = 8.0;
				MTL_ENABLE_DEBUG_INFO = NO;
				SDKROOT = iphoneos;
				SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
				TARGETED_DEVICE_FAMILY = "1,2";
				VALIDATE_PRODUCT = YES;
			};
			name = Release;
		};
		D3565AB21BFD9449002CFA16 /* Debug */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
				INFOPLIST_FILE = "$(SRCROOT)/Example/Info.plist";
				IPHONEOS_DEPLOYMENT_TARGET = 8.0;
				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
				PRODUCT_BUNDLE_IDENTIFIER = Teambition.TBEmptyDataSetExample;
				PRODUCT_NAME = TBEmptyDataSetExample;
				SWIFT_VERSION = 5.0;
			};
			name = Debug;
		};
		D3565AB31BFD9449002CFA16 /* Release */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
				INFOPLIST_FILE = "$(SRCROOT)/Example/Info.plist";
				IPHONEOS_DEPLOYMENT_TARGET = 8.0;
				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
				PRODUCT_BUNDLE_IDENTIFIER = Teambition.TBEmptyDataSetExample;
				PRODUCT_NAME = TBEmptyDataSetExample;
				SWIFT_VERSION = 5.0;
			};
			name = Release;
		};
/* End XCBuildConfiguration section */

/* Begin XCConfigurationList section */
		D3565A9A1BFD9449002CFA16 /* Build configuration list for PBXProject "TBEmptyDataSetExample" */ = {
			isa = XCConfigurationList;
			buildConfigurations = (
				D3565AAF1BFD9449002CFA16 /* Debug */,
				D3565AB01BFD9449002CFA16 /* Release */,
			);
			defaultConfigurationIsVisible = 0;
			defaultConfigurationName = Release;
		};
		D3565AB11BFD9449002CFA16 /* Build configuration list for PBXNativeTarget "TBEmptyDataSetExample" */ = {
			isa = XCConfigurationList;
			buildConfigurations = (
				D3565AB21BFD9449002CFA16 /* Debug */,
				D3565AB31BFD9449002CFA16 /* Release */,
			);
			defaultConfigurationIsVisible = 0;
			defaultConfigurationName = Release;
		};
/* End XCConfigurationList section */
	};
	rootObject = D3565A971BFD9449002CFA16 /* Project object */;
}


================================================
FILE: TBEmptyDataSetExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
   version = "1.0">
   <FileRef
      location = "self:/Users/hongxin/Developer/TBEmptyDataSet/Example/TBEmptyDataSetExample.xcodeproj">
   </FileRef>
</Workspace>


================================================
FILE: TBEmptyDataSetExample.xcodeproj/xcuserdata/hongxin.xcuserdatad/xcschemes/TBEmptyDataSetExample.xcscheme
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
   LastUpgradeVersion = "1020"
   version = "1.3">
   <BuildAction
      parallelizeBuildables = "YES"
      buildImplicitDependencies = "YES">
      <BuildActionEntries>
         <BuildActionEntry
            buildForTesting = "YES"
            buildForRunning = "YES"
            buildForProfiling = "YES"
            buildForArchiving = "YES"
            buildForAnalyzing = "YES">
            <BuildableReference
               BuildableIdentifier = "primary"
               BlueprintIdentifier = "D3565A9E1BFD9449002CFA16"
               BuildableName = "TBEmptyDataSetExample.app"
               BlueprintName = "TBEmptyDataSetExample"
               ReferencedContainer = "container:TBEmptyDataSetExample.xcodeproj">
            </BuildableReference>
         </BuildActionEntry>
      </BuildActionEntries>
   </BuildAction>
   <TestAction
      buildConfiguration = "Debug"
      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
      shouldUseLaunchSchemeArgsEnv = "YES">
      <Testables>
      </Testables>
      <MacroExpansion>
         <BuildableReference
            BuildableIdentifier = "primary"
            BlueprintIdentifier = "D3565A9E1BFD9449002CFA16"
            BuildableName = "TBEmptyDataSetExample.app"
            BlueprintName = "TBEmptyDataSetExample"
            ReferencedContainer = "container:TBEmptyDataSetExample.xcodeproj">
         </BuildableReference>
      </MacroExpansion>
      <AdditionalOptions>
      </AdditionalOptions>
   </TestAction>
   <LaunchAction
      buildConfiguration = "Debug"
      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
      launchStyle = "0"
      useCustomWorkingDirectory = "NO"
      ignoresPersistentStateOnLaunch = "NO"
      debugDocumentVersioning = "YES"
      debugServiceExtension = "internal"
      allowLocationSimulation = "YES">
      <BuildableProductRunnable
         runnableDebuggingMode = "0">
         <BuildableReference
            BuildableIdentifier = "primary"
            BlueprintIdentifier = "D3565A9E1BFD9449002CFA16"
            BuildableName = "TBEmptyDataSetExample.app"
            BlueprintName = "TBEmptyDataSetExample"
            ReferencedContainer = "container:TBEmptyDataSetExample.xcodeproj">
         </BuildableReference>
      </BuildableProductRunnable>
      <AdditionalOptions>
      </AdditionalOptions>
   </LaunchAction>
   <ProfileAction
      buildConfiguration = "Release"
      shouldUseLaunchSchemeArgsEnv = "YES"
      savedToolIdentifier = ""
      useCustomWorkingDirectory = "NO"
      debugDocumentVersioning = "YES">
      <BuildableProductRunnable
         runnableDebuggingMode = "0">
         <BuildableReference
            BuildableIdentifier = "primary"
            BlueprintIdentifier = "D3565A9E1BFD9449002CFA16"
            BuildableName = "TBEmptyDataSetExample.app"
            BlueprintName = "TBEmptyDataSetExample"
            ReferencedContainer = "container:TBEmptyDataSetExample.xcodeproj">
         </BuildableReference>
      </BuildableProductRunnable>
   </ProfileAction>
   <AnalyzeAction
      buildConfiguration = "Debug">
   </AnalyzeAction>
   <ArchiveAction
      buildConfiguration = "Release"
      revealArchiveInOrganizer = "YES">
   </ArchiveAction>
</Scheme>


================================================
FILE: TBEmptyDataSetExample.xcodeproj/xcuserdata/hongxin.xcuserdatad/xcschemes/xcschememanagement.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>SchemeUserState</key>
	<dict>
		<key>TBEmptyDataSetExample.xcscheme</key>
		<dict>
			<key>orderHint</key>
			<integer>0</integer>
		</dict>
	</dict>
	<key>SuppressBuildableAutocreation</key>
	<dict>
		<key>D3565A9E1BFD9449002CFA16</key>
		<dict>
			<key>primary</key>
			<true/>
		</dict>
	</dict>
</dict>
</plist>


================================================
FILE: TBEmptyDataSetExample.xcodeproj/xcuserdata/zetasq.xcuserdatad/xcschemes/TBEmptyDataSetExample.xcscheme
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
   LastUpgradeVersion = "0730"
   version = "1.3">
   <BuildAction
      parallelizeBuildables = "YES"
      buildImplicitDependencies = "YES">
      <BuildActionEntries>
         <BuildActionEntry
            buildForTesting = "YES"
            buildForRunning = "YES"
            buildForProfiling = "YES"
            buildForArchiving = "YES"
            buildForAnalyzing = "YES">
            <BuildableReference
               BuildableIdentifier = "primary"
               BlueprintIdentifier = "D3565A9E1BFD9449002CFA16"
               BuildableName = "TBEmptyDataSetExample.app"
               BlueprintName = "TBEmptyDataSetExample"
               ReferencedContainer = "container:TBEmptyDataSetExample.xcodeproj">
            </BuildableReference>
         </BuildActionEntry>
      </BuildActionEntries>
   </BuildAction>
   <TestAction
      buildConfiguration = "Debug"
      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
      shouldUseLaunchSchemeArgsEnv = "YES">
      <Testables>
      </Testables>
      <MacroExpansion>
         <BuildableReference
            BuildableIdentifier = "primary"
            BlueprintIdentifier = "D3565A9E1BFD9449002CFA16"
            BuildableName = "TBEmptyDataSetExample.app"
            BlueprintName = "TBEmptyDataSetExample"
            ReferencedContainer = "container:TBEmptyDataSetExample.xcodeproj">
         </BuildableReference>
      </MacroExpansion>
      <AdditionalOptions>
      </AdditionalOptions>
   </TestAction>
   <LaunchAction
      buildConfiguration = "Debug"
      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
      launchStyle = "0"
      useCustomWorkingDirectory = "NO"
      ignoresPersistentStateOnLaunch = "NO"
      debugDocumentVersioning = "YES"
      debugServiceExtension = "internal"
      allowLocationSimulation = "YES">
      <BuildableProductRunnable
         runnableDebuggingMode = "0">
         <BuildableReference
            BuildableIdentifier = "primary"
            BlueprintIdentifier = "D3565A9E1BFD9449002CFA16"
            BuildableName = "TBEmptyDataSetExample.app"
            BlueprintName = "TBEmptyDataSetExample"
            ReferencedContainer = "container:TBEmptyDataSetExample.xcodeproj">
         </BuildableReference>
      </BuildableProductRunnable>
      <AdditionalOptions>
      </AdditionalOptions>
   </LaunchAction>
   <ProfileAction
      buildConfiguration = "Release"
      shouldUseLaunchSchemeArgsEnv = "YES"
      savedToolIdentifier = ""
      useCustomWorkingDirectory = "NO"
      debugDocumentVersioning = "YES">
      <BuildableProductRunnable
         runnableDebuggingMode = "0">
         <BuildableReference
            BuildableIdentifier = "primary"
            BlueprintIdentifier = "D3565A9E1BFD9449002CFA16"
            BuildableName = "TBEmptyDataSetExample.app"
            BlueprintName = "TBEmptyDataSetExample"
            ReferencedContainer = "container:TBEmptyDataSetExample.xcodeproj">
         </BuildableReference>
      </BuildableProductRunnable>
   </ProfileAction>
   <AnalyzeAction
      buildConfiguration = "Debug">
   </AnalyzeAction>
   <ArchiveAction
      buildConfiguration = "Release"
      revealArchiveInOrganizer = "YES">
   </ArchiveAction>
</Scheme>


================================================
FILE: TBEmptyDataSetExample.xcodeproj/xcuserdata/zetasq.xcuserdatad/xcschemes/xcschememanagement.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>SchemeUserState</key>
	<dict>
		<key>TBEmptyDataSetExample.xcscheme</key>
		<dict>
			<key>orderHint</key>
			<integer>1</integer>
		</dict>
	</dict>
	<key>SuppressBuildableAutocreation</key>
	<dict>
		<key>D3565A9E1BFD9449002CFA16</key>
		<dict>
			<key>primary</key>
			<true/>
		</dict>
	</dict>
</dict>
</plist>
Download .txt
gitextract_9vpatnox/

├── .gitignore
├── .swift-version
├── .swiftlint.yml
├── Example/
│   ├── .swiftlint.yml
│   ├── AppDelegate.swift
│   ├── Assets.xcassets/
│   │   ├── AppIcon.appiconset/
│   │   │   └── Contents.json
│   │   ├── Contents.json
│   │   ├── icon-empty-events.imageset/
│   │   │   └── Contents.json
│   │   ├── icon-empty-message.imageset/
│   │   │   └── Contents.json
│   │   ├── icon-empty-photos.imageset/
│   │   │   └── Contents.json
│   │   └── loading.imageset/
│   │       └── Contents.json
│   ├── Base.lproj/
│   │   └── Main.storyboard
│   ├── DemoViewController.swift
│   ├── EmptyDataDemoCollectionViewController.swift
│   ├── EmptyDataDemoTableViewController.swift
│   └── Info.plist
├── LICENSE.md
├── README.md
├── TBEmptyDataSet/
│   ├── Constants.swift
│   ├── EmptyDataView.swift
│   ├── Protocols.swift
│   ├── Supporting Files/
│   │   └── Info.plist
│   ├── TBEmptyDataSet.swift
│   └── WeakObjectContainer.swift
├── TBEmptyDataSet.podspec
├── TBEmptyDataSet.xcodeproj/
│   ├── project.pbxproj
│   ├── xcshareddata/
│   │   └── xcschemes/
│   │       └── TBEmptyDataSet.xcscheme
│   └── xcuserdata/
│       ├── hongxin.xcuserdatad/
│       │   └── xcschemes/
│       │       └── xcschememanagement.plist
│       └── zetasq.xcuserdatad/
│           └── xcschemes/
│               └── xcschememanagement.plist
├── TBEmptyDataSet.xcworkspace/
│   ├── contents.xcworkspacedata
│   └── xcshareddata/
│       └── IDEWorkspaceChecks.plist
└── TBEmptyDataSetExample.xcodeproj/
    ├── project.pbxproj
    ├── project.xcworkspace/
    │   └── contents.xcworkspacedata
    └── xcuserdata/
        ├── hongxin.xcuserdatad/
        │   └── xcschemes/
        │       ├── TBEmptyDataSetExample.xcscheme
        │       └── xcschememanagement.plist
        └── zetasq.xcuserdatad/
            └── xcschemes/
                ├── TBEmptyDataSetExample.xcscheme
                └── xcschememanagement.plist
Condensed preview — 37 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (122K chars).
[
  {
    "path": ".gitignore",
    "chars": 38,
    "preview": "TBEmptyDataSet.xcworkspace/xcuserdata\n"
  },
  {
    "path": ".swift-version",
    "chars": 4,
    "preview": "5.0\n"
  },
  {
    "path": ".swiftlint.yml",
    "chars": 835,
    "preview": "disabled_rules: # rule identifiers to exclude from running\n  - line_length\n  - nesting\n  - variable_name_min_length\n  - "
  },
  {
    "path": "Example/.swiftlint.yml",
    "chars": 504,
    "preview": "disabled_rules: # rule identifiers to exclude from running\n  - line_length\n  - nesting\nincluded: # paths to include duri"
  },
  {
    "path": "Example/AppDelegate.swift",
    "chars": 826,
    "preview": "//\n//  AppDelegate.swift\n//  TBEmptyDataSetExample\n//\n//  Created by 洪鑫 on 15/11/19.\n//  Copyright © 2015年 Teambition. A"
  },
  {
    "path": "Example/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "chars": 1590,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"iphone\",\n      \"size\" : \"20x20\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\""
  },
  {
    "path": "Example/Assets.xcassets/Contents.json",
    "chars": 62,
    "preview": "{\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "Example/Assets.xcassets/icon-empty-events.imageset/Contents.json",
    "chars": 166,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"icon-empty-events.pdf\"\n    }\n  ],\n  \"info\" : {\n "
  },
  {
    "path": "Example/Assets.xcassets/icon-empty-message.imageset/Contents.json",
    "chars": 167,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"icon-empty-message.pdf\"\n    }\n  ],\n  \"info\" : {\n"
  },
  {
    "path": "Example/Assets.xcassets/icon-empty-photos.imageset/Contents.json",
    "chars": 166,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"icon-empty-photos.pdf\"\n    }\n  ],\n  \"info\" : {\n "
  },
  {
    "path": "Example/Assets.xcassets/loading.imageset/Contents.json",
    "chars": 341,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n     "
  },
  {
    "path": "Example/Base.lproj/Main.storyboard",
    "chars": 10166,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3"
  },
  {
    "path": "Example/DemoViewController.swift",
    "chars": 3534,
    "preview": "//\n//  DemoViewController.swift\n//  TBEmptyDataSetExample\n//\n//  Created by 洪鑫 on 15/11/26.\n//  Copyright © 2015年 Teambi"
  },
  {
    "path": "Example/EmptyDataDemoCollectionViewController.swift",
    "chars": 9720,
    "preview": "//\n//  EmptyDataDemoCollectionViewController.swift\n//  TBEmptyDataSetExample\n//\n//  Created by 洪鑫 on 15/11/24.\n//  Copyr"
  },
  {
    "path": "Example/EmptyDataDemoTableViewController.swift",
    "chars": 6076,
    "preview": "//\n//  EmptyDataDemoTableViewController.swift\n//  TBEmptyDataSetExample\n//\n//  Created by 洪鑫 on 15/11/24.\n//  Copyright "
  },
  {
    "path": "Example/Info.plist",
    "chars": 1498,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "LICENSE.md",
    "chars": 1078,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2015 Teambition\n\nPermission is hereby granted, free of charge, to any person obtain"
  },
  {
    "path": "README.md",
    "chars": 3981,
    "preview": "# TBEmptyDataSet\nTBEmptyDataSet is an extension of UITableView/UICollectionView's super class, it will  display a placeh"
  },
  {
    "path": "TBEmptyDataSet/Constants.swift",
    "chars": 1997,
    "preview": "//\n//  Constants.swift\n//  TBEmptyDataSet\n//\n//  Created by Xin Hong on 15/11/19.\n//  Copyright © 2015年 Teambition. All "
  },
  {
    "path": "TBEmptyDataSet/EmptyDataView.swift",
    "chars": 10409,
    "preview": "//\n//  EmptyDataView.swift\n//  TBEmptyDataSet\n//\n//  Created by Xin Hong on 15/11/19.\n//  Copyright © 2015年 Teambition. "
  },
  {
    "path": "TBEmptyDataSet/Protocols.swift",
    "chars": 3501,
    "preview": "//\n//  Protocols.swift\n//  TBEmptyDataSet\n//\n//  Created by Xin Hong on 15/11/19.\n//  Copyright © 2015年 Teambition. All "
  },
  {
    "path": "TBEmptyDataSet/Supporting Files/Info.plist",
    "chars": 808,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "TBEmptyDataSet/TBEmptyDataSet.swift",
    "chars": 13091,
    "preview": "//\n//  TBEmptyDataSet.swift\n//  TBEmptyDataSet\n//\n//  Created by Xin Hong on 15/11/19.\n//  Copyright © 2015年 Teambition."
  },
  {
    "path": "TBEmptyDataSet/WeakObjectContainer.swift",
    "chars": 332,
    "preview": "//\n//  WeakObjectContainer.swift\n//  TBEmptyDataSet\n//\n//  Created by Xin Hong on 2016/11/4.\n//  Copyright © 2016年 Teamb"
  },
  {
    "path": "TBEmptyDataSet.podspec",
    "chars": 632,
    "preview": "Pod::Spec.new do |s|\n\n  s.name         = \"TBEmptyDataSet\"\n  s.version      = \"3.0.2\"\n  s.summary      = \"An extension of"
  },
  {
    "path": "TBEmptyDataSet.xcodeproj/project.pbxproj",
    "chars": 13007,
    "preview": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 46;\n\tobjects = {\n\n/* Begin PBXBuildFile section *"
  },
  {
    "path": "TBEmptyDataSet.xcodeproj/xcshareddata/xcschemes/TBEmptyDataSet.xcscheme",
    "chars": 2913,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1020\"\n   version = \"1.3\">\n   <BuildAction\n      "
  },
  {
    "path": "TBEmptyDataSet.xcodeproj/xcuserdata/hongxin.xcuserdatad/xcschemes/xcschememanagement.plist",
    "chars": 498,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "TBEmptyDataSet.xcodeproj/xcuserdata/zetasq.xcuserdatad/xcschemes/xcschememanagement.plist",
    "chars": 498,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "TBEmptyDataSet.xcworkspace/contents.xcworkspacedata",
    "chars": 244,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"group:TBEmptyDataSet"
  },
  {
    "path": "TBEmptyDataSet.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "chars": 238,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "TBEmptyDataSetExample.xcodeproj/project.pbxproj",
    "chars": 14584,
    "preview": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 46;\n\tobjects = {\n\n/* Begin PBXBuildFile section *"
  },
  {
    "path": "TBEmptyDataSetExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "chars": 214,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:/Users/hongxin/"
  },
  {
    "path": "TBEmptyDataSetExample.xcodeproj/xcuserdata/hongxin.xcuserdatad/xcschemes/TBEmptyDataSetExample.xcscheme",
    "chars": 3471,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1020\"\n   version = \"1.3\">\n   <BuildAction\n      "
  },
  {
    "path": "TBEmptyDataSetExample.xcodeproj/xcuserdata/hongxin.xcuserdatad/xcschemes/xcschememanagement.plist",
    "chars": 493,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "TBEmptyDataSetExample.xcodeproj/xcuserdata/zetasq.xcuserdatad/xcschemes/TBEmptyDataSetExample.xcscheme",
    "chars": 3471,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"0730\"\n   version = \"1.3\">\n   <BuildAction\n      "
  },
  {
    "path": "TBEmptyDataSetExample.xcodeproj/xcuserdata/zetasq.xcuserdatad/xcschemes/xcschememanagement.plist",
    "chars": 493,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  }
]

About this extraction

This page contains the full source code of the teambition/TBEmptyDataSet GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 37 files (109.0 KB), approximately 30.5k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!