├── .gitignore ├── CHANGELOG.md ├── Example ├── Podfile ├── Podfile.lock ├── UIEmptyStateExample.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata ├── UIEmptyStateExample.xcworkspace │ └── contents.xcworkspacedata └── UIEmptyStateExample │ ├── AppDelegate.swift │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Contents.json │ ├── buttonBg.imageset │ │ ├── Contents.json │ │ ├── minus-button-1.png │ │ ├── minus-button-2.png │ │ └── minus-button.png │ ├── emptyPokemon.imageset │ │ ├── Contents.json │ │ ├── open-pokeball-1.png │ │ ├── open-pokeball-2.png │ │ └── open-pokeball.png │ └── pokeball.imageset │ │ ├── Contents.json │ │ ├── pokeball-1.png │ │ ├── pokeball-2.png │ │ └── pokeball.png │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── EmptyStateCollectionViewCell.swift │ ├── EmptyStateCollectionViewController.swift │ ├── EmptyStateTableViewController.swift │ └── Info.plist ├── GitHubAssets ├── banner.jpg ├── screen1.jpg ├── screen2.jpg └── screen3.jpg ├── LICENSE ├── README.md ├── UIEmptyState.podspec ├── docs ├── Classes.html ├── Classes │ └── UIEmptyStateView.html ├── Extensions.html ├── Extensions │ ├── UICollectionViewController.html │ ├── UITableViewController.html │ └── UIViewController.html ├── Protocols.html ├── Protocols │ ├── UIEmptyStateDataSource.html │ └── UIEmptyStateDelegate.html ├── badge.svg ├── css │ ├── highlight.css │ └── jazzy.css ├── docsets │ ├── UIEmptyState.docset │ │ └── Contents │ │ │ ├── Info.plist │ │ │ └── Resources │ │ │ ├── Documents │ │ │ ├── Classes.html │ │ │ ├── Classes │ │ │ │ └── UIEmptyStateView.html │ │ │ ├── Extensions.html │ │ │ ├── Extensions │ │ │ │ ├── UICollectionViewController.html │ │ │ │ ├── UITableViewController.html │ │ │ │ └── UIViewController.html │ │ │ ├── Protocols.html │ │ │ ├── Protocols │ │ │ │ ├── UIEmptyStateDataSource.html │ │ │ │ └── UIEmptyStateDelegate.html │ │ │ ├── css │ │ │ │ ├── highlight.css │ │ │ │ └── jazzy.css │ │ │ ├── img │ │ │ │ ├── carat.png │ │ │ │ ├── dash.png │ │ │ │ ├── gh.png │ │ │ │ └── spinner.gif │ │ │ ├── index.html │ │ │ ├── js │ │ │ │ ├── jazzy.js │ │ │ │ ├── jazzy.search.js │ │ │ │ ├── jquery.min.js │ │ │ │ ├── lunr.min.js │ │ │ │ └── typeahead.jquery.js │ │ │ └── search.json │ │ │ └── docSet.dsidx │ └── UIEmptyState.tgz ├── img │ ├── carat.png │ ├── dash.png │ ├── gh.png │ └── spinner.gif ├── index.html ├── js │ ├── jazzy.js │ ├── jazzy.search.js │ ├── jquery.min.js │ ├── lunr.min.js │ └── typeahead.jquery.js ├── search.json └── undocumented.json ├── run-jazzy.sh └── src ├── UIEmptyState.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcschemes │ └── UIEmptyState.xcscheme ├── UIEmptyState ├── Info.plist ├── UIEmptyState.h ├── UIEmptyStateDataSource.swift ├── UIEmptyStateDelegate.swift ├── UIEmptyStateView.swift └── UIViewController+UIEmptyState.swift └── UIEmptyStateTests ├── Info.plist └── UIEmptyStateTests.swift /.gitignore: -------------------------------------------------------------------------------- 1 | ## OS X Finder 2 | .DS_Store 3 | 4 | ## Build generated 5 | build/ 6 | DerivedData 7 | 8 | ## Various settings 9 | *.pbxuser 10 | !default.pbxuser 11 | *.mode1v3 12 | !default.mode1v3 13 | *.mode2v3 14 | !default.mode2v3 15 | *.perspectivev3 16 | !default.perspectivev3 17 | xcuserdata 18 | 19 | ## Other 20 | *.xccheckout 21 | *.moved-aside 22 | *.xcuserstate 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | 29 | # Swift Package Manager 30 | .build/ 31 | 32 | # Carthage 33 | Carthage/Build 34 | 35 | # Ignore pods 36 | Example/Pods/ 37 | UIEmptyState.framework.zip 38 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # UIEmptyState CHANGELOG 2 | 3 | ## Version 4.0.1 4 | 5 | - Fix memory leak due to retain cycle 6 | 7 | ## Version 4.0.0 8 | 9 | - Swift 4.2 support 10 | 11 | ## Version 3.2.0 12 | 13 | - Fix bug where the empty view was not centered properly on 14 | non `UITableViewController` or `UICollectionViewController` types. 15 | - Fix bug where empty view did not account for table header view height 16 | thus was not truly centered in the visible area of the table view 17 | 18 | ## Version 3.1.2 19 | 20 | - Build with Swift 4.1.2 21 | 22 | ## Version 3.1.1 23 | 24 | - Buid with Swift 4.1 25 | 26 | ## Version 3.1.0 27 | 28 | - Added new `emptyStateImageViewTintColor`, which allows applying a tint color to the default `UIEmptyStateView`'s image view. 29 | - Added new `emptyStateViewCenterYOffset`, which allows offsetting the vertical center position for the empty state view. 30 | - Example project now uses local `UIEmptyState` pod, to allow for local development and easier testing. 31 | 32 | Changes made by: [gabmarfer](https://github.com/gabmarfer), thanks! 33 | 34 | ## Version 3.0.0 35 | 36 | - Fixed bug where `UIEmptyStateDataSource.emptyStateView` computed property and the the `UIViewController+UIEmptyState.emptyStateView` properties conflicted thus not allowing creation of custom view. 37 | - Custom views now work as intended, you can return a `UIView` subclass in the `UIEmptyStateDataSource.emptyStateView` computed property. Make sure to set any needed constraints, etc. 38 | 39 | **Breaking API changes:** 40 | 41 | Remove `emptyStateView` from view controller extension. This was never intended to be accessible outside of the extension. Use the `emptyStateView` computed property in `UIEmptyStateDataSource` to create a custom view instead. 42 | 43 | ## Version 2.0.2 44 | 45 | Bug fix for retain cycle between delegate, datasource, and the view controller. 46 | Thanks to [@piotrzuzel](https://github.com/piotrzuzel) for the fix. 47 | 48 | ## Version 2.0.1 49 | 50 | Add shared scheme, fixed thanks to [@piotrzuzel](https://github.com/piotrzuzel). 51 | 52 | ## Version 2.0.0 53 | 54 | #### Breaking API Changes: 55 | 56 | - Renamed `reloadEmptyState(for: tableView)` to `reloadEmptyStateForTableView(_:)` __and__ `reloadEmptyState(for: collectionView)` to `reloadEmptyStateForCollectionView(_:)`. This fixes an issue where error is thrown for duplicate function declaration with Objective-C selector on Swift versions lower than 4.0. 57 | 58 | 59 | - Renamed the `shouldShowEmptyStateView(for:)` datasource method to `emptyStateViewShouldShow(for:)`. This was done to be more consistent with the rest of the API. 60 | 61 | 62 | - Renamed `titleView` to `titleLabel` __and__ `detailView` to `detailLabel`. This makes it more clear exactly what these views actually are. 63 | 64 | #### Improvements and Fixes 65 | 66 | - Fix a bug where constraints for the `UIEmptyStateView` would be added whenever the view appeared thus causing a warning to be thrown by Xcode for duplicate and breaking constraints. Constraints for the view are now only added on initial showing of view. 67 | 68 | - Fix bug where `UIEmptyStateView.detailLabel` would not resize and fit the screen correctly on iOS versions lower than 11.0. `detailLabel` now calculates it's width properly and constraints are added accordingly. 69 | 70 | - Change `emptyStateViewAnimatesEverytime` from `true` to `false`. This seems like a more reasonable default value as it animations can get annoying when repeated multiple times without change. 71 | 72 | ## Versin 1.0.2 73 | 74 | - Fix issue with `UIEmptyStateView` and it's subviews not being accessible. 75 | 76 | ## Version 1.0.1 77 | 78 | - Fix access modifier in default implementation of `emptyStateViewWillHide(view: UIView)` 79 | 80 | ## Version 1.0.0 - Stable Release 81 | 82 | - Add Swift version check to allow support for Swift 3 --> Swift 4. 83 | - Refactor public API to make it less verbose and more Swift-like. 84 | * All methods which had the format `methodName(forSomething:)` have been refactored to simply `methodName(for:)`. 85 | * Due to this renaming, if using Swift 3.2 or lower, you may get an error 86 | about a `@objc` method having already been declared, this is due to Swift 3 inferring an `@objc` attribute when it is not in fact `@objc`. If using Xcode 9 +, you will need to set `Swift 3 @objc Inference` in the `Optimization Level` of this projects `Build Settings` to `Off`. I know this is a hassle, but I want to keep the API clean and stable, no point in changing it at a later date when Swift 4 is fully released and breaking more code. 87 | - Add two new delegate methods to the `UIEmptyStateDelegate` 88 | * `emptyStateViewWillShow(view: UIView)` which is called before the view is shown, given you time to do any additional work. 89 | * `emptyStateViewWillHide(view: UIView)` which is called right before the view will be hidden from the screen. 90 | - Fix some broken documentation/updated docs 91 | 92 | After this release the API should not change that often, thus I wont be breaking your code as much 😅 93 | 94 | Thanks for using `UIEmptyState` 95 | 96 | 97 | ## Version 0.8.3 98 | 99 | - Fix bug where data source was not reverting `edgesForExtendedLayout` to default values after it was done presenting the view 100 | 101 | ## Version 0.8.2 102 | 103 | - Fix issue with constraints not being set properly for Empty state view 104 | 105 | ## Version 0.8.1 106 | 107 | - Make sure `.swift-version` is included in pod to fix warnings in Xcode 9 108 | 109 | ## Version 0.8.0 110 | 111 | - Updated for Swift 4 and Xcode 9 112 | - Now uses `safeAreaLayoutGuide` to adjust centering of empty state view, if on iOS 11 when `shouldAdjustToFitBars` returns `true`. 113 | - Updated project to recommended settings in Xcode 9, set language to Swift 4 and `@objc inference` to `default`. 114 | 115 | ## Version 0.7.0 116 | 117 | - Update constraints for labels so that they do not extend past the edges of the view 118 | - Add private extension to help calculate the the height of labels 119 | 120 | ## Version 0.6.0 121 | 122 | - Fix bug where title for UIEmptyState was not being updated when reloading 123 | - Made sure to assign all datasource properties when creating, and updating the view 124 | 125 | 126 | ## Version 0.5.0 127 | 128 | - Refactored API methods for `UIEmptyStateDataSource` into computed properties to be more "swift-like" 129 | - Add new delegate method to get notified when the view has been shown, here you can bring subviews to front that may have been covered by the UIEmptyState 130 | - Add new property to determine whether the viewcontroller using UIEmptyState should adjust it's frame to take into account navigation bars/tab bars 131 | - Refactored a lot of code and comments 132 | 133 | #### Breaking API Changes in 0.5.0 134 | 135 | Basically, everything... Sorry! 136 | Due to the change from methods to properties, the way you interact with the data source has changed. [Please read the documentation](https://htmlpreview.github.io/?https://raw.githubusercontent.com/luispadron/UIEmptyState/master/docs/Protocols/UIEmptyStateDataSource.html). Most methods have been convereted into computed properties. This will be the design going forward unless it's not possible, i.e requires parameters or would be better in a method. 137 | 138 | ## Version 0.4.0 139 | 140 | - Add ability to animate the empty state view 141 | - Removed returning a detail message in the default implementation as this caused an annoying problem in that you would need to implement that method and return nil if you didn't want to use a detail message 142 | 143 | ## Version 0.3.0 144 | 145 | - Add fixes for views not being updated 146 | - Add fix to constraints becoming wonky after readding views 147 | - Add convenience extenions to `UITableViewController` and `UICollectionViewController` 148 | 149 | You can now do this: 150 | 151 | ```swift 152 | // If a tableview or collectionview controller subclass 153 | // This will default the tableView/collectionView to self.tableView/collectionView 154 | self.reloadEmptyState() 155 | ``` 156 | - Refactoring of UIEmptyStateView 157 | - Rerun Jazzy for documentation 158 | 159 | ## Version 0.2.1 160 | 161 | - Add fix for updating view, titles can now be changed on the fly using data source methods 162 | - Some small amount of refactoring 163 | 164 | ## Version 0.2.0 165 | 166 | - Now works with any UIViewController subclass 167 | - Update example project to contain a `UITableViewController` example as well a `UICollectionViewController` exmaple 168 | - Refactor some uneeded code 169 | - Rerun Jazzy for code documentation 170 | 171 | ##### Breaking API CHanges: 172 | 173 | - Call for reloading of empty state view has changed 174 | 175 | _Before_ 176 | 177 | ```swift 178 | // Called whenever data has changed, only worked for table views 179 | self.reloadTableViewEmptyState() 180 | ``` 181 | 182 | _Now_ 183 | 184 | ```swift 185 | // Called whenever data has changed, now works with collectionview or tableview 186 | // If tableView controller, or controller with tableView property 187 | self.reloadEmptyState(forTableView: self.tableView) 188 | // OR if collectionview controller, or controller with collectionView property 189 | self.reloadEmptyState(forCollectionView: self.collectionView) 190 | ``` 191 | 192 | ## Version 0.1.1 193 | 194 | - Initial of the initial release (messed up the spec in 0.1.0 oops) 195 | - Currently only works with `UITableViewController` 196 | - iOS required of 9.0 + 197 | 198 | ## Version 0.1.0 199 | 200 | - Initial release 201 | - Currently only works with `UITableViewController` 202 | - iOS required of 9.0 + 203 | -------------------------------------------------------------------------------- /Example/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment the next line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | target 'UIEmptyStateExample' do 5 | # Comment the next line if you're not using Swift and don't want to use dynamic frameworks 6 | use_frameworks! 7 | 8 | # Pods for UIEmptyStateExample 9 | pod 'UIEmptyState', :path => '../' 10 | end 11 | -------------------------------------------------------------------------------- /Example/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - UIEmptyState (4.0.0) 3 | 4 | DEPENDENCIES: 5 | - UIEmptyState (from `../`) 6 | 7 | EXTERNAL SOURCES: 8 | UIEmptyState: 9 | :path: "../" 10 | 11 | SPEC CHECKSUMS: 12 | UIEmptyState: a299e655ecb6fc077dfb503c375ef9a023103240 13 | 14 | PODFILE CHECKSUM: 2410e5e41e5d4570ddd34538c5ba93d5d35ce08c 15 | 16 | COCOAPODS: 1.5.3 17 | -------------------------------------------------------------------------------- /Example/UIEmptyStateExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/UIEmptyStateExample.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Example/UIEmptyStateExample/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // UIEmptyStateExample 4 | // 5 | // Created by Luis Padron on 2/3/17. 6 | // Copyright © 2017 Luis Padron. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /Example/UIEmptyStateExample/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /Example/UIEmptyStateExample/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Example/UIEmptyStateExample/Assets.xcassets/buttonBg.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "minus-button.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "minus-button-1.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "minus-button-2.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Example/UIEmptyStateExample/Assets.xcassets/buttonBg.imageset/minus-button-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luispadron/UIEmptyState/4fb00263b97ee3a513f029244eb73580b605ab71/Example/UIEmptyStateExample/Assets.xcassets/buttonBg.imageset/minus-button-1.png -------------------------------------------------------------------------------- /Example/UIEmptyStateExample/Assets.xcassets/buttonBg.imageset/minus-button-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luispadron/UIEmptyState/4fb00263b97ee3a513f029244eb73580b605ab71/Example/UIEmptyStateExample/Assets.xcassets/buttonBg.imageset/minus-button-2.png -------------------------------------------------------------------------------- /Example/UIEmptyStateExample/Assets.xcassets/buttonBg.imageset/minus-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luispadron/UIEmptyState/4fb00263b97ee3a513f029244eb73580b605ab71/Example/UIEmptyStateExample/Assets.xcassets/buttonBg.imageset/minus-button.png -------------------------------------------------------------------------------- /Example/UIEmptyStateExample/Assets.xcassets/emptyPokemon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "open-pokeball.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "open-pokeball-1.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "open-pokeball-2.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Example/UIEmptyStateExample/Assets.xcassets/emptyPokemon.imageset/open-pokeball-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luispadron/UIEmptyState/4fb00263b97ee3a513f029244eb73580b605ab71/Example/UIEmptyStateExample/Assets.xcassets/emptyPokemon.imageset/open-pokeball-1.png -------------------------------------------------------------------------------- /Example/UIEmptyStateExample/Assets.xcassets/emptyPokemon.imageset/open-pokeball-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luispadron/UIEmptyState/4fb00263b97ee3a513f029244eb73580b605ab71/Example/UIEmptyStateExample/Assets.xcassets/emptyPokemon.imageset/open-pokeball-2.png -------------------------------------------------------------------------------- /Example/UIEmptyStateExample/Assets.xcassets/emptyPokemon.imageset/open-pokeball.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luispadron/UIEmptyState/4fb00263b97ee3a513f029244eb73580b605ab71/Example/UIEmptyStateExample/Assets.xcassets/emptyPokemon.imageset/open-pokeball.png -------------------------------------------------------------------------------- /Example/UIEmptyStateExample/Assets.xcassets/pokeball.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "pokeball.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "pokeball-2.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "pokeball-1.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Example/UIEmptyStateExample/Assets.xcassets/pokeball.imageset/pokeball-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luispadron/UIEmptyState/4fb00263b97ee3a513f029244eb73580b605ab71/Example/UIEmptyStateExample/Assets.xcassets/pokeball.imageset/pokeball-1.png -------------------------------------------------------------------------------- /Example/UIEmptyStateExample/Assets.xcassets/pokeball.imageset/pokeball-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luispadron/UIEmptyState/4fb00263b97ee3a513f029244eb73580b605ab71/Example/UIEmptyStateExample/Assets.xcassets/pokeball.imageset/pokeball-2.png -------------------------------------------------------------------------------- /Example/UIEmptyStateExample/Assets.xcassets/pokeball.imageset/pokeball.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luispadron/UIEmptyState/4fb00263b97ee3a513f029244eb73580b605ab71/Example/UIEmptyStateExample/Assets.xcassets/pokeball.imageset/pokeball.png -------------------------------------------------------------------------------- /Example/UIEmptyStateExample/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Example/UIEmptyStateExample/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | -------------------------------------------------------------------------------- /Example/UIEmptyStateExample/EmptyStateCollectionViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EmptyStateCollectionViewCell.swift 3 | // UIEmptyStateExample 4 | // 5 | // Created by Luis Padron on 2/5/17. 6 | // Copyright © 2017 Luis Padron. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class EmptyStateCollectionViewCell: UICollectionViewCell { 12 | @IBOutlet weak var pokemonLabel: UILabel! 13 | 14 | override func awakeFromNib() { 15 | super.awakeFromNib() 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Example/UIEmptyStateExample/EmptyStateCollectionViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EmptyStateCollectionViewController.swift 3 | // UIEmptyStateExample 4 | // 5 | // Created by Luis Padron on 2/5/17. 6 | // Copyright © 2017 Luis Padron. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import UIEmptyState 11 | 12 | private let reuseIdentifier = "collectionViewCell" 13 | 14 | class EmptyStateCollectionViewController: UICollectionViewController, UIEmptyStateDelegate, UIEmptyStateDataSource { 15 | 16 | var caughtPokemon = [String]() 17 | 18 | override func viewDidLoad() { 19 | self.title = "CollectionView Example" 20 | super.viewDidLoad() 21 | 22 | // Set delegate and data source 23 | self.emptyStateDelegate = self 24 | self.emptyStateDataSource = self 25 | // Set the inital state of the collectionview 26 | self.reloadEmptyState() 27 | } 28 | 29 | override func didReceiveMemoryWarning() { 30 | super.didReceiveMemoryWarning() 31 | } 32 | 33 | // MARK: UICollectionViewDataSource 34 | 35 | override func numberOfSections(in collectionView: UICollectionView) -> Int { 36 | // #warning Incomplete implementation, return the number of sections 37 | return 1 38 | } 39 | 40 | 41 | override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 42 | // #warning Incomplete implementation, return the number of items 43 | return caughtPokemon.count 44 | } 45 | 46 | override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 47 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! EmptyStateCollectionViewCell 48 | cell.pokemonLabel.text = caughtPokemon[indexPath.row] 49 | return cell 50 | } 51 | 52 | // MARK: UICollectionViewDelegate 53 | 54 | // MARK: - Helper Methods 55 | 56 | @IBAction func addButtonTapped(_ sender: UIBarButtonItem) { 57 | // Add pokemon 58 | let row = caughtPokemon.count == 0 ? 0 : caughtPokemon.count - 1 59 | addPokemon(at: IndexPath(row: row, section: 0)) 60 | } 61 | 62 | // MARK: - Helper Methods 63 | 64 | let pokemon = ["Pikachu", "Charizard", "Charmander", "Caterpie", "Butterfree", "Mew", "MewTwo", "Growlithe", "Squirtle"] 65 | 66 | func randomPokemon() -> String { 67 | let index = arc4random_uniform(UInt32(pokemon.count)) 68 | return pokemon[Int(index)] 69 | } 70 | 71 | func addPokemon(at path: IndexPath) { 72 | let newPokemon = randomPokemon() 73 | caughtPokemon.append(newPokemon) 74 | self.collectionView?.reloadData() 75 | // Remember to call this to reload the empty state view after every data change 76 | self.reloadEmptyState() 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /Example/UIEmptyStateExample/EmptyStateTableViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // UIEmptyStateExample 4 | // 5 | // Created by Luis Padron on 2/3/17. 6 | // Copyright © 2017 Luis Padron. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import UIEmptyState 11 | 12 | class EmptyStateTableViewController: UITableViewController, UIEmptyStateDelegate, UIEmptyStateDataSource { 13 | 14 | var caughtPokemon = [String]() 15 | 16 | override func viewDidLoad() { 17 | super.viewDidLoad() 18 | self.title = "TableView Example" 19 | // Set the data source and delegate to self 20 | self.emptyStateDataSource = self 21 | self.emptyStateDelegate = self 22 | // Remove seperator lines from empty cells 23 | self.tableView.tableFooterView = UIView(frame: CGRect.zero) 24 | // Initially call the reloadTableViewState to get the initial state 25 | self.reloadEmptyState() 26 | self.view.backgroundColor = UIColor(red: 0.518, green: 0.576, blue: 0.604, alpha: 1.00) 27 | } 28 | 29 | // MARK: - Empty State Data Source 30 | 31 | var emptyStateImage: UIImage? { 32 | return #imageLiteral(resourceName: "emptyPokemon") 33 | } 34 | 35 | var emptyStateTitle: NSAttributedString { 36 | let attrs = [NSAttributedString.Key.foregroundColor: UIColor(red: 0.882, green: 0.890, blue: 0.859, alpha: 1.00), 37 | NSAttributedString.Key.font: UIFont.systemFont(ofSize: 22)] 38 | return NSAttributedString(string: "No Pokemon caught!", attributes: attrs) 39 | } 40 | 41 | var emptyStateButtonTitle: NSAttributedString? { 42 | let attrs = [NSAttributedString.Key.foregroundColor: UIColor.white, 43 | NSAttributedString.Key.font: UIFont.systemFont(ofSize: 16)] 44 | return NSAttributedString(string: "Catch'em All", attributes: attrs) 45 | } 46 | 47 | var emptyStateButtonSize: CGSize? { 48 | return CGSize(width: 100, height: 40) 49 | } 50 | 51 | 52 | // MARK: - Empty State Delegate 53 | 54 | func emptyStateViewWillShow(view: UIView) { 55 | guard let emptyView = view as? UIEmptyStateView else { return } 56 | // Some custom button stuff 57 | emptyView.button.layer.cornerRadius = 5 58 | emptyView.button.layer.borderWidth = 1 59 | emptyView.button.layer.borderColor = UIColor.red.cgColor 60 | emptyView.button.layer.backgroundColor = UIColor.red.cgColor 61 | } 62 | 63 | func emptyStatebuttonWasTapped(button: UIButton) { 64 | // Add a pokemon 65 | let row = caughtPokemon.count == 0 ? 0 : caughtPokemon.count - 1 66 | addPokemon(at: IndexPath(row: row, section: 0)) 67 | } 68 | 69 | // MARK: - TableView Delegation 70 | 71 | override func numberOfSections(in tableView: UITableView) -> Int { 72 | return 1 73 | } 74 | 75 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 76 | return caughtPokemon.count 77 | } 78 | 79 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 80 | let cell = tableView.dequeueReusableCell(withIdentifier: "exampleCell")! 81 | let pokemon = caughtPokemon[indexPath.row] 82 | cell.textLabel?.text = "Pokemon caught: \(pokemon)" 83 | return cell 84 | } 85 | 86 | override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { 87 | if editingStyle == .delete { 88 | // Delete the row 89 | tableView.beginUpdates() 90 | caughtPokemon.remove(at: indexPath.row) 91 | tableView.deleteRows(at: [indexPath], with: .automatic) 92 | tableView.endUpdates() 93 | // Call reload of empty state 94 | self.reloadEmptyState() 95 | } 96 | } 97 | 98 | @IBAction func addButtonTapped(_ sender: UIBarButtonItem) { 99 | // Add pokemon 100 | let row = caughtPokemon.count == 0 ? 0 : caughtPokemon.count - 1 101 | addPokemon(at: IndexPath(row: row, section: 0)) 102 | } 103 | 104 | // MARK: - Helper Methods 105 | 106 | let pokemon = ["Pikachu", "Charizard", "Charmander", "Caterpie", "Butterfree", "Mew", "MewTwo", "Growlithe", "Squirtle"] 107 | 108 | func randomPokemon() -> String { 109 | let index = arc4random_uniform(UInt32(pokemon.count)) 110 | return pokemon[Int(index)] 111 | } 112 | 113 | func addPokemon(at path: IndexPath) { 114 | let newPokemon = randomPokemon() 115 | caughtPokemon.append(newPokemon) 116 | tableView.beginUpdates() 117 | tableView.insertRows(at: [path], with: .automatic) 118 | tableView.endUpdates() 119 | self.reloadEmptyState() 120 | } 121 | } 122 | 123 | -------------------------------------------------------------------------------- /Example/UIEmptyStateExample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /GitHubAssets/banner.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luispadron/UIEmptyState/4fb00263b97ee3a513f029244eb73580b605ab71/GitHubAssets/banner.jpg -------------------------------------------------------------------------------- /GitHubAssets/screen1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luispadron/UIEmptyState/4fb00263b97ee3a513f029244eb73580b605ab71/GitHubAssets/screen1.jpg -------------------------------------------------------------------------------- /GitHubAssets/screen2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luispadron/UIEmptyState/4fb00263b97ee3a513f029244eb73580b605ab71/GitHubAssets/screen2.jpg -------------------------------------------------------------------------------- /GitHubAssets/screen3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luispadron/UIEmptyState/4fb00263b97ee3a513f029244eb73580b605ab71/GitHubAssets/screen3.jpg -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 Luis Padron 2 | 3 | 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: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | 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. 8 | 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![banner](https://raw.githubusercontent.com/luispadron/UIEmptyState/master/GitHubAssets/banner.jpg) 2 | 3 | 4 | 5 | 6 | 7 | ## Requirements 8 | 9 | - Xcode 9.0 + 10 | - iOS 9.0 or greater 11 | 12 | 13 | ## Installation 14 | 15 | ### CocoaPods 16 | 17 | 1. Install [CocoaPods](http://cocoapods.org) 18 | 2. Add this repo to your `Podfile` 19 | 20 | ```ruby 21 | target 'Example' do 22 | use_frameworks! 23 | 24 | pod 'UIEmptyState' 25 | end 26 | ``` 27 | 28 | 3. Run `pod install` 29 | 4. Open up the new `.xcworkspace` that CocoaPods generated 30 | 5. Whenever you want to use the library: `import UIEmptyState` 31 | 32 | ### Carthage 33 | 34 | 1. Make sure Carthage is install 35 | 36 | `brew install carthage` 37 | 2. Add this repo to your Cartfile 38 | 39 | `github "luispadron/UIEmptyState"` 40 | 41 | 42 | ### Manually 43 | 44 | 1. Simply download the `UIEmptyState` source files and import them into your project. 45 | 46 | 47 | ## Usage 48 | 49 | As long as you are using a `UIViewController` subclass you will get default conformance as well as the `reloadEmptyState` method. 50 | 51 | ```swift 52 | // No subclassing required, simply conform to the two protocols 53 | class ViewController: UITableViewController, UIEmptyStateDataSource, UIEmptyStateDelegate { 54 | 55 | override func viewDidLoad() { 56 | super.viewDidLoad() 57 | // Set the data source and delegate 58 | self.emptyStateDataSource = self 59 | self.emptyStateDelegate = self 60 | // Optionally remove seperator lines from empty cells 61 | self.tableView.tableFooterView = UIView(frame: CGRect.zero) 62 | } 63 | 64 | override func viewDidAppear(_ animated: Bool) { 65 | super.viewDidAppear(animated) 66 | // Set the initial state of the tableview, called here because cells should be done loading by now 67 | // Number of cells are used to determine if the view should be shown or not 68 | self.reloadEmptyState() 69 | } 70 | } 71 | ``` 72 | 73 | Whenever you need to reload the empty state view for example, on data changes to your table view source, make sure to call `self.reloadEmptyState()` if inside a `UITableViewController` or `UICollectionViewController`. If inside a regular `UIViewController` make sure to call the appropriate `reloadEmptyStateForTableView(_:)` or `reloadEmptyStateForCollectionView(_:)` methods. 74 | 75 | Example: 76 | 77 | ```swift 78 | // Inside a UITableViewController subclass 79 | 80 | func foo() { 81 | // My data has changed here, I want to my tableview, 82 | // and in case I no longer have data (user deleted, etc) also reload empty view 83 | self.tableView.reloadData() 84 | // Reload empty view as well 85 | self.reloadEmptyState() 86 | } 87 | 88 | func deleteFoo() { 89 | // This works too, just call after end updates 90 | tableView.beginUpdates() 91 | fooSource.remove(at: indexPath.row) 92 | tableView.deleteRows(at: [indexPath], with: .automatic) 93 | tableView.endUpdates() 94 | // Call reload of empty state 95 | self.reloadEmptyState() 96 | } 97 | ``` 98 | 99 | If you need more help take a look at the example project here (Pokemon nerds, will like it): [Example](https://github.com/luispadron/UIEmptyState/tree/master/UIEmptyStateExample) 100 | 101 | ## Documentation 102 | 103 | 104 | #### [Read the full documentation here](http://htmlpreview.github.io/?https://github.com/luispadron/UIEmptyState/blob/master/docs/index.html) 105 | 106 | ## Example Project 107 | 108 | 1. Clone this repo 109 | 2. Change directory into `Example` 110 | 3. Run `pod install` 111 | 112 | 113 | ## License (MIT) 114 | 115 | ``` 116 | Copyright (c) 2017 Luis Padron 117 | 118 | 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: 119 | 120 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 121 | 122 | 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. 123 | ``` 124 | -------------------------------------------------------------------------------- /UIEmptyState.podspec: -------------------------------------------------------------------------------- 1 | 2 | Pod::Spec.new do |s| 3 | 4 | s.name = "UIEmptyState" 5 | s.version = "5.0.0" 6 | s.summary = "An empty state control to give visually appealing context when building iOS applications." 7 | s.description = <<-DESC 8 | Empty state control which gives context with either a message, image, and buttons to the user when ever there is a reason the state is empty. 9 | Easily conform to the protocol to provide a visually appealing view to an empty table view controller. 10 | DESC 11 | s.homepage = "https://github.com/luispadron/UIEmptyState" 12 | s.screenshots = "https://raw.githubusercontent.com/luispadron/UIEmptyState/master/GitHubAssets/screen1.jpg", "https://raw.githubusercontent.com/luispadron/UIEmptyState/master/GitHubAssets/screen2.jpg", "https://raw.githubusercontent.com/luispadron/UIEmptyState/master/GitHubAssets/screen3.jpg" 13 | s.license = { :type => "MIT", :file => "LICENSE" } 14 | s.author = { "Luis Padron" => "luis@luispadron.com" } 15 | s.social_media_url = "https://luispadron.com" 16 | s.platform = :ios, "12.0" 17 | s.swift_version = '5.3' 18 | s.source = { :git => "https://github.com/luispadron/UIEmptyState.git", :tag => "v#{s.version}" } 19 | s.source_files = "src/UIEmptyState", "src/UIEmptyState/**/*.{h,m}" 20 | end 21 | 22 | -------------------------------------------------------------------------------- /docs/Classes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Classes Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 |

