EmptyStateKit

Empty states are actually a great way to engage, keep and delight users at critical moments. Use EmptyStateKit to make your app even more interesting and add a bit of fun to the UI interactions whenever the view has no content to display.

EmptyStateKitz

Display awesome placeholder empty state in any UIView or UITableView/UICollectionView with refresh content features. It is super-useful to speed up a mobile development workflow due to its flexibility and ease of use. EmptyStateKit is written in Swift 5.

  • [x] Awesome fade & scale animations
  • [x] Totally customizable
  • [x] Use it also in your own tables and collection views
  • [x] Easy usage
  • [x] Supports iOS, developed in Swift 5

Example

To run the example project, clone the repo, and run pod install from the Example directory first.

video

Installation

EmptyStateKit is available through CocoaPods. To install
it, simply add the following line to your Podfile and run pod install:

pod 'EmptyStateKit'

Then you can import it when you need

import EmptyStateKit

Usage

It's important, when an error or empty state occurs, to inform or teach the user how to recover from the error or how to populate the screen with content. Keep in mind to give your users a next step, like  a link to another section, a button to recover from an error or content to interact with. This will help you to increase engagement! In the example you will see multiple types of empty states and how you can customize them. Be creative!.


Once you've installed the pod, follow next steps. It's really simple:

1. Complete empty states data

To complete empty states data, you can choose implementing EmptyStateDataSource or extend your custom enum states with image, title, description and titleButton params.

I recommend the second option to keep states data outside your view controller.

Using EmptyStateDataSource

Implement EmptyStateDataSource and complete with each custom state you have.

extension ViewController: EmptyStateDataSource {

    func imageForState(_ state: CustomState, inEmptyState emptyState: EmptyState) -> UIImage? {
        switch state as! MainState {
        case .noInternet: return UIImage(named: "Internet")
        }
    }

    func titleForState(_ state: CustomState, inEmptyState emptyState: EmptyState) -> String? {
        switch state as! MainState {
        case .noInternet:  return "We’re Sorry"
        }
    }

    func descriptionForState(_ state: CustomState, inEmptyState emptyState: EmptyState) -> String? {
        switch state as! MainState {
        case .noInternet: return "Our staff is still working on the issue for better experience"
        }
    }

    func titleButtonForState(_ state: CustomState, inEmptyState emptyState: EmptyState) -> String? {
        switch state as! MainState {
        case .noInternet: return "Try again?"
        }
    }
}

Remember to assign dataSource to your empty state view

view.emptyState.dataSource = self

Using CustomState enum

You can implement image, title, description and titleButton params for each state. Create your custom states enum that implements CustomState. Then complete with required params:

enum State: CustomState {

    case noNotifications
    case noSearch
    case noInternet

    var image: UIImage? {
        switch self {
        case .noNotifications: return UIImage(named: "Messages")
        case .noSearch: return UIImage(named: "Search")
        case .noInternet: return UIImage(named: "Internet")
        }
    }

    var title: String? {
        switch self {
        case .noNotifications: return "No message notifications"
        case .noSearch: return "No results"
        case .noInternet: return "We’re Sorry"
        }
    }

    var description: String? {
        switch self {
        case .noNotifications: return "Sorry, you don't have any message. Please come back later!"
        case .noSearch: return "Please try another search item"
        case .noInternet: return "Our staff is still working on the issue for better experience"
        }
    }

    var titleButton: String? {
        switch self {
        case .noNotifications: return "Search again?"
        case .noSearch: return "Go back"
        case .noInternet: return "Try again?"
        }
    }
}

2. Show or hide your empty state

This step is really simple ;)

view.emptyState.show(State.noInternet)

You can use empty states in your Table or Collection views maintaining refreshing content features. For this, table or collection view must be empty (0 rows). EmptyStateKit will complete the table or collection background view and remove the separatorStyle to show your empty state.

3. Handling button action with EmptyStateDelegate

Implement EmptyStateDelegate protocol to delegate the button action.

extension ViewController: EmptyStateDelegate {

    func emptyState(emptyState: EmptyState, didPressButton button: UIButton) {
        view.emptyState.hide()
    }
}

Remember to assign delegate to your empty state view

view.emptyState.delegate = self

Format

You can format your empty states or use default values. See EmptyStateFormat default values:

/// Title attributes
public var titleAttributes: [NSAttributedString.Key: Any] = [.font: UIFont(name: "AvenirNext-DemiBold", size: 26)!, .foregroundColor: UIColor.darkGray]

/// Description attributes
public var descriptionAttributes: [NSAttributedString.Key: Any] = [.font: UIFont(name: "Avenir Next", size: 14)!, .foregroundColor: UIColor.darkGray]

/// Button attributes
public var buttonAttributes: [NSAttributedString.Key: Any] = [.font: UIFont(name: "AvenirNext-DemiBold", size: 14)!, .foregroundColor: UIColor.white]

/// Button color
public var buttonColor: UIColor = .red

/// Button shadow radius
public var buttonShadowRadius: CGFloat = 0

/// Button corner radius
public var buttonRadius: CGFloat = 20.0

/// Button width, nil = auto
public var buttonWidth: CGFloat? = nil

/// Button top margin
public var buttonTopMargin: CGFloat = 20

/// Set image as background
public var coverImage: Bool = false

/// Image animation type
public var animation: EmptyStateAnimation? = .scale(0.3, 0.3)

/// Alpha container
public var alpha: CGFloat = 1.0

/// Background color
public var backgroundColor: UIColor = .white

/// Background Gradient color
public var gradientColor: (UIColor, UIColor)? = nil

/// Position
public var position = EmptyStatePosition()

/// Margin for vertical position
public var verticalMargin: CGFloat = 40

/// Left & right margin
public var horizontalMargin: CGFloat = 40

/// Image size
public var imageSize: CGSize = CGSize(width: 200, height: 200)

Remember to assign format object to your empty state view.

view.emptyState.format = format

Animation Types

Fade

This type will make first fade animation with text and second fade animation with the image when empty state appears

format.animation = EmptyStateAnimation.fade(0.3, 0.3) 

(Text fade animation duration, Image fade animation duration)

Scale

This type will make first fade animation with text and second scale animation with the image when empty state appears

format.animation = EmptyStateAnimation.scale(0.3, 0.3) 

(Text fade animation duration, Image scale animation duration)

Position Types

EmptyStateViewPosition

public enum EmptyStateViewPosition {
    case top
    case center
    case bottom
}

EmptyStateTextPosition

public enum EmptyStateTextPosition {
    case left
    case center
    case right
}

EmptyStateImagePosition

FYI: Content mode images are Aspect Fit

public enum EmptyStateImagePosition {
    case top
    case bottom
    case cover(MarginTop?, MarginBottom?) 
}

Cover image will be setted by (Cover margin top image, Cover margin bottom image)

GitHub