{"id":2939,"url":"https://github.com/aschuch/StatefulViewController","last_synced_at":"2025-08-06T15:30:33.198Z","repository":{"id":20316501,"uuid":"23590587","full_name":"aschuch/StatefulViewController","owner":"aschuch","description":"Placeholder views based on content, loading, error or empty states","archived":false,"fork":false,"pushed_at":"2020-11-12T08:46:46.000Z","size":510,"stargazers_count":2136,"open_issues_count":22,"forks_count":149,"subscribers_count":34,"default_branch":"master","last_synced_at":"2024-12-06T11:06:23.972Z","etag":null,"topics":["empty","error","loading","placeholder","state","state-management","swift","uiviewcontroller"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/aschuch.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-09-02T18:32:32.000Z","updated_at":"2024-10-17T01:32:33.000Z","dependencies_parsed_at":"2022-08-25T03:01:39.017Z","dependency_job_id":null,"html_url":"https://github.com/aschuch/StatefulViewController","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aschuch%2FStatefulViewController","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aschuch%2FStatefulViewController/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aschuch%2FStatefulViewController/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aschuch%2FStatefulViewController/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aschuch","download_url":"https://codeload.github.com/aschuch/StatefulViewController/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":228915464,"owners_count":17991409,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["empty","error","loading","placeholder","state","state-management","swift","uiviewcontroller"],"created_at":"2024-01-05T20:16:26.871Z","updated_at":"2024-12-09T15:30:51.854Z","avatar_url":"https://github.com/aschuch.png","language":"Swift","readme":"# StatefulViewController\n\n[![Build Status](https://travis-ci.org/aschuch/StatefulViewController.svg)](https://travis-ci.org/aschuch/StatefulViewController)\n![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)\n![Swift 3.0](https://img.shields.io/badge/Swift-3.0-orange.svg)\n![Platform](https://img.shields.io/badge/platform-iOS%20%7C%20tvOS-lightgrey.svg)\n\nA protocol to enable `UIViewController`s or `UIView`s to present placeholder views based on content, loading, error or empty states.\n\n![StatefulViewController Example](Resources/example.gif)\n\n## Overview\n\nIn a networked application a view controller or custom view typically has the following states that need to be communicated to the user:\n\n* **Loading**: The content is currently loaded over the network.\n* **Content**: The content is available and presented to the user.\n* **Empty**: There is currently no content available to display.\n* **Error**: An error occurred whilst downloading content.\n\nAs trivial as this flow may sound, there are a lot of cases that result in a rather large decision tree.\n\n![Decision Tree](Resources/decision_tree.png)\n\n`StatefulViewController` is a concrete implementation of this particular decision tree. (If you want to create your own modified version, you might be interested in the [state machine](#viewstatemachine) that is used to show and hide views.)\n\n## Version Compatibility\n\nCurrent Swift compatibility breakdown:\n\n| Swift Version | Framework Version |\n| ------------- | ----------------- |\n| 3.0           | 3.x               |\n| 2.3           | 2.x               |\n| 2.2           | 1.x               |\n\n[all releases]: https://github.com/aschuch/StatefulViewController/releases\n\n## Usage\n\n\u003e This guide describes the use of the `StatefulViewController` protocol on `UIViewController`. However, you can also adopt the `StatefulViewController` protocol on any `UIViewController` subclass, such as `UITableViewController` or `UICollectionViewController`, as well as your custom `UIView` subclasses.\n\nFirst, make sure your view controller adopts to the `StatefulViewController` protocol.\n\n```swift\nclass MyViewController: UIViewController, StatefulViewController {\n    // ...\n}\n```\n\nThen, configure the `loadingView`, `emptyView` and `errorView` properties (provided by the `StatefulViewController` protocol) in `viewDidLoad`.\n\n```swift\noverride func viewDidLoad() {\n    super.viewDidLoad()\n\n    // Setup placeholder views\n    loadingView = // UIView\n    emptyView = // UIView\n    errorView = // UIView\n}\n```\n\nIn addition, call the `setupInitialViewState()` method in `viewWillAppear:` in order to setup the initial state of the controller.\n\n```swift\noverride func viewWillAppear(animated: Bool) {\n    super.viewWillAppear(animated)\n\n    setupInitialViewState()\n}\n```\n\nAfter that, simply tell the view controller whenever content is loading and `StatefulViewController` will take care of showing and hiding the correct loading, error and empty view for you.\n\n```swift\noverride func viewWillAppear(animated: Bool) {\n    super.viewWillAppear(animated)\n\n    loadDeliciousWines()\n}\n\nfunc loadDeliciousWines() {\n    startLoading()\n\n    let url = NSURL(string: \"http://example.com/api\")\n    let session = NSURLSession.sharedSession()\n    session.dataTaskWithURL(url) { (let data, let response, let error) in\n        endLoading(error: error)\n    }.resume()\n}\n```\n\n### Life cycle\n\nStatefulViewController calls the `hasContent` method to check if there is any content to display. If you do not override this method in your own class, `StatefulViewController` will always assume that there is content to display.\n\n```swift\nfunc hasContent() -\u003e Bool {\n    return datasourceArray.count \u003e 0\n}\n```\n\nOptionally, you might also be interested to respond to an error even if content is already shown. `StatefulViewController` will not show its `errorView` in this case, because there is already content that can be shown.\n\nTo e.g. show a custom alert or other unobtrusive error message, use `handleErrorWhenContentAvailable:` to manually present the error to the user.\n\n```swift\nfunc handleErrorWhenContentAvailable(error: ErrorType) {\n    let alertController = UIAlertController(title: \"Ooops\", message: \"Something went wrong.\", preferredStyle: .Alert)\n    alertController.addAction(UIAlertAction(title: \"OK\", style: .Default, handler: nil))\n    presentViewController(alertController, animated: true, completion: nil)\n}\n```\n\n\n\n### Custom Placeholder View insets\n\nPer default, StatefulViewController presents all configured placeholder views fullscreen (i.e. with 0 insets from top, bottom, left \u0026 right from the superview). In case a placeholder view should have custom insets the configured placeholderview may conform to the `StatefulPlaceholderView` protocol and override the `placeholderViewInsets` method to return custom edge insets.\n\n```swift\nclass MyPlaceholderView: UIView, StatefulPlaceholderView {\n    func placeholderViewInsets() -\u003e UIEdgeInsets {\n        return UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20)\n    }\n}\n```\n\n\n\n\u003ca name=\"viewstatemachine\"\u003e\u003c/a\u003e\n\n### View State Machine\n\n\u003e Note: The following section is only intended for those, who want to create a stateful controller that differs from the flow described above.\n\nYou can also use the underlying view state machine to create a similar implementation for your custom flow of showing/hiding views.\n\n```swift\nlet stateMachine = ViewStateMachine(view: view)\n\n// Add states\nstateMachine[\"loading\"] = loadingView\nstateMachine[\"other\"] = otherView\n\n// Transition to state\nstateMachine.transitionToState(.View(\"loading\"), animated: true) {\n    println(\"finished switching to loading view\")\n}\n\n// Hide all views\nstateMachine.transitionToState(.None, animated: true) {\n    println(\"all views hidden now\")\n}\n```\n\n## Installation\n\n#### Carthage\n\nAdd the following line to your [Cartfile](https://github.com/Carthage/Carthage/blob/master/Documentation/Artifacts.md#cartfile).\n\n```\ngithub \"aschuch/StatefulViewController\" ~\u003e 3.0\n```\n\nThen run `carthage update`.\n\n#### CocoaPods\n\nAdd the following line to your Podfile.\n\n```\npod \"StatefulViewController\", \"~\u003e 3.0\"\n```\n\nThen run `pod install` with CocoaPods 0.36 or newer.\n\n#### Manually\n\nJust drag and drop the two `.swift` files in the `StatefulViewController` folder into your project.\n\n## Tests\n\nOpen the Xcode project and press `⌘-U` to run the tests.\n\nAlternatively, all tests can be run from the terminal using [xctool](https://github.com/facebook/xctool).\n\n```bash\nxctool -scheme StatefulViewControllerTests -sdk iphonesimulator test\n```\n\n## Todo\n\n* Default loading, error, empty views\n* Protocol on views that notifies them of removal and add\n* Views can provide delays in order to tell the state machine to show/remove them only after a specific delay (e.g. for hide and show animations)\n\n\n## Contributing\n\n* Create something awesome, make the code better, add some functionality,\n  whatever (this is the hardest part).\n* [Fork it](http://help.github.com/forking/)\n* Create new branch to make your changes\n* Commit all your changes to your branch\n* Submit a [pull request](http://help.github.com/pull-requests/)\n\n\n## Contact\n\nFeel free to get in touch.\n\n* Website: \u003chttp://schuch.me\u003e\n* Twitter: [@schuchalexander](http://twitter.com/schuchalexander)\n","funding_links":[],"categories":["UI","Libs","UI [🔝](#readme)","uiviewcontroller"],"sub_categories":["TextField \u0026 TextView","UI","Layout","Other free courses"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faschuch%2FStatefulViewController","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faschuch%2FStatefulViewController","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faschuch%2FStatefulViewController/lists"}