22 | 23 | UIEmptyState Docs 24 | 25 | (100% documented) 26 |

27 | 28 |

29 |

30 | 31 |
32 |

33 | 34 |

35 | 36 | 37 | View on GitHub 38 | 39 |

40 | 41 |
42 | 43 | 48 | 49 |
50 | 87 |
88 | 89 |
90 |
91 |

Classes

92 |

The following classes are available globally.

93 | 94 |
95 |
96 | 97 |
98 |
99 |
100 |
    101 |
  • 102 |
    103 | 104 | 105 | 106 | UIEmptyStateView 107 | 108 |
    109 |
    110 |
    111 |
    112 |
    113 |
    114 |

    A UIView which has a stack view and inside the stackview are 1-4 other views 115 | This view is used as the default view for the emptyStateView in the UIEmptyStateDataSource

    116 | 117 | See more 118 |
    119 |
    120 |

    Declaration

    121 |
    122 |

    Swift

    123 |
    open class UIEmptyStateView: UIView
    124 | 125 |
    126 |
    127 |
    128 |
    129 |
  • 130 |
131 |
132 |
133 |
134 | 135 |
136 |
137 | 141 | 142 | 143 | 144 | -------------------------------------------------------------------------------- /docs/Extensions.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Extensions Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 |

22 | 23 | UIEmptyState Docs 24 | 25 | (100% documented) 26 |

27 | 28 |

29 |

30 | 31 |
32 |

33 | 34 |

35 | 36 | 37 | View on GitHub 38 | 39 |

40 | 41 |
42 | 43 | 48 | 49 |
50 | 87 |
88 | 89 |
90 |
91 |

Extensions

92 |

The following extensions are available globally.

93 | 94 |
95 |
96 | 97 |
98 |
99 |
100 |
    101 |
  • 102 |
    103 | 104 | 105 | 106 | UIViewController 107 | 108 |
    109 |
    110 |
    111 |
    112 |
    113 |
    114 |

    Extension on UIViewController which adds method and computed properties in order to allow empty view creation

    115 | 116 | See more 117 |
    118 |
    119 |
    120 |
  • 121 |
  • 122 |
    123 | 124 | 125 | 126 | UITableViewController 127 | 128 |
    129 |
    130 |
    131 |
    132 |
    133 |
    134 |

    A convenience extension for UITableViewController which defaults the tableView

    135 | 136 | See more 137 |
    138 |
    139 |
    140 |
  • 141 |
  • 142 |
    143 | 144 | 145 | 146 | UICollectionViewController 147 | 148 |
    149 |
    150 |
    151 |
    152 |
    153 |
    154 |

    A convenience extension for UICollectionViewController which defaults the collectionView

    155 | 156 | See more 157 |
    158 |
    159 |
    160 |
  • 161 |
162 |
163 |
164 |
165 | 166 |
167 |
168 | 172 | 173 | 174 | 175 | -------------------------------------------------------------------------------- /docs/Extensions/UICollectionViewController.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | UICollectionViewController Extension Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |

23 | 24 | UIEmptyState Docs 25 | 26 | (100% documented) 27 |

28 | 29 |

30 |

31 | 32 |
33 |

34 | 35 |

36 | 37 | 38 | View on GitHub 39 | 40 |

41 | 42 |
43 | 44 | 49 | 50 |
51 | 88 |
89 | 90 |
91 |
92 |

UICollectionViewController

93 |

A convenience extension for UICollectionViewController which defaults the collectionView

94 | 95 |
96 |
97 | 98 |
99 |
100 |
101 |
    102 |
  • 103 |
    104 | 105 | 106 | 107 | reloadEmptyState() 108 | 109 |
    110 |
    111 |
    112 |
    113 |
    114 |
    115 |

    Reloads the empty state, defaults the collectionView to self.collectionView

    116 | 117 |
    118 |
    119 |

    Declaration

    120 |
    121 |

    Swift

    122 |
    public func reloadEmptyState()
    123 | 124 |
    125 |
    126 |
    127 |
    128 |
  • 129 |
130 |
131 |
132 |
133 | 134 |
135 |
136 | 140 | 141 | 142 | 143 | -------------------------------------------------------------------------------- /docs/Extensions/UITableViewController.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | UITableViewController Extension Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |

23 | 24 | UIEmptyState Docs 25 | 26 | (100% documented) 27 |

28 | 29 |

30 |

31 | 32 |
33 |

34 | 35 |

36 | 37 | 38 | View on GitHub 39 | 40 |

41 | 42 |
43 | 44 | 49 | 50 |
51 | 88 |
89 | 90 |
91 |
92 |

UITableViewController

93 |

A convenience extension for UITableViewController which defaults the tableView

94 | 95 |
96 |
97 | 98 |
99 |
100 |
101 |
    102 |
  • 103 |
    104 | 105 | 106 | 107 | reloadEmptyState() 108 | 109 |
    110 |
    111 |
    112 |
    113 |
    114 |
    115 |

    Reloads the empty state, defaults the tableView to self.tableView

    116 | 117 |
    118 |
    119 |

    Declaration

    120 |
    121 |

    Swift

    122 |
    public func reloadEmptyState()
    123 | 124 |
    125 |
    126 |
    127 |
    128 |
  • 129 |
130 |
131 |
132 |
133 | 134 |
135 |
136 | 140 | 141 | 142 | 143 | -------------------------------------------------------------------------------- /docs/Extensions/UIViewController.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | UIViewController Extension Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |

23 | 24 | UIEmptyState Docs 25 | 26 | (100% documented) 27 |

28 | 29 |

30 |

31 | 32 |
33 |

34 | 35 |

36 | 37 | 38 | View on GitHub 39 | 40 |

41 | 42 |
43 | 44 | 49 | 50 |
51 | 88 |
89 | 90 |
91 |
92 |

UIViewController

93 |

Extension on UIViewController which adds method and computed properties in order to allow empty view creation

94 | 95 |
96 |
97 | 98 |
99 |
100 |
101 |
    102 |
  • 103 |
    104 | 105 | 106 | 107 | emptyStateDataSource 108 | 109 |
    110 |
    111 |
    112 |
    113 |
    114 |
    115 |

    The data source for the Empty View

    116 | 117 |

    Default conformance for UIViewController is provided, 118 | however feel free to implement these methods to customize your view.

    119 | 120 |
    121 |
    122 |

    Declaration

    123 |
    124 |

    Swift

    125 |
    public weak var emptyStateDataSource: UIEmptyStateDataSource?
    126 | 127 |
    128 |
    129 |
    130 |
    131 |
  • 132 |
  • 133 |
    134 | 135 | 136 | 137 | emptyStateDelegate 138 | 139 |
    140 |
    141 |
    142 |
    143 |
    144 |
    145 |

    The delegate for UIEmptyStateView

    146 | 147 |

    Important: this delegate and its functions are only used when using UIEmptyStateView. 148 | If you will provide a custom view in the UIEmptyStateDataSource you must handle how this delegate operates

    149 | 150 |
    151 |
    152 |

    Declaration

    153 |
    154 |

    Swift

    155 |
    public weak var emptyStateDelegate: UIEmptyStateDelegate?
    156 | 157 |
    158 |
    159 |
    160 |
    161 |
  • 162 |
  • 163 |
    164 | 165 | 166 | 167 | reloadEmptyStateForTableView(_:) 168 | 169 |
    170 |
    171 |
    172 |
    173 |
    174 |
    175 |

    The method responsible for show and hiding the UIEmptyStateDataSource.viewForEmptyState view

    176 | 177 |

    Important: 178 | This should be called whenever changes are made to the tableView data source or after reloading the tableview

    179 | 180 |

    Do NOT override this method/implement it unless you need custom behavior and know what you are doing.

    181 | 182 |
    183 |
    184 |

    Declaration

    185 |
    186 |

    Swift

    187 |
    public func reloadEmptyStateForTableView(_ tableView: UITableView)
    188 | 189 |
    190 |
    191 |
    192 |
    193 |
  • 194 |
  • 195 |
    196 | 197 | 198 | 199 | reloadEmptyStateForCollectionView(_:) 200 | 201 |
    202 |
    203 |
    204 |
    205 |
    206 |
    207 |

    The method responsible for show and hiding the UIEmptyStateDataSource.viewForEmptyState view

    208 | 209 |

    Important: 210 | This should be called whenever changes are made to the collection view data source or after reloading the collection view.

    211 | 212 |

    Do NOT override this method/implement it unless you need custom behavior and know what you are doing.

    213 | 214 |
    215 |
    216 |

    Declaration

    217 |
    218 |

    Swift

    219 |
    public func reloadEmptyStateForCollectionView(_ collectionView: UICollectionView)
    220 | 221 |
    222 |
    223 |
    224 |
    225 |
  • 226 |
227 |
228 |
229 |
230 | 231 |
232 |
233 | 237 | 238 | 239 | 240 | -------------------------------------------------------------------------------- /docs/Protocols.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Protocols Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 |

22 | 23 | UIEmptyState Docs 24 | 25 | (100% documented) 26 |

27 | 28 |

29 |

30 | 31 |
32 |

33 | 34 |

35 | 36 | 37 | View on GitHub 38 | 39 |

40 | 41 |
42 | 43 | 48 | 49 |
50 | 87 |
88 | 89 |
90 |
91 |

Protocols

92 |

The following protocols are available globally.

93 | 94 |
95 |
96 | 97 |
98 |
99 |
100 |
    101 |
  • 102 |
    103 | 104 | 105 | 106 | UIEmptyStateDataSource 107 | 108 |
    109 |
    110 |
    111 |
    112 |
    113 |
    114 |

    The data source for the Empty View

    115 | 116 |

    Default conformance for UIViewContoller is provided, 117 | however feel free to implement these methods to customize your view.

    118 | 119 | See more 120 |
    121 |
    122 |

    Declaration

    123 |
    124 |

    Swift

    125 |
    public protocol UIEmptyStateDataSource: class
    126 | 127 |
    128 |
    129 |
    130 |
    131 |
  • 132 |
133 |
134 |
135 |
    136 |
  • 137 |
    138 | 139 | 140 | 141 | UIEmptyStateDelegate 142 | 143 |
    144 |
    145 |
    146 |
    147 |
    148 |
    149 |

    The delegate for UIEmptyStateView

    150 | 151 |

    Important: 152 | This delegate and its functions are only used when using UIEmptyStateView. 153 | If you will provide a custom view in the UIEmptyStateDataSource viewForEmptyState 154 | you must handle how this delegate operates

    155 | 156 | See more 157 |
    158 |
    159 |

    Declaration

    160 |
    161 |

    Swift

    162 |
    public protocol UIEmptyStateDelegate: class
    163 | 164 |
    165 |
    166 |
    167 |
    168 |
  • 169 |
170 |
171 |
172 |
173 | 174 |
175 |
176 | 180 | 181 | 182 | 183 | -------------------------------------------------------------------------------- /docs/badge.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | documentation 17 | 18 | 19 | documentation 20 | 21 | 22 | 100% 23 | 24 | 25 | 100% 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /docs/css/highlight.css: -------------------------------------------------------------------------------- 1 | /* Credit to https://gist.github.com/wataru420/2048287 */ 2 | .highlight { 3 | /* Comment */ 4 | /* Error */ 5 | /* Keyword */ 6 | /* Operator */ 7 | /* Comment.Multiline */ 8 | /* Comment.Preproc */ 9 | /* Comment.Single */ 10 | /* Comment.Special */ 11 | /* Generic.Deleted */ 12 | /* Generic.Deleted.Specific */ 13 | /* Generic.Emph */ 14 | /* Generic.Error */ 15 | /* Generic.Heading */ 16 | /* Generic.Inserted */ 17 | /* Generic.Inserted.Specific */ 18 | /* Generic.Output */ 19 | /* Generic.Prompt */ 20 | /* Generic.Strong */ 21 | /* Generic.Subheading */ 22 | /* Generic.Traceback */ 23 | /* Keyword.Constant */ 24 | /* Keyword.Declaration */ 25 | /* Keyword.Pseudo */ 26 | /* Keyword.Reserved */ 27 | /* Keyword.Type */ 28 | /* Literal.Number */ 29 | /* Literal.String */ 30 | /* Name.Attribute */ 31 | /* Name.Builtin */ 32 | /* Name.Class */ 33 | /* Name.Constant */ 34 | /* Name.Entity */ 35 | /* Name.Exception */ 36 | /* Name.Function */ 37 | /* Name.Namespace */ 38 | /* Name.Tag */ 39 | /* Name.Variable */ 40 | /* Operator.Word */ 41 | /* Text.Whitespace */ 42 | /* Literal.Number.Float */ 43 | /* Literal.Number.Hex */ 44 | /* Literal.Number.Integer */ 45 | /* Literal.Number.Oct */ 46 | /* Literal.String.Backtick */ 47 | /* Literal.String.Char */ 48 | /* Literal.String.Doc */ 49 | /* Literal.String.Double */ 50 | /* Literal.String.Escape */ 51 | /* Literal.String.Heredoc */ 52 | /* Literal.String.Interpol */ 53 | /* Literal.String.Other */ 54 | /* Literal.String.Regex */ 55 | /* Literal.String.Single */ 56 | /* Literal.String.Symbol */ 57 | /* Name.Builtin.Pseudo */ 58 | /* Name.Variable.Class */ 59 | /* Name.Variable.Global */ 60 | /* Name.Variable.Instance */ 61 | /* Literal.Number.Integer.Long */ } 62 | .highlight .c { 63 | color: #999988; 64 | font-style: italic; } 65 | .highlight .err { 66 | color: #a61717; 67 | background-color: #e3d2d2; } 68 | .highlight .k { 69 | color: #000000; 70 | font-weight: bold; } 71 | .highlight .o { 72 | color: #000000; 73 | font-weight: bold; } 74 | .highlight .cm { 75 | color: #999988; 76 | font-style: italic; } 77 | .highlight .cp { 78 | color: #999999; 79 | font-weight: bold; } 80 | .highlight .c1 { 81 | color: #999988; 82 | font-style: italic; } 83 | .highlight .cs { 84 | color: #999999; 85 | font-weight: bold; 86 | font-style: italic; } 87 | .highlight .gd { 88 | color: #000000; 89 | background-color: #ffdddd; } 90 | .highlight .gd .x { 91 | color: #000000; 92 | background-color: #ffaaaa; } 93 | .highlight .ge { 94 | color: #000000; 95 | font-style: italic; } 96 | .highlight .gr { 97 | color: #aa0000; } 98 | .highlight .gh { 99 | color: #999999; } 100 | .highlight .gi { 101 | color: #000000; 102 | background-color: #ddffdd; } 103 | .highlight .gi .x { 104 | color: #000000; 105 | background-color: #aaffaa; } 106 | .highlight .go { 107 | color: #888888; } 108 | .highlight .gp { 109 | color: #555555; } 110 | .highlight .gs { 111 | font-weight: bold; } 112 | .highlight .gu { 113 | color: #aaaaaa; } 114 | .highlight .gt { 115 | color: #aa0000; } 116 | .highlight .kc { 117 | color: #000000; 118 | font-weight: bold; } 119 | .highlight .kd { 120 | color: #000000; 121 | font-weight: bold; } 122 | .highlight .kp { 123 | color: #000000; 124 | font-weight: bold; } 125 | .highlight .kr { 126 | color: #000000; 127 | font-weight: bold; } 128 | .highlight .kt { 129 | color: #445588; } 130 | .highlight .m { 131 | color: #009999; } 132 | .highlight .s { 133 | color: #d14; } 134 | .highlight .na { 135 | color: #008080; } 136 | .highlight .nb { 137 | color: #0086B3; } 138 | .highlight .nc { 139 | color: #445588; 140 | font-weight: bold; } 141 | .highlight .no { 142 | color: #008080; } 143 | .highlight .ni { 144 | color: #800080; } 145 | .highlight .ne { 146 | color: #990000; 147 | font-weight: bold; } 148 | .highlight .nf { 149 | color: #990000; } 150 | .highlight .nn { 151 | color: #555555; } 152 | .highlight .nt { 153 | color: #000080; } 154 | .highlight .nv { 155 | color: #008080; } 156 | .highlight .ow { 157 | color: #000000; 158 | font-weight: bold; } 159 | .highlight .w { 160 | color: #bbbbbb; } 161 | .highlight .mf { 162 | color: #009999; } 163 | .highlight .mh { 164 | color: #009999; } 165 | .highlight .mi { 166 | color: #009999; } 167 | .highlight .mo { 168 | color: #009999; } 169 | .highlight .sb { 170 | color: #d14; } 171 | .highlight .sc { 172 | color: #d14; } 173 | .highlight .sd { 174 | color: #d14; } 175 | .highlight .s2 { 176 | color: #d14; } 177 | .highlight .se { 178 | color: #d14; } 179 | .highlight .sh { 180 | color: #d14; } 181 | .highlight .si { 182 | color: #d14; } 183 | .highlight .sx { 184 | color: #d14; } 185 | .highlight .sr { 186 | color: #009926; } 187 | .highlight .s1 { 188 | color: #d14; } 189 | .highlight .ss { 190 | color: #990073; } 191 | .highlight .bp { 192 | color: #999999; } 193 | .highlight .vc { 194 | color: #008080; } 195 | .highlight .vg { 196 | color: #008080; } 197 | .highlight .vi { 198 | color: #008080; } 199 | .highlight .il { 200 | color: #009999; } 201 | -------------------------------------------------------------------------------- /docs/css/jazzy.css: -------------------------------------------------------------------------------- 1 | *, *:before, *:after { 2 | box-sizing: inherit; } 3 | 4 | body { 5 | margin: 0; 6 | background: #fff; 7 | color: #333; 8 | font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; 9 | letter-spacing: .2px; 10 | -webkit-font-smoothing: antialiased; 11 | box-sizing: border-box; } 12 | 13 | h1 { 14 | font-size: 2rem; 15 | font-weight: 700; 16 | margin: 1.275em 0 0.6em; } 17 | 18 | h2 { 19 | font-size: 1.75rem; 20 | font-weight: 700; 21 | margin: 1.275em 0 0.3em; } 22 | 23 | h3 { 24 | font-size: 1.5rem; 25 | font-weight: 700; 26 | margin: 1em 0 0.3em; } 27 | 28 | h4 { 29 | font-size: 1.25rem; 30 | font-weight: 700; 31 | margin: 1.275em 0 0.85em; } 32 | 33 | h5 { 34 | font-size: 1rem; 35 | font-weight: 700; 36 | margin: 1.275em 0 0.85em; } 37 | 38 | h6 { 39 | font-size: 1rem; 40 | font-weight: 700; 41 | margin: 1.275em 0 0.85em; 42 | color: #777; } 43 | 44 | p { 45 | margin: 0 0 1em; } 46 | 47 | ul, ol { 48 | padding: 0 0 0 2em; 49 | margin: 0 0 0.85em; } 50 | 51 | blockquote { 52 | margin: 0 0 0.85em; 53 | padding: 0 15px; 54 | color: #858585; 55 | border-left: 4px solid #e5e5e5; } 56 | 57 | img { 58 | max-width: 100%; } 59 | 60 | a { 61 | color: #4183c4; 62 | text-decoration: none; } 63 | a:hover, a:focus { 64 | outline: 0; 65 | text-decoration: underline; } 66 | 67 | table { 68 | background: #fff; 69 | width: 100%; 70 | border-collapse: collapse; 71 | border-spacing: 0; 72 | overflow: auto; 73 | margin: 0 0 0.85em; } 74 | 75 | tr:nth-child(2n) { 76 | background-color: #fbfbfb; } 77 | 78 | th, td { 79 | padding: 6px 13px; 80 | border: 1px solid #ddd; } 81 | 82 | pre { 83 | margin: 0 0 1.275em; 84 | padding: .85em 1em; 85 | overflow: auto; 86 | background: #f7f7f7; 87 | font-size: .85em; 88 | font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; } 89 | 90 | code { 91 | font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; } 92 | 93 | p > code, li > code { 94 | background: #f7f7f7; 95 | padding: .2em; } 96 | p > code:before, p > code:after, li > code:before, li > code:after { 97 | letter-spacing: -.2em; 98 | content: "\00a0"; } 99 | 100 | pre code { 101 | padding: 0; 102 | white-space: pre; } 103 | 104 | .content-wrapper { 105 | display: flex; 106 | flex-direction: column; } 107 | @media (min-width: 768px) { 108 | .content-wrapper { 109 | flex-direction: row; } } 110 | 111 | .header { 112 | display: flex; 113 | padding: 8px; 114 | font-size: 0.875em; 115 | background: #444; 116 | color: #999; } 117 | 118 | .header-col { 119 | margin: 0; 120 | padding: 0 8px; } 121 | 122 | .header-col--primary { 123 | flex: 1; } 124 | 125 | .header-link { 126 | color: #fff; } 127 | 128 | .header-icon { 129 | padding-right: 6px; 130 | vertical-align: -4px; 131 | height: 16px; } 132 | 133 | .breadcrumbs { 134 | font-size: 0.875em; 135 | padding: 8px 16px; 136 | margin: 0; 137 | background: #fbfbfb; 138 | border-bottom: 1px solid #ddd; } 139 | 140 | .carat { 141 | height: 10px; 142 | margin: 0 5px; } 143 | 144 | .navigation { 145 | order: 2; } 146 | @media (min-width: 768px) { 147 | .navigation { 148 | order: 1; 149 | width: 25%; 150 | max-width: 300px; 151 | padding-bottom: 64px; 152 | overflow: hidden; 153 | word-wrap: normal; 154 | background: #fbfbfb; 155 | border-right: 1px solid #ddd; } } 156 | 157 | .nav-groups { 158 | list-style-type: none; 159 | padding-left: 0; } 160 | 161 | .nav-group-name { 162 | border-bottom: 1px solid #ddd; 163 | padding: 8px 0 8px 16px; } 164 | 165 | .nav-group-name-link { 166 | color: #333; } 167 | 168 | .nav-group-tasks { 169 | margin: 8px 0; 170 | padding: 0 0 0 8px; } 171 | 172 | .nav-group-task { 173 | font-size: 1em; 174 | list-style-type: none; 175 | white-space: nowrap; } 176 | 177 | .nav-group-task-link { 178 | color: #808080; } 179 | 180 | .main-content { 181 | order: 1; } 182 | @media (min-width: 768px) { 183 | .main-content { 184 | order: 2; 185 | flex: 1; 186 | padding-bottom: 60px; } } 187 | 188 | .section { 189 | padding: 0 32px; 190 | border-bottom: 1px solid #ddd; } 191 | 192 | .section-content { 193 | max-width: 834px; 194 | margin: 0 auto; 195 | padding: 16px 0; } 196 | 197 | .section-name { 198 | color: #666; 199 | display: block; } 200 | 201 | .declaration .highlight { 202 | overflow-x: initial; 203 | padding: 8px 0; 204 | margin: 0; 205 | background-color: transparent; 206 | border: none; } 207 | 208 | .task-group-section { 209 | border-top: 1px solid #ddd; } 210 | 211 | .task-group { 212 | padding-top: 0px; } 213 | 214 | .task-name-container a[name]:before { 215 | content: ""; 216 | display: block; } 217 | 218 | .item-container { 219 | padding: 0; } 220 | 221 | .item { 222 | padding-top: 8px; 223 | width: 100%; 224 | list-style-type: none; } 225 | .item a[name]:before { 226 | content: ""; 227 | display: block; } 228 | .item .token { 229 | padding-left: 3px; 230 | margin-left: 0px; 231 | font-size: 1rem; } 232 | .item .declaration-note { 233 | font-size: .85em; 234 | color: #808080; 235 | font-style: italic; } 236 | 237 | .pointer-container { 238 | border-bottom: 1px solid #ddd; 239 | left: -23px; 240 | padding-bottom: 13px; 241 | position: relative; 242 | width: 110%; } 243 | 244 | .pointer { 245 | left: 21px; 246 | top: 7px; 247 | display: block; 248 | position: absolute; 249 | width: 12px; 250 | height: 12px; 251 | border-left: 1px solid #ddd; 252 | border-top: 1px solid #ddd; 253 | background: #fff; 254 | transform: rotate(45deg); } 255 | 256 | .height-container { 257 | display: none; 258 | position: relative; 259 | width: 100%; 260 | overflow: hidden; } 261 | .height-container .section { 262 | background: #fff; 263 | border: 1px solid #ddd; 264 | border-top-width: 0; 265 | padding-top: 10px; 266 | padding-bottom: 5px; 267 | padding: 8px 16px; } 268 | 269 | .aside, .language { 270 | padding: 6px 12px; 271 | margin: 12px 0; 272 | border-left: 5px solid #dddddd; 273 | overflow-y: hidden; } 274 | .aside .aside-title, .language .aside-title { 275 | font-size: 9px; 276 | letter-spacing: 2px; 277 | text-transform: uppercase; 278 | padding-bottom: 0; 279 | margin: 0; 280 | color: #aaa; 281 | -webkit-user-select: none; } 282 | .aside p:last-child, .language p:last-child { 283 | margin-bottom: 0; } 284 | 285 | .language { 286 | border-left: 5px solid #cde9f4; } 287 | .language .aside-title { 288 | color: #4183c4; } 289 | 290 | .aside-warning { 291 | border-left: 5px solid #ff6666; } 292 | .aside-warning .aside-title { 293 | color: #ff0000; } 294 | 295 | .graybox { 296 | border-collapse: collapse; 297 | width: 100%; } 298 | .graybox p { 299 | margin: 0; 300 | word-break: break-word; 301 | min-width: 50px; } 302 | .graybox td { 303 | border: 1px solid #ddd; 304 | padding: 5px 25px 5px 10px; 305 | vertical-align: middle; } 306 | .graybox tr td:first-of-type { 307 | text-align: right; 308 | padding: 7px; 309 | vertical-align: top; 310 | word-break: normal; 311 | width: 40px; } 312 | 313 | .slightly-smaller { 314 | font-size: 0.9em; } 315 | 316 | .footer { 317 | padding: 8px 16px; 318 | background: #444; 319 | color: #ddd; 320 | font-size: 0.8em; } 321 | .footer p { 322 | margin: 8px 0; } 323 | .footer a { 324 | color: #fff; } 325 | 326 | html.dash .header, html.dash .breadcrumbs, html.dash .navigation { 327 | display: none; } 328 | html.dash .height-container { 329 | display: block; } 330 | 331 | form[role=search] input { 332 | font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; 333 | font-size: 14px; 334 | line-height: 24px; 335 | padding: 0 10px; 336 | margin: 0; 337 | border: none; 338 | border-radius: 1em; } 339 | .loading form[role=search] input { 340 | background: white url(../img/spinner.gif) center right 4px no-repeat; } 341 | form[role=search] .tt-menu { 342 | margin: 0; 343 | min-width: 300px; 344 | background: #fbfbfb; 345 | color: #333; 346 | border: 1px solid #ddd; } 347 | form[role=search] .tt-highlight { 348 | font-weight: bold; } 349 | form[role=search] .tt-suggestion { 350 | font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; 351 | padding: 0 8px; } 352 | form[role=search] .tt-suggestion span { 353 | display: table-cell; 354 | white-space: nowrap; } 355 | form[role=search] .tt-suggestion .doc-parent-name { 356 | width: 100%; 357 | text-align: right; 358 | font-weight: normal; 359 | font-size: 0.9em; 360 | padding-left: 16px; } 361 | form[role=search] .tt-suggestion:hover, 362 | form[role=search] .tt-suggestion.tt-cursor { 363 | cursor: pointer; 364 | background-color: #4183c4; 365 | color: #fff; } 366 | form[role=search] .tt-suggestion:hover .doc-parent-name, 367 | form[role=search] .tt-suggestion.tt-cursor .doc-parent-name { 368 | color: #fff; } 369 | -------------------------------------------------------------------------------- /docs/docsets/UIEmptyState.docset/Contents/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleIdentifier 6 | com.jazzy.uiemptystate 7 | CFBundleName 8 | UIEmptyState 9 | DocSetPlatformFamily 10 | uiemptystate 11 | isDashDocset 12 | 13 | dashIndexFilePath 14 | index.html 15 | isJavaScriptEnabled 16 | 17 | DashDocSetFamily 18 | dashtoc 19 | 20 | 21 | -------------------------------------------------------------------------------- /docs/docsets/UIEmptyState.docset/Contents/Resources/Documents/Classes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Classes Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 |

22 | 23 | UIEmptyState Docs 24 | 25 | (100% documented) 26 |

27 | 28 |

29 |

30 | 31 |
32 |

33 | 34 |

35 | 36 | 37 | View on GitHub 38 | 39 |

40 | 41 |
42 | 43 | 48 | 49 |
50 | 87 |
88 | 89 |
90 |
91 |

Classes

92 |

The following classes are available globally.

93 | 94 |
95 |
96 | 97 |
98 |
99 |
100 |
    101 |
  • 102 |
    103 | 104 | 105 | 106 | UIEmptyStateView 107 | 108 |
    109 |
    110 |
    111 |
    112 |
    113 |
    114 |

    A UIView which has a stack view and inside the stackview are 1-4 other views 115 | This view is used as the default view for the emptyStateView in the UIEmptyStateDataSource

    116 | 117 | See more 118 |
    119 |
    120 |

    Declaration

    121 |
    122 |

    Swift

    123 |
    open class UIEmptyStateView: UIView
    124 | 125 |
    126 |
    127 |
    128 |
    129 |
  • 130 |
131 |
132 |
133 |
134 | 135 |
136 |
137 | 141 | 142 | 143 | 144 | -------------------------------------------------------------------------------- /docs/docsets/UIEmptyState.docset/Contents/Resources/Documents/Extensions.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Extensions Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 |

22 | 23 | UIEmptyState Docs 24 | 25 | (100% documented) 26 |

27 | 28 |

29 |

30 | 31 |
32 |

33 | 34 |

35 | 36 | 37 | View on GitHub 38 | 39 |

40 | 41 |
42 | 43 | 48 | 49 |
50 | 87 |
88 | 89 |
90 |
91 |

Extensions

92 |

The following extensions are available globally.

93 | 94 |
95 |
96 | 97 |
98 |
99 |
100 |
    101 |
  • 102 |
    103 | 104 | 105 | 106 | UIViewController 107 | 108 |
    109 |
    110 |
    111 |
    112 |
    113 |
    114 |

    Extension on UIViewController which adds method and computed properties in order to allow empty view creation

    115 | 116 | See more 117 |
    118 |
    119 |
    120 |
  • 121 |
  • 122 |
    123 | 124 | 125 | 126 | UITableViewController 127 | 128 |
    129 |
    130 |
    131 |
    132 |
    133 |
    134 |

    A convenience extension for UITableViewController which defaults the tableView

    135 | 136 | See more 137 |
    138 |
    139 |
    140 |
  • 141 |
  • 142 |
    143 | 144 | 145 | 146 | UICollectionViewController 147 | 148 |
    149 |
    150 |
    151 |
    152 |
    153 |
    154 |

    A convenience extension for UICollectionViewController which defaults the collectionView

    155 | 156 | See more 157 |
    158 |
    159 |
    160 |
  • 161 |
162 |
163 |
164 |
165 | 166 |
167 |
168 | 172 | 173 | 174 | 175 | -------------------------------------------------------------------------------- /docs/docsets/UIEmptyState.docset/Contents/Resources/Documents/Extensions/UICollectionViewController.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | UICollectionViewController Extension Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |

23 | 24 | UIEmptyState Docs 25 | 26 | (100% documented) 27 |

28 | 29 |

30 |

31 | 32 |
33 |

34 | 35 |

36 | 37 | 38 | View on GitHub 39 | 40 |

41 | 42 |
43 | 44 | 49 | 50 |
51 | 88 |
89 | 90 |
91 |
92 |

UICollectionViewController

93 |

A convenience extension for UICollectionViewController which defaults the collectionView

94 | 95 |
96 |
97 | 98 |
99 |
100 |
101 |
    102 |
  • 103 |
    104 | 105 | 106 | 107 | reloadEmptyState() 108 | 109 |
    110 |
    111 |
    112 |
    113 |
    114 |
    115 |

    Reloads the empty state, defaults the collectionView to self.collectionView

    116 | 117 |
    118 |
    119 |

    Declaration

    120 |
    121 |

    Swift

    122 |
    public func reloadEmptyState()
    123 | 124 |
    125 |
    126 |
    127 |
    128 |
  • 129 |
130 |
131 |
132 |
133 | 134 |
135 |
136 | 140 | 141 | 142 | 143 | -------------------------------------------------------------------------------- /docs/docsets/UIEmptyState.docset/Contents/Resources/Documents/Extensions/UITableViewController.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | UITableViewController Extension Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |

23 | 24 | UIEmptyState Docs 25 | 26 | (100% documented) 27 |

28 | 29 |

30 |

31 | 32 |
33 |

34 | 35 |

36 | 37 | 38 | View on GitHub 39 | 40 |

41 | 42 |
43 | 44 | 49 | 50 |
51 | 88 |
89 | 90 |
91 |
92 |

UITableViewController

93 |

A convenience extension for UITableViewController which defaults the tableView

94 | 95 |
96 |
97 | 98 |
99 |
100 |
101 |
    102 |
  • 103 |
    104 | 105 | 106 | 107 | reloadEmptyState() 108 | 109 |
    110 |
    111 |
    112 |
    113 |
    114 |
    115 |

    Reloads the empty state, defaults the tableView to self.tableView

    116 | 117 |
    118 |
    119 |

    Declaration

    120 |
    121 |

    Swift

    122 |
    public func reloadEmptyState()
    123 | 124 |
    125 |
    126 |
    127 |
    128 |
  • 129 |
130 |
131 |
132 |
133 | 134 |
135 |
136 | 140 | 141 | 142 | 143 | -------------------------------------------------------------------------------- /docs/docsets/UIEmptyState.docset/Contents/Resources/Documents/Extensions/UIViewController.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | UIViewController Extension Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |

23 | 24 | UIEmptyState Docs 25 | 26 | (100% documented) 27 |

28 | 29 |

30 |

31 | 32 |
33 |

34 | 35 |

36 | 37 | 38 | View on GitHub 39 | 40 |

41 | 42 |
43 | 44 | 49 | 50 |
51 | 88 |
89 | 90 |
91 |
92 |

UIViewController

93 |

Extension on UIViewController which adds method and computed properties in order to allow empty view creation

94 | 95 |
96 |
97 | 98 |
99 |
100 |
101 |
    102 |
  • 103 |
    104 | 105 | 106 | 107 | emptyStateDataSource 108 | 109 |
    110 |
    111 |
    112 |
    113 |
    114 |
    115 |

    The data source for the Empty View

    116 | 117 |

    Default conformance for UIViewController is provided, 118 | however feel free to implement these methods to customize your view.

    119 | 120 |
    121 |
    122 |

    Declaration

    123 |
    124 |

    Swift

    125 |
    public weak var emptyStateDataSource: UIEmptyStateDataSource?
    126 | 127 |
    128 |
    129 |
    130 |
    131 |
  • 132 |
  • 133 |
    134 | 135 | 136 | 137 | emptyStateDelegate 138 | 139 |
    140 |
    141 |
    142 |
    143 |
    144 |
    145 |

    The delegate for UIEmptyStateView

    146 | 147 |

    Important: this delegate and its functions are only used when using UIEmptyStateView. 148 | If you will provide a custom view in the UIEmptyStateDataSource you must handle how this delegate operates

    149 | 150 |
    151 |
    152 |

    Declaration

    153 |
    154 |

    Swift

    155 |
    public weak var emptyStateDelegate: UIEmptyStateDelegate?
    156 | 157 |
    158 |
    159 |
    160 |
    161 |
  • 162 |
  • 163 |
    164 | 165 | 166 | 167 | reloadEmptyStateForTableView(_:) 168 | 169 |
    170 |
    171 |
    172 |
    173 |
    174 |
    175 |

    The method responsible for show and hiding the UIEmptyStateDataSource.viewForEmptyState view

    176 | 177 |

    Important: 178 | This should be called whenever changes are made to the tableView data source or after reloading the tableview

    179 | 180 |

    Do NOT override this method/implement it unless you need custom behavior and know what you are doing.

    181 | 182 |
    183 |
    184 |

    Declaration

    185 |
    186 |

    Swift

    187 |
    public func reloadEmptyStateForTableView(_ tableView: UITableView)
    188 | 189 |
    190 |
    191 |
    192 |
    193 |
  • 194 |
  • 195 |
    196 | 197 | 198 | 199 | reloadEmptyStateForCollectionView(_:) 200 | 201 |
    202 |
    203 |
    204 |
    205 |
    206 |
    207 |

    The method responsible for show and hiding the UIEmptyStateDataSource.viewForEmptyState view

    208 | 209 |

    Important: 210 | This should be called whenever changes are made to the collection view data source or after reloading the collection view.

    211 | 212 |

    Do NOT override this method/implement it unless you need custom behavior and know what you are doing.

    213 | 214 |
    215 |
    216 |

    Declaration

    217 |
    218 |

    Swift

    219 |
    public func reloadEmptyStateForCollectionView(_ collectionView: UICollectionView)
    220 | 221 |
    222 |
    223 |
    224 |
    225 |
  • 226 |
227 |
228 |
229 |
230 | 231 |
232 |
233 | 237 | 238 | 239 | 240 | -------------------------------------------------------------------------------- /docs/docsets/UIEmptyState.docset/Contents/Resources/Documents/Protocols.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Protocols Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 |

22 | 23 | UIEmptyState Docs 24 | 25 | (100% documented) 26 |

27 | 28 |

29 |

30 | 31 |
32 |

33 | 34 |

35 | 36 | 37 | View on GitHub 38 | 39 |

40 | 41 |
42 | 43 | 48 | 49 |
50 | 87 |
88 | 89 |
90 |
91 |

Protocols

92 |

The following protocols are available globally.

93 | 94 |
95 |
96 | 97 |
98 |
99 |
100 |
    101 |
  • 102 |
    103 | 104 | 105 | 106 | UIEmptyStateDataSource 107 | 108 |
    109 |
    110 |
    111 |
    112 |
    113 |
    114 |

    The data source for the Empty View

    115 | 116 |

    Default conformance for UIViewContoller is provided, 117 | however feel free to implement these methods to customize your view.

    118 | 119 | See more 120 |
    121 |
    122 |

    Declaration

    123 |
    124 |

    Swift

    125 |
    public protocol UIEmptyStateDataSource: class
    126 | 127 |
    128 |
    129 |
    130 |
    131 |
  • 132 |
133 |
134 |
135 |
    136 |
  • 137 |
    138 | 139 | 140 | 141 | UIEmptyStateDelegate 142 | 143 |
    144 |
    145 |
    146 |
    147 |
    148 |
    149 |

    The delegate for UIEmptyStateView

    150 | 151 |

    Important: 152 | This delegate and its functions are only used when using UIEmptyStateView. 153 | If you will provide a custom view in the UIEmptyStateDataSource viewForEmptyState 154 | you must handle how this delegate operates

    155 | 156 | See more 157 |
    158 |
    159 |

    Declaration

    160 |
    161 |

    Swift

    162 |
    public protocol UIEmptyStateDelegate: class
    163 | 164 |
    165 |
    166 |
    167 |
    168 |
  • 169 |
170 |
171 |
172 |
173 | 174 |
175 |
176 | 180 | 181 | 182 | 183 | -------------------------------------------------------------------------------- /docs/docsets/UIEmptyState.docset/Contents/Resources/Documents/css/highlight.css: -------------------------------------------------------------------------------- 1 | /* Credit to https://gist.github.com/wataru420/2048287 */ 2 | .highlight { 3 | /* Comment */ 4 | /* Error */ 5 | /* Keyword */ 6 | /* Operator */ 7 | /* Comment.Multiline */ 8 | /* Comment.Preproc */ 9 | /* Comment.Single */ 10 | /* Comment.Special */ 11 | /* Generic.Deleted */ 12 | /* Generic.Deleted.Specific */ 13 | /* Generic.Emph */ 14 | /* Generic.Error */ 15 | /* Generic.Heading */ 16 | /* Generic.Inserted */ 17 | /* Generic.Inserted.Specific */ 18 | /* Generic.Output */ 19 | /* Generic.Prompt */ 20 | /* Generic.Strong */ 21 | /* Generic.Subheading */ 22 | /* Generic.Traceback */ 23 | /* Keyword.Constant */ 24 | /* Keyword.Declaration */ 25 | /* Keyword.Pseudo */ 26 | /* Keyword.Reserved */ 27 | /* Keyword.Type */ 28 | /* Literal.Number */ 29 | /* Literal.String */ 30 | /* Name.Attribute */ 31 | /* Name.Builtin */ 32 | /* Name.Class */ 33 | /* Name.Constant */ 34 | /* Name.Entity */ 35 | /* Name.Exception */ 36 | /* Name.Function */ 37 | /* Name.Namespace */ 38 | /* Name.Tag */ 39 | /* Name.Variable */ 40 | /* Operator.Word */ 41 | /* Text.Whitespace */ 42 | /* Literal.Number.Float */ 43 | /* Literal.Number.Hex */ 44 | /* Literal.Number.Integer */ 45 | /* Literal.Number.Oct */ 46 | /* Literal.String.Backtick */ 47 | /* Literal.String.Char */ 48 | /* Literal.String.Doc */ 49 | /* Literal.String.Double */ 50 | /* Literal.String.Escape */ 51 | /* Literal.String.Heredoc */ 52 | /* Literal.String.Interpol */ 53 | /* Literal.String.Other */ 54 | /* Literal.String.Regex */ 55 | /* Literal.String.Single */ 56 | /* Literal.String.Symbol */ 57 | /* Name.Builtin.Pseudo */ 58 | /* Name.Variable.Class */ 59 | /* Name.Variable.Global */ 60 | /* Name.Variable.Instance */ 61 | /* Literal.Number.Integer.Long */ } 62 | .highlight .c { 63 | color: #999988; 64 | font-style: italic; } 65 | .highlight .err { 66 | color: #a61717; 67 | background-color: #e3d2d2; } 68 | .highlight .k { 69 | color: #000000; 70 | font-weight: bold; } 71 | .highlight .o { 72 | color: #000000; 73 | font-weight: bold; } 74 | .highlight .cm { 75 | color: #999988; 76 | font-style: italic; } 77 | .highlight .cp { 78 | color: #999999; 79 | font-weight: bold; } 80 | .highlight .c1 { 81 | color: #999988; 82 | font-style: italic; } 83 | .highlight .cs { 84 | color: #999999; 85 | font-weight: bold; 86 | font-style: italic; } 87 | .highlight .gd { 88 | color: #000000; 89 | background-color: #ffdddd; } 90 | .highlight .gd .x { 91 | color: #000000; 92 | background-color: #ffaaaa; } 93 | .highlight .ge { 94 | color: #000000; 95 | font-style: italic; } 96 | .highlight .gr { 97 | color: #aa0000; } 98 | .highlight .gh { 99 | color: #999999; } 100 | .highlight .gi { 101 | color: #000000; 102 | background-color: #ddffdd; } 103 | .highlight .gi .x { 104 | color: #000000; 105 | background-color: #aaffaa; } 106 | .highlight .go { 107 | color: #888888; } 108 | .highlight .gp { 109 | color: #555555; } 110 | .highlight .gs { 111 | font-weight: bold; } 112 | .highlight .gu { 113 | color: #aaaaaa; } 114 | .highlight .gt { 115 | color: #aa0000; } 116 | .highlight .kc { 117 | color: #000000; 118 | font-weight: bold; } 119 | .highlight .kd { 120 | color: #000000; 121 | font-weight: bold; } 122 | .highlight .kp { 123 | color: #000000; 124 | font-weight: bold; } 125 | .highlight .kr { 126 | color: #000000; 127 | font-weight: bold; } 128 | .highlight .kt { 129 | color: #445588; } 130 | .highlight .m { 131 | color: #009999; } 132 | .highlight .s { 133 | color: #d14; } 134 | .highlight .na { 135 | color: #008080; } 136 | .highlight .nb { 137 | color: #0086B3; } 138 | .highlight .nc { 139 | color: #445588; 140 | font-weight: bold; } 141 | .highlight .no { 142 | color: #008080; } 143 | .highlight .ni { 144 | color: #800080; } 145 | .highlight .ne { 146 | color: #990000; 147 | font-weight: bold; } 148 | .highlight .nf { 149 | color: #990000; } 150 | .highlight .nn { 151 | color: #555555; } 152 | .highlight .nt { 153 | color: #000080; } 154 | .highlight .nv { 155 | color: #008080; } 156 | .highlight .ow { 157 | color: #000000; 158 | font-weight: bold; } 159 | .highlight .w { 160 | color: #bbbbbb; } 161 | .highlight .mf { 162 | color: #009999; } 163 | .highlight .mh { 164 | color: #009999; } 165 | .highlight .mi { 166 | color: #009999; } 167 | .highlight .mo { 168 | color: #009999; } 169 | .highlight .sb { 170 | color: #d14; } 171 | .highlight .sc { 172 | color: #d14; } 173 | .highlight .sd { 174 | color: #d14; } 175 | .highlight .s2 { 176 | color: #d14; } 177 | .highlight .se { 178 | color: #d14; } 179 | .highlight .sh { 180 | color: #d14; } 181 | .highlight .si { 182 | color: #d14; } 183 | .highlight .sx { 184 | color: #d14; } 185 | .highlight .sr { 186 | color: #009926; } 187 | .highlight .s1 { 188 | color: #d14; } 189 | .highlight .ss { 190 | color: #990073; } 191 | .highlight .bp { 192 | color: #999999; } 193 | .highlight .vc { 194 | color: #008080; } 195 | .highlight .vg { 196 | color: #008080; } 197 | .highlight .vi { 198 | color: #008080; } 199 | .highlight .il { 200 | color: #009999; } 201 | -------------------------------------------------------------------------------- /docs/docsets/UIEmptyState.docset/Contents/Resources/Documents/css/jazzy.css: -------------------------------------------------------------------------------- 1 | *, *:before, *:after { 2 | box-sizing: inherit; } 3 | 4 | body { 5 | margin: 0; 6 | background: #fff; 7 | color: #333; 8 | font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; 9 | letter-spacing: .2px; 10 | -webkit-font-smoothing: antialiased; 11 | box-sizing: border-box; } 12 | 13 | h1 { 14 | font-size: 2rem; 15 | font-weight: 700; 16 | margin: 1.275em 0 0.6em; } 17 | 18 | h2 { 19 | font-size: 1.75rem; 20 | font-weight: 700; 21 | margin: 1.275em 0 0.3em; } 22 | 23 | h3 { 24 | font-size: 1.5rem; 25 | font-weight: 700; 26 | margin: 1em 0 0.3em; } 27 | 28 | h4 { 29 | font-size: 1.25rem; 30 | font-weight: 700; 31 | margin: 1.275em 0 0.85em; } 32 | 33 | h5 { 34 | font-size: 1rem; 35 | font-weight: 700; 36 | margin: 1.275em 0 0.85em; } 37 | 38 | h6 { 39 | font-size: 1rem; 40 | font-weight: 700; 41 | margin: 1.275em 0 0.85em; 42 | color: #777; } 43 | 44 | p { 45 | margin: 0 0 1em; } 46 | 47 | ul, ol { 48 | padding: 0 0 0 2em; 49 | margin: 0 0 0.85em; } 50 | 51 | blockquote { 52 | margin: 0 0 0.85em; 53 | padding: 0 15px; 54 | color: #858585; 55 | border-left: 4px solid #e5e5e5; } 56 | 57 | img { 58 | max-width: 100%; } 59 | 60 | a { 61 | color: #4183c4; 62 | text-decoration: none; } 63 | a:hover, a:focus { 64 | outline: 0; 65 | text-decoration: underline; } 66 | 67 | table { 68 | background: #fff; 69 | width: 100%; 70 | border-collapse: collapse; 71 | border-spacing: 0; 72 | overflow: auto; 73 | margin: 0 0 0.85em; } 74 | 75 | tr:nth-child(2n) { 76 | background-color: #fbfbfb; } 77 | 78 | th, td { 79 | padding: 6px 13px; 80 | border: 1px solid #ddd; } 81 | 82 | pre { 83 | margin: 0 0 1.275em; 84 | padding: .85em 1em; 85 | overflow: auto; 86 | background: #f7f7f7; 87 | font-size: .85em; 88 | font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; } 89 | 90 | code { 91 | font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; } 92 | 93 | p > code, li > code { 94 | background: #f7f7f7; 95 | padding: .2em; } 96 | p > code:before, p > code:after, li > code:before, li > code:after { 97 | letter-spacing: -.2em; 98 | content: "\00a0"; } 99 | 100 | pre code { 101 | padding: 0; 102 | white-space: pre; } 103 | 104 | .content-wrapper { 105 | display: flex; 106 | flex-direction: column; } 107 | @media (min-width: 768px) { 108 | .content-wrapper { 109 | flex-direction: row; } } 110 | 111 | .header { 112 | display: flex; 113 | padding: 8px; 114 | font-size: 0.875em; 115 | background: #444; 116 | color: #999; } 117 | 118 | .header-col { 119 | margin: 0; 120 | padding: 0 8px; } 121 | 122 | .header-col--primary { 123 | flex: 1; } 124 | 125 | .header-link { 126 | color: #fff; } 127 | 128 | .header-icon { 129 | padding-right: 6px; 130 | vertical-align: -4px; 131 | height: 16px; } 132 | 133 | .breadcrumbs { 134 | font-size: 0.875em; 135 | padding: 8px 16px; 136 | margin: 0; 137 | background: #fbfbfb; 138 | border-bottom: 1px solid #ddd; } 139 | 140 | .carat { 141 | height: 10px; 142 | margin: 0 5px; } 143 | 144 | .navigation { 145 | order: 2; } 146 | @media (min-width: 768px) { 147 | .navigation { 148 | order: 1; 149 | width: 25%; 150 | max-width: 300px; 151 | padding-bottom: 64px; 152 | overflow: hidden; 153 | word-wrap: normal; 154 | background: #fbfbfb; 155 | border-right: 1px solid #ddd; } } 156 | 157 | .nav-groups { 158 | list-style-type: none; 159 | padding-left: 0; } 160 | 161 | .nav-group-name { 162 | border-bottom: 1px solid #ddd; 163 | padding: 8px 0 8px 16px; } 164 | 165 | .nav-group-name-link { 166 | color: #333; } 167 | 168 | .nav-group-tasks { 169 | margin: 8px 0; 170 | padding: 0 0 0 8px; } 171 | 172 | .nav-group-task { 173 | font-size: 1em; 174 | list-style-type: none; 175 | white-space: nowrap; } 176 | 177 | .nav-group-task-link { 178 | color: #808080; } 179 | 180 | .main-content { 181 | order: 1; } 182 | @media (min-width: 768px) { 183 | .main-content { 184 | order: 2; 185 | flex: 1; 186 | padding-bottom: 60px; } } 187 | 188 | .section { 189 | padding: 0 32px; 190 | border-bottom: 1px solid #ddd; } 191 | 192 | .section-content { 193 | max-width: 834px; 194 | margin: 0 auto; 195 | padding: 16px 0; } 196 | 197 | .section-name { 198 | color: #666; 199 | display: block; } 200 | 201 | .declaration .highlight { 202 | overflow-x: initial; 203 | padding: 8px 0; 204 | margin: 0; 205 | background-color: transparent; 206 | border: none; } 207 | 208 | .task-group-section { 209 | border-top: 1px solid #ddd; } 210 | 211 | .task-group { 212 | padding-top: 0px; } 213 | 214 | .task-name-container a[name]:before { 215 | content: ""; 216 | display: block; } 217 | 218 | .item-container { 219 | padding: 0; } 220 | 221 | .item { 222 | padding-top: 8px; 223 | width: 100%; 224 | list-style-type: none; } 225 | .item a[name]:before { 226 | content: ""; 227 | display: block; } 228 | .item .token { 229 | padding-left: 3px; 230 | margin-left: 0px; 231 | font-size: 1rem; } 232 | .item .declaration-note { 233 | font-size: .85em; 234 | color: #808080; 235 | font-style: italic; } 236 | 237 | .pointer-container { 238 | border-bottom: 1px solid #ddd; 239 | left: -23px; 240 | padding-bottom: 13px; 241 | position: relative; 242 | width: 110%; } 243 | 244 | .pointer { 245 | left: 21px; 246 | top: 7px; 247 | display: block; 248 | position: absolute; 249 | width: 12px; 250 | height: 12px; 251 | border-left: 1px solid #ddd; 252 | border-top: 1px solid #ddd; 253 | background: #fff; 254 | transform: rotate(45deg); } 255 | 256 | .height-container { 257 | display: none; 258 | position: relative; 259 | width: 100%; 260 | overflow: hidden; } 261 | .height-container .section { 262 | background: #fff; 263 | border: 1px solid #ddd; 264 | border-top-width: 0; 265 | padding-top: 10px; 266 | padding-bottom: 5px; 267 | padding: 8px 16px; } 268 | 269 | .aside, .language { 270 | padding: 6px 12px; 271 | margin: 12px 0; 272 | border-left: 5px solid #dddddd; 273 | overflow-y: hidden; } 274 | .aside .aside-title, .language .aside-title { 275 | font-size: 9px; 276 | letter-spacing: 2px; 277 | text-transform: uppercase; 278 | padding-bottom: 0; 279 | margin: 0; 280 | color: #aaa; 281 | -webkit-user-select: none; } 282 | .aside p:last-child, .language p:last-child { 283 | margin-bottom: 0; } 284 | 285 | .language { 286 | border-left: 5px solid #cde9f4; } 287 | .language .aside-title { 288 | color: #4183c4; } 289 | 290 | .aside-warning { 291 | border-left: 5px solid #ff6666; } 292 | .aside-warning .aside-title { 293 | color: #ff0000; } 294 | 295 | .graybox { 296 | border-collapse: collapse; 297 | width: 100%; } 298 | .graybox p { 299 | margin: 0; 300 | word-break: break-word; 301 | min-width: 50px; } 302 | .graybox td { 303 | border: 1px solid #ddd; 304 | padding: 5px 25px 5px 10px; 305 | vertical-align: middle; } 306 | .graybox tr td:first-of-type { 307 | text-align: right; 308 | padding: 7px; 309 | vertical-align: top; 310 | word-break: normal; 311 | width: 40px; } 312 | 313 | .slightly-smaller { 314 | font-size: 0.9em; } 315 | 316 | .footer { 317 | padding: 8px 16px; 318 | background: #444; 319 | color: #ddd; 320 | font-size: 0.8em; } 321 | .footer p { 322 | margin: 8px 0; } 323 | .footer a { 324 | color: #fff; } 325 | 326 | html.dash .header, html.dash .breadcrumbs, html.dash .navigation { 327 | display: none; } 328 | html.dash .height-container { 329 | display: block; } 330 | 331 | form[role=search] input { 332 | font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; 333 | font-size: 14px; 334 | line-height: 24px; 335 | padding: 0 10px; 336 | margin: 0; 337 | border: none; 338 | border-radius: 1em; } 339 | .loading form[role=search] input { 340 | background: white url(../img/spinner.gif) center right 4px no-repeat; } 341 | form[role=search] .tt-menu { 342 | margin: 0; 343 | min-width: 300px; 344 | background: #fbfbfb; 345 | color: #333; 346 | border: 1px solid #ddd; } 347 | form[role=search] .tt-highlight { 348 | font-weight: bold; } 349 | form[role=search] .tt-suggestion { 350 | font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; 351 | padding: 0 8px; } 352 | form[role=search] .tt-suggestion span { 353 | display: table-cell; 354 | white-space: nowrap; } 355 | form[role=search] .tt-suggestion .doc-parent-name { 356 | width: 100%; 357 | text-align: right; 358 | font-weight: normal; 359 | font-size: 0.9em; 360 | padding-left: 16px; } 361 | form[role=search] .tt-suggestion:hover, 362 | form[role=search] .tt-suggestion.tt-cursor { 363 | cursor: pointer; 364 | background-color: #4183c4; 365 | color: #fff; } 366 | form[role=search] .tt-suggestion:hover .doc-parent-name, 367 | form[role=search] .tt-suggestion.tt-cursor .doc-parent-name { 368 | color: #fff; } 369 | -------------------------------------------------------------------------------- /docs/docsets/UIEmptyState.docset/Contents/Resources/Documents/img/carat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luispadron/UIEmptyState/4fb00263b97ee3a513f029244eb73580b605ab71/docs/docsets/UIEmptyState.docset/Contents/Resources/Documents/img/carat.png -------------------------------------------------------------------------------- /docs/docsets/UIEmptyState.docset/Contents/Resources/Documents/img/dash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luispadron/UIEmptyState/4fb00263b97ee3a513f029244eb73580b605ab71/docs/docsets/UIEmptyState.docset/Contents/Resources/Documents/img/dash.png -------------------------------------------------------------------------------- /docs/docsets/UIEmptyState.docset/Contents/Resources/Documents/img/gh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luispadron/UIEmptyState/4fb00263b97ee3a513f029244eb73580b605ab71/docs/docsets/UIEmptyState.docset/Contents/Resources/Documents/img/gh.png -------------------------------------------------------------------------------- /docs/docsets/UIEmptyState.docset/Contents/Resources/Documents/img/spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luispadron/UIEmptyState/4fb00263b97ee3a513f029244eb73580b605ab71/docs/docsets/UIEmptyState.docset/Contents/Resources/Documents/img/spinner.gif -------------------------------------------------------------------------------- /docs/docsets/UIEmptyState.docset/Contents/Resources/Documents/js/jazzy.js: -------------------------------------------------------------------------------- 1 | window.jazzy = {'docset': false} 2 | if (typeof window.dash != 'undefined') { 3 | document.documentElement.className += ' dash' 4 | window.jazzy.docset = true 5 | } 6 | if (navigator.userAgent.match(/xcode/i)) { 7 | document.documentElement.className += ' xcode' 8 | window.jazzy.docset = true 9 | } 10 | 11 | // On doc load, toggle the URL hash discussion if present 12 | $(document).ready(function() { 13 | if (!window.jazzy.docset) { 14 | var linkToHash = $('a[href="' + window.location.hash +'"]'); 15 | linkToHash.trigger("click"); 16 | } 17 | }); 18 | 19 | // On token click, toggle its discussion and animate token.marginLeft 20 | $(".token").click(function(event) { 21 | if (window.jazzy.docset) { 22 | return; 23 | } 24 | var link = $(this); 25 | var animationDuration = 300; 26 | $content = link.parent().parent().next(); 27 | $content.slideToggle(animationDuration); 28 | 29 | // Keeps the document from jumping to the hash. 30 | var href = $(this).attr('href'); 31 | if (history.pushState) { 32 | history.pushState({}, '', href); 33 | } else { 34 | location.hash = href; 35 | } 36 | event.preventDefault(); 37 | }); 38 | 39 | // Dumb down quotes within code blocks that delimit strings instead of quotations 40 | // https://github.com/realm/jazzy/issues/714 41 | $("code q").replaceWith(function () { 42 | return ["\"", $(this).contents(), "\""]; 43 | }); 44 | -------------------------------------------------------------------------------- /docs/docsets/UIEmptyState.docset/Contents/Resources/Documents/js/jazzy.search.js: -------------------------------------------------------------------------------- 1 | $(function(){ 2 | var searchIndex = lunr(function() { 3 | this.ref('url'); 4 | this.field('name'); 5 | }); 6 | 7 | var $typeahead = $('[data-typeahead]'); 8 | var $form = $typeahead.parents('form'); 9 | var searchURL = $form.attr('action'); 10 | 11 | function displayTemplate(result) { 12 | return result.name; 13 | } 14 | 15 | function suggestionTemplate(result) { 16 | var t = '
'; 17 | t += '' + result.name + ''; 18 | if (result.parent_name) { 19 | t += '' + result.parent_name + ''; 20 | } 21 | t += '
'; 22 | return t; 23 | } 24 | 25 | $typeahead.one('focus', function() { 26 | $form.addClass('loading'); 27 | 28 | $.getJSON(searchURL).then(function(searchData) { 29 | $.each(searchData, function (url, doc) { 30 | searchIndex.add({url: url, name: doc.name}); 31 | }); 32 | 33 | $typeahead.typeahead( 34 | { 35 | highlight: true, 36 | minLength: 3 37 | }, 38 | { 39 | limit: 10, 40 | display: displayTemplate, 41 | templates: { suggestion: suggestionTemplate }, 42 | source: function(query, sync) { 43 | var results = searchIndex.search(query).map(function(result) { 44 | var doc = searchData[result.ref]; 45 | doc.url = result.ref; 46 | return doc; 47 | }); 48 | sync(results); 49 | } 50 | } 51 | ); 52 | $form.removeClass('loading'); 53 | $typeahead.trigger('focus'); 54 | }); 55 | }); 56 | 57 | var baseURL = searchURL.slice(0, -"search.json".length); 58 | 59 | $typeahead.on('typeahead:select', function(e, result) { 60 | window.location = baseURL + result.url; 61 | }); 62 | }); 63 | -------------------------------------------------------------------------------- /docs/docsets/UIEmptyState.docset/Contents/Resources/docSet.dsidx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luispadron/UIEmptyState/4fb00263b97ee3a513f029244eb73580b605ab71/docs/docsets/UIEmptyState.docset/Contents/Resources/docSet.dsidx -------------------------------------------------------------------------------- /docs/docsets/UIEmptyState.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luispadron/UIEmptyState/4fb00263b97ee3a513f029244eb73580b605ab71/docs/docsets/UIEmptyState.tgz -------------------------------------------------------------------------------- /docs/img/carat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luispadron/UIEmptyState/4fb00263b97ee3a513f029244eb73580b605ab71/docs/img/carat.png -------------------------------------------------------------------------------- /docs/img/dash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luispadron/UIEmptyState/4fb00263b97ee3a513f029244eb73580b605ab71/docs/img/dash.png -------------------------------------------------------------------------------- /docs/img/gh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luispadron/UIEmptyState/4fb00263b97ee3a513f029244eb73580b605ab71/docs/img/gh.png -------------------------------------------------------------------------------- /docs/img/spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luispadron/UIEmptyState/4fb00263b97ee3a513f029244eb73580b605ab71/docs/img/spinner.gif -------------------------------------------------------------------------------- /docs/js/jazzy.js: -------------------------------------------------------------------------------- 1 | window.jazzy = {'docset': false} 2 | if (typeof window.dash != 'undefined') { 3 | document.documentElement.className += ' dash' 4 | window.jazzy.docset = true 5 | } 6 | if (navigator.userAgent.match(/xcode/i)) { 7 | document.documentElement.className += ' xcode' 8 | window.jazzy.docset = true 9 | } 10 | 11 | // On doc load, toggle the URL hash discussion if present 12 | $(document).ready(function() { 13 | if (!window.jazzy.docset) { 14 | var linkToHash = $('a[href="' + window.location.hash +'"]'); 15 | linkToHash.trigger("click"); 16 | } 17 | }); 18 | 19 | // On token click, toggle its discussion and animate token.marginLeft 20 | $(".token").click(function(event) { 21 | if (window.jazzy.docset) { 22 | return; 23 | } 24 | var link = $(this); 25 | var animationDuration = 300; 26 | $content = link.parent().parent().next(); 27 | $content.slideToggle(animationDuration); 28 | 29 | // Keeps the document from jumping to the hash. 30 | var href = $(this).attr('href'); 31 | if (history.pushState) { 32 | history.pushState({}, '', href); 33 | } else { 34 | location.hash = href; 35 | } 36 | event.preventDefault(); 37 | }); 38 | 39 | // Dumb down quotes within code blocks that delimit strings instead of quotations 40 | // https://github.com/realm/jazzy/issues/714 41 | $("code q").replaceWith(function () { 42 | return ["\"", $(this).contents(), "\""]; 43 | }); 44 | -------------------------------------------------------------------------------- /docs/js/jazzy.search.js: -------------------------------------------------------------------------------- 1 | $(function(){ 2 | var searchIndex = lunr(function() { 3 | this.ref('url'); 4 | this.field('name'); 5 | }); 6 | 7 | var $typeahead = $('[data-typeahead]'); 8 | var $form = $typeahead.parents('form'); 9 | var searchURL = $form.attr('action'); 10 | 11 | function displayTemplate(result) { 12 | return result.name; 13 | } 14 | 15 | function suggestionTemplate(result) { 16 | var t = '
'; 17 | t += '' + result.name + ''; 18 | if (result.parent_name) { 19 | t += '' + result.parent_name + ''; 20 | } 21 | t += '
'; 22 | return t; 23 | } 24 | 25 | $typeahead.one('focus', function() { 26 | $form.addClass('loading'); 27 | 28 | $.getJSON(searchURL).then(function(searchData) { 29 | $.each(searchData, function (url, doc) { 30 | searchIndex.add({url: url, name: doc.name}); 31 | }); 32 | 33 | $typeahead.typeahead( 34 | { 35 | highlight: true, 36 | minLength: 3 37 | }, 38 | { 39 | limit: 10, 40 | display: displayTemplate, 41 | templates: { suggestion: suggestionTemplate }, 42 | source: function(query, sync) { 43 | var results = searchIndex.search(query).map(function(result) { 44 | var doc = searchData[result.ref]; 45 | doc.url = result.ref; 46 | return doc; 47 | }); 48 | sync(results); 49 | } 50 | } 51 | ); 52 | $form.removeClass('loading'); 53 | $typeahead.trigger('focus'); 54 | }); 55 | }); 56 | 57 | var baseURL = searchURL.slice(0, -"search.json".length); 58 | 59 | $typeahead.on('typeahead:select', function(e, result) { 60 | window.location = baseURL + result.url; 61 | }); 62 | }); 63 | -------------------------------------------------------------------------------- /docs/undocumented.json: -------------------------------------------------------------------------------- 1 | { 2 | "warnings": [ 3 | 4 | ], 5 | "source_directory": "/Users/luis/Programming/iOS/UIEmptyState/src" 6 | } -------------------------------------------------------------------------------- /run-jazzy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | jazzy --clean -a Luis Padron --source-directory src -u https://luispadron.com -m UIEmptyState --module-version 1.0.0 --readme README.md -g https://github.com/luispadron/UIEmptyState --theme fullwidth 4 | -------------------------------------------------------------------------------- /src/UIEmptyState.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/UIEmptyState.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/UIEmptyState.xcodeproj/xcshareddata/xcschemes/UIEmptyState.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 42 | 48 | 49 | 50 | 51 | 52 | 62 | 63 | 69 | 70 | 71 | 72 | 78 | 79 | 85 | 86 | 87 | 88 | 90 | 91 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /src/UIEmptyState/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/UIEmptyState/UIEmptyState.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIEmptyState.h 3 | // UIEmptyState 4 | // 5 | // Created by Luis Padron on 1/30/17. 6 | // Copyright © 2017 Luis Padron. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for UIEmptyState. 12 | FOUNDATION_EXPORT double UIEmptyStateVersionNumber; 13 | 14 | //! Project version string for UIEmptyState. 15 | FOUNDATION_EXPORT const unsigned char UIEmptyStateVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/UIEmptyState/UIEmptyStateDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIEmptyStateDelegate.swift 3 | // UIEmptyState 4 | // 5 | // Created by Luis Padron on 1/31/17. 6 | // Copyright © 2017 Luis Padron. All rights reserved. 7 | // 8 | 9 | /** 10 | The delegate for UIEmptyStateView 11 | 12 | **Important:** 13 | This delegate and its functions are only used when using `UIEmptyStateView`. 14 | If you will provide a custom view in the `UIEmptyStateDataSource` `viewForEmptyState` 15 | you must handle how this delegate operates 16 | */ 17 | public protocol UIEmptyStateDelegate: class { 18 | /** 19 | The call back for when the `emptyStateView` will be shown on screen 20 | 21 | - parameters: 22 | - view: The view that is will show 23 | */ 24 | func emptyStateViewWillShow(view: UIView) 25 | 26 | /** 27 | The call back for when the `emptyStateView` is now shown on screen 28 | 29 | - parameters: 30 | - view: The view that is now shown 31 | */ 32 | func emptyStateViewDidShow(view: UIView) 33 | 34 | /** 35 | The call back for when the `emptyStateView` will be hidden 36 | 37 | - parameters: 38 | - view: The view that will be hidden 39 | */ 40 | func emptyStateViewWillHide(view: UIView) 41 | 42 | /** 43 | The call back for when the button inside the emptyStateView is tapped 44 | 45 | - parameters: 46 | - button: The button that was tapped 47 | */ 48 | func emptyStatebuttonWasTapped(button: UIButton) 49 | 50 | /** 51 | The call back for when the emptyStateView itself is tapped 52 | 53 | - parameters: 54 | - view: The view that was tapped 55 | */ 56 | func emptyStateViewWasTapped(view: UIView) 57 | 58 | /** 59 | The call back for when the animation of the emptyStateView is done 60 | - paramaters: 61 | - view: The view which finished animating 62 | - didFinish: Whether the animation finished completely, i.e not interrupted 63 | */ 64 | func emptyStateViewAnimationCompleted(for view: UIView, didFinish: Bool) 65 | } 66 | 67 | /// Extension to add default conformance to UIViewController, by default the method bodies are empty 68 | extension UIEmptyStateDelegate where Self: UIViewController { 69 | /// Default empty implementation of `emptyStateViewWillShow` 70 | public func emptyStateViewWillShow(view: UIView) { } 71 | /// Default empty implementation of `emptyStateViewDidShow` 72 | public func emptyStateViewDidShow(view: UIView) { } 73 | /// Default empty implementation of `emptyStateViewWillHide` 74 | public func emptyStateViewWillHide(view: UIView) { } 75 | /// Default empty implementation of `emptyStateButtonWasTapped` 76 | public func emptyStatebuttonWasTapped(button: UIButton) { } 77 | /// Default empty implementation of `emptyStateViewWasTapped` 78 | public func emptyStateViewWasTapped(view: UIView) { } 79 | /// Default empty implementation of `emptyStateViewAnimationCompleted` 80 | public func emptyStateViewAnimationCompleted(for view: UIView, didFinish: Bool) { } 81 | } 82 | -------------------------------------------------------------------------------- /src/UIEmptyState/UIViewController+UIEmptyState.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewController+UIEmptyState 3 | // UIEmptyState 4 | // 5 | // Created by Luis Padron on 1/31/17. 6 | // Copyright © 2017 Luis Padron. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | /// Extension on UIViewController which adds method and computed properties in order to allow empty view creation 12 | extension UIViewController { 13 | /// Private struct of keys to be used with objective-c associated objects 14 | private struct Keys { 15 | static var emptyStateView = "com.luispadron.emptyStateView" 16 | static var emptyStateDataSource = "com.luispadron.emptyStateDataSource" 17 | static var emptyStateDelegate = "com.luispadron.emptyStateDelegate" 18 | } 19 | 20 | /** 21 | The data source for the Empty View 22 | 23 | Default conformance for UIViewController is provided, 24 | however feel free to implement these methods to customize your view. 25 | */ 26 | public var emptyStateDataSource: UIEmptyStateDataSource? { 27 | get { return objc_getAssociatedObject(self, &Keys.emptyStateDataSource) as? UIEmptyStateDataSource } 28 | set { objc_setAssociatedObject(self, &Keys.emptyStateDataSource, newValue, .OBJC_ASSOCIATION_ASSIGN) } 29 | } 30 | 31 | /** 32 | The delegate for UIEmptyStateView 33 | 34 | **Important:** this delegate and its functions are only used when using UIEmptyStateView. 35 | If you will provide a custom view in the UIEmptyStateDataSource you must handle how this delegate operates 36 | */ 37 | public var emptyStateDelegate: UIEmptyStateDelegate? { 38 | get { return objc_getAssociatedObject(self, &Keys.emptyStateDelegate) as? UIEmptyStateDelegate } 39 | set { objc_setAssociatedObject(self, &Keys.emptyStateDelegate, newValue, .OBJC_ASSOCIATION_ASSIGN) } 40 | } 41 | 42 | /** 43 | The empty state view associated to the ViewController 44 | 45 | **Note:** 46 | This view corresponds and is created from 47 | the UIEmptyDataSource method: `func viewForEmptyState() -> UIView` 48 | 49 | To set this view, use `UIEmptyStateDataSource.emptyStateView`. 50 | 51 | By default this view is of type `UIEmptyStateView` 52 | */ 53 | private var emptyView: UIView? { 54 | get { return objc_getAssociatedObject(self, &Keys.emptyStateView) as? UIView } 55 | set { 56 | objc_setAssociatedObject(self, &Keys.emptyStateView, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) 57 | // Set the views delegate 58 | if let view = emptyView as? UIEmptyStateView { view.delegate = emptyStateDelegate } 59 | } 60 | } 61 | 62 | /** 63 | The method responsible for show and hiding the `UIEmptyStateDataSource.viewForEmptyState` view 64 | 65 | **Important:** 66 | This should be called whenever changes are made to the tableView data source or after reloading the tableview 67 | 68 | Do NOT override this method/implement it unless you need custom behavior and know what you are doing. 69 | */ 70 | public func reloadEmptyStateForTableView(_ tableView: UITableView) { 71 | guard let source = emptyStateDataSource, source.emptyStateViewShouldShow(for: tableView) else { 72 | // Call the will hide delegate 73 | if let view = emptyView { 74 | self.emptyStateDelegate?.emptyStateViewWillHide(view: view) 75 | } 76 | // If shouldnt show view remove from superview, enable scrolling again 77 | emptyView?.isHidden = true 78 | tableView.isScrollEnabled = true 79 | // Also return edges for extended layout to the default values, if adjusted 80 | if emptyStateDataSource?.emptyStateViewAdjustsToFitBars ?? false { 81 | self.edgesForExtendedLayout = .all 82 | } 83 | return 84 | } 85 | 86 | // Check whether scrolling for tableview is allowed or not 87 | tableView.isScrollEnabled = source.emptyStateViewCanScroll 88 | 89 | finishReload(for: source, in: tableView) 90 | } 91 | 92 | /** 93 | The method responsible for show and hiding the `UIEmptyStateDataSource.viewForEmptyState` view 94 | 95 | **Important:** 96 | This should be called whenever changes are made to the collection view data source or after reloading the collection view. 97 | 98 | Do NOT override this method/implement it unless you need custom behavior and know what you are doing. 99 | */ 100 | public func reloadEmptyStateForCollectionView(_ collectionView: UICollectionView) { 101 | guard let source = emptyStateDataSource, 102 | source.emptyStateViewShouldShow(for: collectionView) else { 103 | // Call the will hide delegate 104 | if let view = emptyView { 105 | self.emptyStateDelegate?.emptyStateViewWillHide(view: view) 106 | } 107 | // If shouldnt show view remove from superview, enable scrolling again 108 | emptyView?.isHidden = true 109 | collectionView.isScrollEnabled = true 110 | // Also return edges for extended layout to the default values, if adjusted 111 | if emptyStateDataSource?.emptyStateViewAdjustsToFitBars ?? false { 112 | self.edgesForExtendedLayout = .all 113 | } 114 | return 115 | } 116 | 117 | // Check to see if scrolling is enabled 118 | collectionView.isScrollEnabled = source.emptyStateViewCanScroll 119 | 120 | finishReload(for: source, in: collectionView) 121 | 122 | } 123 | 124 | /// Finishes the reload, i.e assigns the empty view, and adjusts any other UI 125 | private func finishReload(for source: UIEmptyStateDataSource, in superView: UIView) { 126 | let emptyView = showView(for: source, in: superView) 127 | 128 | // Only add constraints if they haven't already been added 129 | if emptyView.constraints.count <= 2 { // 2, because theres already 2 constraints added to it's subviews 130 | self.createViewConstraints(for: emptyView, in: superView, source: source) 131 | } 132 | 133 | // Return & call the did show view delegate 134 | self.emptyStateDelegate?.emptyStateViewDidShow(view: emptyView) 135 | } 136 | 137 | //// Private helper which creates view contraints for the UIEmptyStateView. 138 | private func createViewConstraints(for view: UIView, in superView: UIView, source: UIEmptyStateDataSource) { 139 | 140 | var centerYOffset: CGFloat? 141 | // Takes into account any extra offset that the table's header view might need, this way 142 | // we get a true center alignment with the table view. 143 | if let tableHeader = (superView as? UITableView)?.tableHeaderView { 144 | centerYOffset = tableHeader.frame.height / 2.0 145 | } 146 | 147 | var centerY = view.centerYAnchor.constraint(equalTo: superView.centerYAnchor) 148 | var centerX = view.centerXAnchor.constraint(equalTo: superView.centerXAnchor, constant: centerYOffset ?? 0) 149 | centerX.isActive = true 150 | centerY.isActive = true 151 | 152 | // If iOS 11.0 is not available, then adjust the extended layout accordingly using older API 153 | // and then return 154 | guard #available(iOS 11.0, *) else { 155 | // Adjust to fit bars if allowed 156 | if source.emptyStateViewAdjustsToFitBars { 157 | self.edgesForExtendedLayout = [] 158 | } else { 159 | self.edgesForExtendedLayout = .all 160 | } 161 | return 162 | } 163 | 164 | // iOS 11.0+ is available, thus use new safeAreaLayoutGuide, but only if adjustesToFitBars is true. 165 | // The reason for this is safeAreaLayoutGuide will take into account any bar that may be used 166 | // If for some reason user doesn't want to adjust to bars, then keep the old center constraints 167 | if source.emptyStateViewAdjustsToFitBars { 168 | // Adjust constraint to fit new big title bars, etc 169 | centerX.isActive = false 170 | centerX = view.centerXAnchor.constraint(equalTo: superView.safeAreaLayoutGuide.centerXAnchor) 171 | centerX.isActive = true 172 | 173 | centerY.isActive = false 174 | centerY = view.centerYAnchor.constraint(equalTo: superView.safeAreaLayoutGuide.centerYAnchor, 175 | constant: centerYOffset ?? 0) 176 | centerY.isActive = true 177 | 178 | } 179 | } 180 | 181 | /// Private helper method which will create the empty state view if not created, or show it if hidden 182 | private func showView(for source: UIEmptyStateDataSource, in view: UIView) -> UIView { 183 | 184 | if let createdView = emptyView { 185 | // Call the will show delegate 186 | self.emptyStateDelegate?.emptyStateViewWillShow(view: createdView) 187 | // View has been created, update it and then reshow 188 | createdView.isHidden = false 189 | guard let view = createdView as? UIEmptyStateView else { return createdView } 190 | 191 | view.backgroundColor = source.emptyStateBackgroundColor 192 | view.title = source.emptyStateTitle 193 | view.image = source.emptyStateImage 194 | view.imageSize = source.emptyStateImageSize 195 | view.imageViewTintColor = source.emptyStateImageViewTintColor 196 | view.buttonTitle = source.emptyStateButtonTitle 197 | view.buttonImage = source.emptyStateButtonImage 198 | view.buttonSize = source.emptyStateButtonSize 199 | view.detailMessage = source.emptyStateDetailMessage 200 | view.spacing = source.emptyStateViewSpacing 201 | view.centerYOffset = source.emptyStateViewCenterYOffset 202 | view.backgroundColor = source.emptyStateBackgroundColor 203 | 204 | // Animate now 205 | if source.emptyStateViewCanAnimate && source.emptyStateViewAnimatesEverytime { 206 | DispatchQueue.main.async { 207 | source.emptyStateViewAnimation( 208 | for: view, 209 | animationDuration: source.emptyStateViewAnimationDuration, 210 | completion: { finished in 211 | self.emptyStateDelegate?.emptyStateViewAnimationCompleted(for: view, didFinish: finished) 212 | } 213 | ) 214 | } 215 | } 216 | 217 | return view 218 | 219 | } else { 220 | // We can create the view now 221 | let newView = source.emptyStateView 222 | // Call the will show delegate 223 | self.emptyStateDelegate?.emptyStateViewWillShow(view: newView) 224 | // Add to emptyStateView property 225 | emptyView = newView 226 | // Add as a subView, bring it infront of the tableView 227 | view.addSubview(newView) 228 | view.bringSubviewToFront(newView) 229 | // Animate now 230 | if source.emptyStateViewCanAnimate { 231 | DispatchQueue.main.async { 232 | source.emptyStateViewAnimation( 233 | for: newView, 234 | animationDuration: source.emptyStateViewAnimationDuration, 235 | completion: { finished in 236 | self.emptyStateDelegate?.emptyStateViewAnimationCompleted(for: newView, didFinish: finished) 237 | } 238 | ) 239 | } 240 | } 241 | 242 | return newView 243 | } 244 | } 245 | 246 | } 247 | 248 | /// A convenience extension for UITableViewController which defaults the tableView 249 | extension UITableViewController { 250 | /// Reloads the empty state, defaults the tableView to `self.tableView` 251 | public func reloadEmptyState() { 252 | self.reloadEmptyStateForTableView(self.tableView) 253 | } 254 | } 255 | 256 | /// A convenience extension for UICollectionViewController which defaults the collectionView 257 | extension UICollectionViewController { 258 | /// Reloads the empty state, defaults the collectionView to `self.collectionView` 259 | public func reloadEmptyState() { 260 | guard let collectionView = self.collectionView else { 261 | print("UIEmptyState ==== WARNING: Tried to reload collectionView's empty state but the collectionView for\n\(self) was nil.") 262 | return 263 | } 264 | 265 | self.reloadEmptyStateForCollectionView(collectionView) 266 | } 267 | } 268 | 269 | 270 | 271 | -------------------------------------------------------------------------------- /src/UIEmptyStateTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/UIEmptyStateTests/UIEmptyStateTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIEmptyStateTests.swift 3 | // UIEmptyStateTests 4 | // 5 | // Created by Luis Padron on 1/30/17. 6 | // Copyright © 2017 Luis Padron. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import UIEmptyState 11 | 12 | class UIEmptyStateTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testExample() { 25 | // This is an example of a functional test case. 26 | // Use XCTAssert and related functions to verify your tests produce the correct results. 27 | } 28 | 29 | func testPerformanceExample() { 30 | // This is an example of a performance test case. 31 | self.measure { 32 | // Put the code you want to measure the time of here. 33 | } 34 | } 35 | 36 | } 37 | --------------------------------------------------------------------------------