SwiftEntryKit is a presentation library for iOS. It can be used to easily display overlays within your iOS apps.

Overview

SwiftEntryKit

Platform Language Version Carthage compatible Accio: Supported License

๐Ÿค— Donations can be made here.

Table of Contents

Overview

SwiftEntryKit is a simple yet versatile content presenter written in Swift.

Features

Banners or pop-ups are called Entries.

  • Entries are displayed inside a separate UIWindow (of type EKWindow), so users are able to navigate the app freely while entries are being displayed in a non intrusive manner.
  • The kit offers beautiful presets that can be themed with your own colors and fonts.
  • Customization: Entries are highly customizable
    • Can be positioned either at the top, center, or the bottom of the screen.
    • Can be displayed within or outside the screen safe area.
    • Can be stylized: have a border, drop-shadow and round corners.
    • Their content and the surrounding background can be blurred, dimmed, colored or have a gradient style.
    • Transition animations are customizable - entrance, exit and pop (by another entry).
    • The user interaction with the entry or the screen can be intercepted.
    • Entries can be enqueued or override previous entries using the precedence attribute.
    • Each entry has a display priority attribute. That means that it can be dismissed only by other entry with an equal or higher priority.
    • Presets support accessibility.
    • Entries have an optional rubber banding effect while panning.
    • Entries can be optionally dismissed using a simple swipe gesture.
    • Entries can be optionally injected with lifecycle events: will and did appear/disappear.
    • The status bar style is settable for the display duration of the entry.
    • Supports navigation controllers & custom views as well!

Example Project

The example project contains various presets and examples you can use and modify as your like.

Example Project Installation

You can either use the terminal or git client such as Source Tree. The zip file doesn't contain a necessary dependency (QuickLayout).

Terminal Users

Run git clone with --recurse-submodules, to include QuickLayout as submodule, likewise:

$ git clone --recurse-submodules https://github.com/huri000/SwiftEntryKit.git

Git Client (Source Tree)

Cloning from https://github.com/huri000/SwiftEntryKit.git also setups QuickLayout as submodule.

Presets

Toasts Notes Floats Popups
toasts_example notes_example floats_example popup_example
Alerts Forms Rating More...
alert_example form_example rating_example custom_example

Playground

noun: a place where people can play ๐Ÿˆ

The example app contains a playground screen, an interface that allows you to customize your preferable entries. The playground screen has some limitations (allows to select constant values) but you can easily modify the code to suit your needs. Check it out!

The Playground Screen Top Toast Sample
playground_example playground-sample-1

Requirements

  • iOS 9 or any higher version.
  • Xcode 9 or any higher version.
  • Swift 4.0 or any higher version.
  • The library has not been tested with iOS 8.x.y or a lower version.
  • SwiftEntryKit leans heavily on QuickLayout - A lightweight library written in Swift that is used to easily layout views programmatically.

Installation

  • SwiftEntryKit is compatible with Swift 5 as of release 1.0.0.
  • SwiftEntryKit is compatible with Swift 4.2 as of release 0.8.1.
  • Developers who use lower Swift version should install release 0.7.2.

CocoaPods

CocoaPods is a dependency manager for Cocoa projects. You can install it with the following command:

$ gem install cocoapods

To integrate SwiftEntryKit into your Xcode project using CocoaPods, specify it in your Podfile:

source 'https://github.com/cocoapods/specs.git'
platform :ios, '9.0'
use_frameworks!

pod 'SwiftEntryKit', '1.2.7'

Then, run the following command:

$ pod install

Carthage

Carthage is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks.

You can install Carthage with Homebrew using the following command:

$ brew update
$ brew install carthage

To integrate SwiftEntryKit into your Xcode project using Carthage, specify the following in your Cartfile:

github "huri000/SwiftEntryKit" == 1.2.7

Accio

Accio is a decentralized dependency manager driven by SwiftPM that works for iOS/tvOS/watchOS/macOS projects.

You can install Accio with Homebrew using the following command:

$ brew tap JamitLabs/Accio https://github.com/JamitLabs/Accio.git
$ brew install accio

To integrate SwiftEntryKit into your Xcode project using Accio, specify the following in your Package.swift manifest:

.package(url: "https://github.com/huri000/SwiftEntryKit", .exact("1.2.7"))

After specifying "SwiftEntryKit" as a dependency of the target in which you want to use it, run accio install.

Usage

Quick Usage

No setup is needed! Each time you wish to display an entry, just create your view and initialize an EKAttributes struct. See also the preset usage example, and the example project. likewise:

// Customized view
let customView = SomeCustomView()
/*
Do some customization on customView
*/

// Attributes struct that describes the display, style, user interaction and animations of customView.
var attributes = EKAttributes()
/*
Adjust preferable attributes
*/

And then, just call:

SwiftEntryKit.display(entry: customView, using: attributes)

The kit will replace the application main window with the EKWindow instance and display the entry.

Entry Attributes

EKAttributes is the entry's descriptor. Each time an entry is displayed, an EKAttributes struct is necessary to describe the entry's presentation, position inside the screen, the display duration, its frame constraints (if needed), its styling (corners, border and shadow), the user interaction events, the animations (in / out) and more.

Create a mutable EKAttributes structure likewise:

var attributes = EKAttributes()

Below are the properties that can be modified in the EKAttributes:

Entry Name

Entries can have names. When an EKAttributes struct is instantiated, it is nameless, meaning, the name property is nil. It is recommended to set a meaningful name for an entry.

attributes.name = "Top Note"

Entries with names can be specifically referred to later, for example, you can inquire whether a specific entry is currently displayed:

if SwiftEntryKit.isCurrentlyDisplaying(entryNamed: "Top Note") {
    /* Do your things */
}

Window Level

Entries can be displayed above the application main window, above the status bar, above the alerts window or even have a custom level (UIWindowLevel).

For example, set the window level to normal, likewise:

attributes.windowLevel = .normal

This causes the entry to appear above the application key window and below the status bar.

The default value of windowLevel is .statusBar.

Display Position

The entry can be displayed either at the top, center, or the bottom of the screen.

For example, set the display position to bottom, likewise:

attributes.position = .bottom

The default value of position is .top.

Precedence

The precedence attribute of an entry describes the manner in which entries are pushed in. It offers 2 approaches for managing the presentation priority of multiple simultaneous entries.

Override

If the display priority is equal or higher than the currently displayed entry, override it.

Example for setting .override precedence with .max display priority while ignoring entries that are already enqueued (leaving them to display after the new entry is dismissed).

attributes.precedence = .override(priority: .max, dropEnqueuedEntries: false)

You can optionally flush the entries that are inside the queue.

In case dropEnqueuedEntries is false, enqueued entries remain in the queue. The first enqueued entry will show right after the new entry pops out. In case dropEnqueuedEntries is true, the entry-queue is flushed as the new entry is being displayed.

Enqueue

If the queue is empty, display the entry immediately, otherwise, insert the entry into the queue until its turn to show arrives.

Example for setting .enqueue precedence with .normal display priority:

attributes.precedence = .enqueue(priority: .normal)
Heuristics

There are 2 possible heuristics for entries prioritization in the queue:

  • Display Priority Queue: The entries are sorted by their display priority, then by chronological order.
  • Chronological Queue: The entries are sorted only by their chronological order (standard queue).

Select the heuristic that suits you best by doing the following, only once, before using SwiftEntryKit to display entries.

EKAttributes.Precedence.QueueingHeuristic.value = .priority

Or:

EKAttributes.Precedence.QueueingHeuristic.value = .chronological

The default value of EKAttributes.Precedence.QueueingHeuristic.value is .priority.

The default value of precedence is .override(priority: .normal, dropEnqueuedEntries: false).

Display Priority

The display priority of the entry determines whether it dismisses other entries or is dismissed by them. An entry can be dismissed only by an entry with an equal or a higher display priority.

let highPriorityAttributes = EKAttributes()
highPriorityAttributes.precedence.priority = .high

let normalPriorityAttributes = EKAttributes()
normalPriorityAttributes.precedence.priority = .normal

// Display high priority entry
SwiftEntryKit.display(entry: view1, using: highPriorityAttributes)

// Display normal priority entry (ignored!)
SwiftEntryKit.display(entry: view2, using: normalPriorityAttributes)

view2 won't be displayed!

Display Duration

The display duration of the entry (Counted from the moment the entry has finished its entrance animation and until the exit animation begins).

Display for 4 seconds:

attributes.displayDuration = 4

Display for an infinite duration

attributes.displayDuration = .infinity

The default value of displayDuration is 2.

Position Constraints

Constraints that tie the entry tightly to the screen context, for example: Height, Width, Max Width, Max Height, Additional Vertical Offset & Safe Area related info.

  • Entries that support Auto Layout - Their height is inferred from the constraints that applied to them.
  • Entries that don't support Auto Layout - Their exact size must be explicitly set using positionConstraints's size property.

For example:

Ratio edge - signifies that the ratio of the width edge has a ratio of 0.9 of the screen's width.

let widthConstraint = EKAttributes.PositionConstraints.Edge.ratio(value: 0.9)

Intrinsic edge - signifies that the wanted height value is the content height - Decided by the entries vertical constraints

let heightConstraint = EKAttributes.PositionConstraints.Edge.intrinsic

Create the entry size constraints likewise:

attributes.positionConstraints.size = .init(width: widthConstraint, height: heightConstraint)

You can also set attributes.positionConstraints.maxSize in order to make sure the entry does not exceeds predefined limitations. This is useful on device orientation change.

Safe Area - can be used to override the safe area or to color it (More examples are in the example project) That snippet implies that the safe area insets should be kept and not be a part of the entry.

attributes.positionConstraints.safeArea = .empty(fillSafeArea: false)

Vertical Offset - an additional offset that can be applied to the entry (Other than the safe area).

attributes.positionConstraints.verticalOffset = 10

Autorotation - whether the entry autorotates along with the orientation of the device. Defaults to true.

attributes.positionConstraints.rotation.isEnabled = false

Keyboard Releation - used to bind an entry to the keyboard once the keyboard is displayed.

let offset = EKAttributes.PositionConstraints.KeyboardRelation.Offset(bottom: 10, screenEdgeResistance: 20)
let keyboardRelation = EKAttributes.PositionConstraints.KeyboardRelation.bind(offset: offset)
attributes.positionConstraints.keyboardRelation = keyboardRelation

In the example above the entry's bottom is tuned to have a 10pts offset from the top of the keyboard (while it shows) Because the entry's frame might exceed the screen bounds, the user might not see all the entry - we wouldn't want that. Therefore, an additional associated value has been added - screenEdgeResistance with value of 20pts. That is, to make sure that the entry remains within the bounds of the screen, and always visible to the user. The extreme situation might occur as the device orientation is landscape and the keyboard shows up (See example project form presets for more information).

User Interaction

The entry and the screen can be interacted by the user. User interaction be can intercepted in various ways:

An interaction (Any touch whatsoever) with the entry delays its exit by 3s:

attributes.entryInteraction = .delayExit(by: 3)

A tap on the entry / screen dismisses it immediately:

attributes.entryInteraction = .dismiss
attributes.screenInteraction = .dismiss

A tap on the entry is swallowed (ignored):

attributes.entryInteraction = .absorbTouches

A tap on the screen is forwarded to the lower level window, in most cases the receiver will be the application window. This is very useful when you want to display an unintrusive content like banners and push notification entries.

attributes.screenInteraction = .forward

Pass additional actions that are invoked when the user taps the entry:

let action = {
    // Do something useful
}
attributes.entryInteraction.customTapActions.append(action)

The default value of screenInteraction is .forward.

The default value of entryInteraction is .dismiss.

Scroll Behavior

Describes the entry behavior when it's being scrolled, that is, dismissal by a swipe gesture and a rubber band effect much similar to a UIScrollView.

Disable the pan and swipe gestures on the entry:

attributes.scroll = .disabled

Enable swipe and stretch and pullback with jolt effect:

attributes.scroll = .enabled(swipeable: true, pullbackAnimation: .jolt)

Enable swipe and stretch and pullback with an ease-out effect:

attributes.scroll = .enabled(swipeable: true, pullbackAnimation: .easeOut)

Enable swipe but disable stretch:

attributes.scroll = .edgeCrossingDisabled(swipeable: true)

The default value of scroll is .enabled(swipeable: true, pullbackAnimation: .jolt).

Haptic Feedback

The device can produce a haptic feedback, thus adding an additional sensory depth to each entry.

The default value of hapticFeedbackType is .none.

Lifecycle Events

Events can be injected to the entry so that they are to be called during its lifecycle.

attributes.lifecycleEvents.willAppear = {
    // Executed before the entry animates inside 
}

attributes.lifecycleEvents.didAppear = {
    // Executed after the entry animates inside
}

attributes.lifecycleEvents.willDisappear = {
    // Executed before the entry animates outside
}

attributes.lifecycleEvents.didDisappear = {
    // Executed after the entry animates outside
}

Display Mode

To allow you to fully support any user interface style, SwiftEntryKit introduces two specialized types:

  • EKColor describes a color under light and dark modes.
  • EKAttributes.BackgroundStyle.BlurStyle describes a blur effect under light and dark modes.

The following forces SwiftEntryKit to display the entry on dark mode.

attributes.displayMode = .dark

The possible values are: .light, .dark, .inferred. The default value is .inferred, which means that the entry will be displayed with the current user interface style.

Background Style

The entry and the screen can have various background styles, such as blur, color, gradient and even an image.

The following example implies clear background for both the entry and the screen:

attributes.entryBackground = .clear
attributes.screenBackground = .clear

Colored entry background and dimmed screen background:

attributes.entryBackground = .color(color: .standardContent)
attributes.screenBackground = .color(color: EKColor(UIColor(white: 0.5, alpha: 0.5)))

Gradient entry background (diagonal vector):

let colors: [EKColor] = ...
attributes.entryBackground = .gradient(gradient: .init(colors: colors, startPoint: .zero, endPoint: CGPoint(x: 1, y: 1)))

Visual Effect entry background:

attributes.entryBackground = .visualEffect(style: .dark)

The default value of entryBackground and screenBackground is .clear.

Shadow

The shadow that surrounds the entry.

Enable shadow around the entry:

attributes.shadow = .active(with: .init(color: .black, opacity: 0.3, radius: 10, offset: .zero))

Disable shadow around the entry:

attributes.shadow = .none

The default value of shadow is .none.

Round Corners

Round corners around the entry.

Only top left and right corners with radius of 10:

attributes.roundCorners = .top(radius: 10)

Only bottom left and right corners with radius of 10:

attributes.roundCorners = .bottom(radius: 10)

All corners with radius of 10:

attributes.roundCorners = .all(radius: 10)

No round corners:

attributes.roundCorners = .none

The default value of roundCorners is .none.

Border

The border around the entry.

Add a black border with thickness of 0.5pts:

attributes.border = .value(color: .black, width: 0.5)

No border:

attributes.border = .none

The default value of border is .none.

Animations

Describes how the entry animates into and out of the screen.

  • Each animation descriptor can have up to 3 types of animations at the same time. Those can be combined to a single complex one!
  • Translation animation anchor can be explicitly set but it receives a default value according to position of the entry.

Example for translation from top with spring, scale in and even fade in as a single entrance animation:

attributes.entranceAnimation = .init(
                 translate: .init(duration: 0.7, anchorPosition: .top, spring: .init(damping: 1, initialVelocity: 0)), 
                 scale: .init(from: 0.6, to: 1, duration: 0.7), 
                 fade: .init(from: 0.8, to: 1, duration: 0.3))

The default value of entranceAnimation and exitAnimation is .translation - The entry translates in or out, respectively, with duration of 0.3 seconds.

Pop Behavior

Describes the entry behavior when it's being popped (dismissed by an entry with equal / higher display-priority.

The entry is being popped animatedly:

attributes.popBehavior = .animated(animation: .init(translate: .init(duration: 0.2)))

The entry is being overridden (Disappears promptly):

attributes.popBehavior = .overridden

The default value of popBehavior is .animated(animation: .translation) - It translates out with duration of 0.3 seconds.

Status Bar

The status bar appearance can be modified during the display of the entry. SwiftEntryKit supports both View controller-based status bar appearance and manual setting.

Setting the status bar style is fairly simple -

Status bar becomes visible and gets a light style:

attributes.statusBar = .light

The status bar becomes hidden:

attributes.statusBar = .hidden

The status bar appearance is inferred from the previous context (won't be changed):

attributes.statusBar = .inferred

In case there is an already presenting entry with lower/equal display priority, the status bar will change its style. When the entry is removed, the status bar gets its initial style back.

The default value of statusBar is .inferred.

EKAttributes' interface is as follows:

public struct EKAttributes

    // Identification
    public var name: String?

    // Display
    public var windowLevel: WindowLevel
    public var position: Position
    public var precedence: Precedence
    public var displayDuration: DisplayDuration
    public var positionConstraints: PositionConstraints

    // User Interaction
    public var screenInteraction: UserInteraction
    public var entryInteraction: UserInteraction
    public var scroll: Scroll
    public var hapticFeedbackType: NotificationHapticFeedback
    public var lifecycleEvents: LifecycleEvents

    // Theme & Style
    public var displayMode = DisplayMode.inferred
    public var entryBackground: BackgroundStyle
    public var screenBackground: BackgroundStyle
    public var shadow: Shadow
    public var roundCorners: RoundCorners
    public var border: Border
    public var statusBar: StatusBar
    
    // Animations
    public var entranceAnimation: Animation
    public var exitAnimation: Animation
    public var popBehavior: PopBehavior
}

Presets Usage Example:

You can use one of the presets that come with SwiftEntryKit, doing these 4 simple steps:

  1. Create your EKAttributes struct and set your preferable properties.
  2. Create EKNotificationMessage struct (The Content) and set the content.
  3. Create EKNotificationMessageView (The View) and inject EKNotificationMessage struct to it.
  4. Display the entry using SwiftEntryKit class method.

EKNotificationMessageView preset example:

// Generate top floating entry and set some properties
var attributes = EKAttributes.topFloat
attributes.entryBackground = .gradient(gradient: .init(colors: [EKColor(.red), EKColor(.green)], startPoint: .zero, endPoint: CGPoint(x: 1, y: 1)))
attributes.popBehavior = .animated(animation: .init(translate: .init(duration: 0.3), scale: .init(from: 1, to: 0.7, duration: 0.7)))
attributes.shadow = .active(with: .init(color: .black, opacity: 0.5, radius: 10, offset: .zero))
attributes.statusBar = .dark
attributes.scroll = .enabled(swipeable: true, pullbackAnimation: .jolt)
attributes.positionConstraints.maxSize = .init(width: .constant(value: UIScreen.main.minEdge), height: .intrinsic)

let title = EKProperty.LabelContent(text: titleText, style: .init(font: titleFont, color: textColor))
let description = EKProperty.LabelContent(text: descText, style: .init(font: descFont, color: textColor))
let image = EKProperty.ImageContent(image: UIImage(named: imageName)!, size: CGSize(width: 35, height: 35))
let simpleMessage = EKSimpleMessage(image: image, title: title, description: description)
let notificationMessage = EKNotificationMessage(simpleMessage: simpleMessage)

let contentView = EKNotificationMessageView(with: notificationMessage)
SwiftEntryKit.display(entry: contentView, using: attributes)

Custom View Usage Example:

// Create a basic toast that appears at the top
var attributes = EKAttributes.topToast

// Set its background to white
attributes.entryBackground = .color(color: .white)

// Animate in and out using default translation
attributes.entranceAnimation = .translation
attributes.exitAnimation = .translation

let customView = UIView()
/*
... Customize the view as you like ...
*/

// Display the view with the configuration
SwiftEntryKit.display(entry: customView, using: attributes)

Displaying a View Controller

As from version 0.4.0, view controllers are supported as well.

SwiftEntryKit.display(entry: customViewController, using: attributes)

Alternative Rollback Window

By default, the window held by the application delegate becomes the key again right after SwiftEntryKit has finished displaying the entry. This behavior can be changed using rollbackWindow parameter.

SwiftEntryKit.display(entry: view, using: attributes, rollbackWindow: .custom(window: alternativeWindow))

After the entry has been dismissed, the given window alternativeWindow would become the key instead of the window that is held by the application delegate.

Dismissing an Entry

You can dismiss the currently displayed entry by simply invoke dismiss in the SwiftEntryKit class, likewise:

SwiftEntryKit.dismiss()

Or:

SwiftEntryKit.dismiss(.displayed)

This dismisses the entry animatedly using its exitAnimation attribute and on completion, the window would be removed as well.

You can dismiss the currently displayed entry and flush the queue as well, likewise:

SwiftEntryKit.dismiss(.all)

Only flush the queue, leaving any currently displayed entry to its natural lifecycle:

SwiftEntryKit.dismiss(.queue)

Dismiss a specific entry by name - either currently displayed or enqueued. All the entries with the given name are dismissed.

SwiftEntryKit.dismiss(.specific(entryName: "Entry Name"))

Dismiss any entry with a lower or equal display priority of .normal.

SwiftEntryKit.dismiss(.prioritizedLowerOrEqualTo(priority: .normal))

Using a completion handler

Inject a trailing closure to be executed after the entry dismissal.

SwiftEntryKit.dismiss {
    // Executed right after the entry has been dismissed
}

Is Currently Displaying

Inquire whether an entry is currently displayed:

if SwiftEntryKit.isCurrentlyDisplaying {
    /* Do your things */
}

Inquire whether a specific entry is currently displayed using the name property inside EKAttributes.

if SwiftEntryKit.isCurrentlyDisplaying(entryNamed: "Top Note") {
/* Do your things */
}

Queue Contains

Inquire whether the queue of entries is not empty:

if SwiftEntryKit.isQueueEmpty {
    /* Do your things */
}

Inquire whether the queue of entries contains an entry with name:

if SwiftEntryKit.queueContains(entryNamed: "Custom-Name") {
    /* Do your things */
}

Swiping and Rubber Banding

Entries can be panned vertically (This ability can be enabled using the scroll attributes). Thefore it's only natural that an entry can be dismissed using a swipe-like gesture.

Enable swipe gesture. When the swipe gesture fails (doesn't pass the velocity threshold) ease it back.

attributes.scroll = .enabled(swipeable: true, pullbackAnimation: .easeOut)

Enable swipe gesture. When the swipe gesture fails throw it back out with a jolt.

attributes.scroll = .enabled(swipeable: true, pullbackAnimation: .jolt)

The PullbackAnimation values (duration, damping & initialSpringVelocity) can be customized as well.

Swipe Jolt
swipe_example band_example

Dealing with safe area:

EKAttributes.PositionConstraints.SafeArea may be used to override the safe area with the entry's content, or to fill the safe area with a background color (like Toasts do), or even leave the safe area empty (Like Floats do).

SwiftEntryKit supports iOS 11.x.y and is backward compatible to iOS 9.x.y, so the status bar area is treated as same as the safe area in earlier iOS versions.

Dealing with orientation change:

SwiftEntryKit identifies orientation changes and adjust the entry's layout to those changes. Therefore, if you wish to limit the entries's width, you are able to do so by giving it a maximum value, likewise:

var attributes = EKAttributes.topFloat

// Give the entry the width of the screen minus 20pts from each side, the height is decided by the content's contraint's
attributes.positionConstraints.size = .init(width: .offset(value: 20), height: .intrinsic)

// Give the entry maximum width of the screen minimum edge - thus the entry won't grow much when the device orientation changes from portrait to landscape mode.
let edgeWidth = min(UIScreen.main.bounds.width, UIScreen.main.bounds.height)
attributes.positionConstraints.maxSize = .init(width: .constant(value: edgeWidth), height: .intrinsic)

let customView = UIView()
/*
... Customize the view as you like ...
*/

// Use class method of SwiftEntryKit to display the view using the desired attributes
SwiftEntryKit.display(entry: customView, using: attributes)
Orientation Change Demonstration
orientation_change

Dark Mode in the Example Project

You can tinker with the display mode using a segmented control on presets screen, forcing light and dark modes. All the presets are dark mode ready, but only some in the example project demonstrate dark mode capabilities.

light_dark

Swift and Objective-C Interoperability

SwiftEntryKit's APIs use the Swift language exclusive syntax (enums, associated values, and more). Therefore, SwiftEntryKit cannot be referenced directly from an Objective-C file (.m, .h or .mm).

Yet, it is pretty easy to integrate SwiftEntryKit into an Objective-C project using a simple .swift class that is a sort of adapter between SwiftEntryKit and your Objective-C code.

This project demonstrates that using Carthage and CocoaPods.

Author

Daniel Huri, [email protected]

Donations

Donations can be made by sending either Bitcoin or Ether to the following addresses.

BTC ETH
134TiBiUvVNt7Na5KXEFBSChLdgVDw1Hnr 0xAe6616181FCdde4793AE749Ce21Cd5Af9333A3E2
btc_address eth_address

Thank You

Thanks Lily Azar, [email protected] for those awesome preset icons.

Credits

Icons Credits

License

SwiftEntryKit is available under the MIT license. See the LICENSE file for more info.

Exceptions

Please be aware that any use of the icons inside the project requires attribution to the creator. See credits for the creators list.

Comments
  • Unable to use example project

    Unable to use example project

    When I build the example project, I am getting "no such module 'QuickLayout' in the file EKMessageContentView.swift. And on the project navigator, I see a red "QuickLayout.xcodeproj".

    What should I do here?

    opened by junweimah 25
  • Unable to dismiss a ViewControllerEntry in Xcode 10.2 (work in Xcode 10.1)

    Unable to dismiss a ViewControllerEntry in Xcode 10.2 (work in Xcode 10.1)

    Describe the bug Unable to dismiss a ViewControllerEntry by a "screenInteraction" in Xcode 10.2. However, it works in Xcode 10.1.

    To Reproduce Steps to reproduce the behavior:

    1. Open a ViewControllerEntry
    2. Click outside the entry (screenInteraction)
    3. Even if the screenInteraction is set to .dismiss, the behavior works like a .forward

    Expected behavior When I click outside the entry, I would like to dismiss the entry. To do this, I set : attributes.screenInteraction = .dismiss

    Configuration

    • iPhone X, iPhone XS, iPhone XR on Simulator
    • iOS 12.x.x
    • Xcode Version 10.2
    • SwiftEntryKit 0.8.6 and SwiftEntryKit 0.8.9

    Additional context My configuration works perfectly on Xcode 10.1, and bug in Xcode 10.2

    Thanks in advance,

    Rรฉmi Rodrigues,

    help wanted 
    opened by RemiRodrigues 20
  • Multiple button support?

    Multiple button support?

    Is there a way to display multiple buttons on top of each other in such a way that they're stacked in the view? I briefly saw the documentation and saw that action-methods are supported, but how would one pass n-number of buttons, each with different delegate methods?

    enhancement 
    opened by HackShitUp 20
  • Crash On Displaying Entry

    Crash On Displaying Entry

    Describe the bug I followed the example on displaying a bottom float. I create the attributes and call display but it crashes as soon as I try to display it

    @objc func handleAttend(isAttending:Bool){
        
        var attributes = EKAttributes.bottomFloat
        attributes.hapticFeedbackType = .success
        attributes.entryBackground = .color(color: .white)
        attributes.entryInteraction = .delayExit(by: 3)
        attributes.scroll = .enabled(swipeable: true, pullbackAnimation: .jolt)
        attributes.statusBar = .dark
        attributes.positionConstraints.maxSize = .init(width: .constant(value: UIScreen.main.minEdge), height: .intrinsic)
                
        floatCellSelected(with: attributes, row: 0)
        
        // 2
        if (isAttending) {
            
            AttendService.setIsAttending(!((isAttending)), from: currentEvent) { [unowned self] (success) in
                // 5
                
                defer {
                    self.isUserInteractionEnabled = true
                }
                
                // 6
                guard success else { return }
                
                // 7
                self.currentEvent?.isAttending = !((self.currentEvent!.isAttending))
                
                self.currentEvent?.currentAttendCount += !((self.currentEvent!.isAttending)) ? 1 : -1
                self.actionLabel.text = "Confirm Attendance"
            }
            
        }else{
            
            AttendService.setIsAttending(!((isAttending)), from: currentEvent) {[unowned self] (success) in
                // 5
                
                defer {
                    self.isUserInteractionEnabled = true
                }
                
                guard success else { return }
                
                self.currentEvent?.isAttending = !((self.currentEvent!.isAttending))
                
                self.currentEvent?.currentAttendCount += !((self.currentEvent!.isAttending)) ? 1 : -1
                self.actionLabel.text = "Attendance Confirmed"
    
            }
            
        }
        
    }
    
    //float for attendacne confirmation
    private func floatCellSelected(with attributes: EKAttributes, row: Int) {
        guard let eventTitle = currentEvent?.currentEventName.capitalized else {
            return
        }
        guard let eventImage = currentEvent?.currentEventImage else{
            return
        }
        
        
        Alamofire.request(eventImage).responseImage { response in
            debugPrint(response)
            if let image = response.result.value {
                
                switch row {
                case 0:
                    let title = "Attendance Removed"
                    let desc = "Sorry to see that you changed your mind about attending \(eventTitle)"
                    self.showNotificationMessage(attributes: attributes, title: title, desc: desc, textColor: .black, imageName: image)
                case 1:
                    let title = "Attendance Confirmed"
                    let desc = "Congratulations your attendance has been confirmed for \(eventTitle)"
                    self.showNotificationMessage(attributes: attributes, title: title, desc: desc, textColor: .black, imageName: image)
                default:
                    break
                }
            }
        }
    
    }
    
    
    // Bumps a notification structured entry
    private func showNotificationMessage(attributes: EKAttributes, title: String, desc: String, textColor: UIColor, imageName: UIImage? = nil) {
        let title = EKProperty.LabelContent(text: title, style: .init(font: MainFont.medium.with(size: 16), color: textColor))
        let description = EKProperty.LabelContent(text: desc, style: .init(font: MainFont.light.with(size: 14), color: textColor))
        var image: EKProperty.ImageContent?
        if let imageName = imageName {
            image?.image = imageName
        }
        
        let simpleMessage = EKSimpleMessage(image: image, title: title, description: description)
        let notificationMessage = EKNotificationMessage(simpleMessage: simpleMessage)
        
        let contentView = EKNotificationMessageView(with: notificationMessage)
        SwiftEntryKit.display(entry: contentView, using: attributes)
    }
    

    Im not sure what is making it crash but I am running this on ios 12 with an iphone X and XCode 10.2 It crashes here specifically.

      public class func display(entry view: UIView, using attributes: EKAttributes, presentInsideKeyWindow: Bool = false, rollbackWindow: RollbackWindow = .main) {
            DispatchQueue.main.async {
                EKWindowProvider.shared.display(view: view, using: attributes, presentInsideKeyWindow: presentInsideKeyWindow, rollbackWindow: rollbackWindow)
            }
        }
    
    opened by Smiller193 13
  • iPhone X issue with presenting Alert right after previous was closed

    iPhone X issue with presenting Alert right after previous was closed

    Describe the bug Hello! There is issue when we are trying to show Alert when the previous one was closed. To Reproduce Steps to reproduce the behavior:

    1. show alert "centerFloat"
    2. In
    SwiftEntryKit.dismiss(with: {
                   
               })
    

    show new alert "centerFloat"

    Attribute:

    var attributes: EKAttributes attributes = .centerFloat attributes.windowLevel = .normal attributes.hapticFeedbackType = .success attributes.screenInteraction = canBeClosed ? .dismiss : .absorbTouches attributes.entryInteraction = .absorbTouches attributes.roundCorners = .all(radius: 20) attributes.scroll = .enabled(swipeable: canBeClosed ? true : false, pullbackAnimation: .jolt) attributes.screenBackground = .color(color: UIColor(white: 0, alpha: 0.7)) attributes.entryBackground = .color(color: .white) attributes.entranceAnimation = .init(scale: .init(from: 0.9, to: 1, duration: 0.4, spring: .init(damping: 1, initialVelocity: 0)), fade: .init(from: 0, to: 1, duration: 0.3)) attributes.exitAnimation = .init(scale: .init(from: 1, to: 0.9, duration: 0.4, spring: .init(damping: 1, initialVelocity: 0)), fade: .init(from: 1, to: 0, duration: 0.3)) attributes.displayDuration = .infinity attributes.border = .value(color: .black, width: 0.5) attributes.shadow = .active(with: .init(color: .black, opacity: 0.3, radius: 5, offset: .zero)) attributes.positionConstraints.maxSize = .init(width: .offset(value: CGFloat(40.0.dp)), height: .intrinsic) attributes.positionConstraints.keyboardRelation = .bind(offset: .init(bottom: 10, screenEdgeResistance: 10))

    1. iPhone X freezes

    iPhone (please complete the following information):

    • Device: only iPhone X
    • iOS Version: 11.
    • Xcode Version 9.4.1
    • Dependency Manager Version: CocoaPods 1.5.3
    • SwiftEntryKit Release # 0.5.1
    bug 
    opened by Banck 12
  • Feature request: Callback when presented and dismissed

    Feature request: Callback when presented and dismissed

    Is your feature request related to a problem? Please describe. I should have a callback when popup is presented and dismissed, to do actions after presented or dismissed popup.

    Describe the solution you'd like I should have a callback when popup is presented and dismissed.

    Describe alternatives you've considered Maybe it's possible to add a delegate with to methods when finishing the animation for presenting and same when dismissing.

    Additional context nothing

    enhancement 
    opened by pjcau 12
  • Screen interaction is disabled when status bar entry kit is being displayed

    Screen interaction is disabled when status bar entry kit is being displayed

    I am having problem where screen interaction is being disabled when the entry kit is displaying. Once the entry kit is dismissed, the screen interaction is enabled again

    Xcode 11, iOS 13, iPad application, Swift Entry Kit 1.1.3

    here are my codes

    var attributes = EKAttributes()
        attributes = .statusBar
        attributes.hapticFeedbackType = .success
        attributes.displayDuration = .infinity
        attributes.screenInteraction = .forward
        attributes.popBehavior = .animated(animation: .translation)
        attributes.entryBackground = .color(color: .satCyan)
        attributes.shadow = .active(with: .init(color: .chatMessage, opacity: 0.5, radius: 2))
        attributes.statusBar = .light
        
        let text = "hello world"
        let style = EKProperty.LabelStyle(font: MainFont.light.with(size: 14), color: .white, alignment: .center)
        let labelContent = EKProperty.LabelContent(text: text, style: style)
        let sequence = (1...4).map { "cloud\($0)" }
        let animationDuration: TimeInterval = 1
        let animation = EKProperty.ImageContent.TransformAnimation.animate(duration: animationDuration, options: [.curveEaseInOut], transform: .init(scaleX: 1.1, y: 1.1))
        let imageContent = EKProperty.ImageContent(imagesNames: sequence, imageSequenceAnimationDuration: animationDuration, animation: animation, size: CGSize(width: 20, height: 20), contentMode: .scaleAspectFit)
        
        let contentView = EKImageNoteMessageView(with: labelContent, imageContent: imageContent)
        
        SwiftEntryKit.display(entry: contentView, using: attributes)`
    

    ### UPDATE @huri000 If the sample project is run in ipad simulator, this problem will appear. Any recommendation on this?

    opened by avjiang 11
  • Xcode 10 Errors

    Xcode 10 Errors

    As you can see the attached screenshot, I got a bunch of errors when migrating to Xcode 10. I did not see these errors before. I could unlock the pod and fix the deprecated code by myself, but even after I did that, my app crashes. See the second screenshot for what I did and the error on the print line. It seems caused by EKAttributes.

    notificationbannerswift_errors notificationbannerswift_crash

    opened by MenuHunt 11
  • Swipe down to dismiss Entry

    Swipe down to dismiss Entry

    Is your feature request related to a problem? Please describe. The current version of the library although it supports custom exit animations does not seem to support swiping down to dismiss an entry.

    Describe the solution you'd like If possible, create another flag in .enable called something like direction. I want to present an Entry up from the bottom of the screen and then dismiss it by dragging down and then have it also exit from the bottom of the screen.

    Describe alternatives you've considered I've been looking through the code, especially EKContentView and EKAttributes + Scroll

    opened by asikand 10
  • Background dimmed view is NOT animating

    Background dimmed view is NOT animating

    Describe the bug In the previous versions, dimmed background view (screenBackground) was dismissed animatedly (fade out). But in this version, after dismissing our UIView/UIViewController, dimmed view is dismissing WITHOUT animation.

    To Reproduce I use this code:

    var attributes = EKAttributes.centerFloat
    attributes.entryBackground = .visualEffect(style: .extraLight)
    attributes.entranceAnimation = .init(
    	translate: nil,
    	scale: .init(from: 1.2, to: 1, duration: 0.2),
    	fade: .init(from: 0.0, to: 1, duration: 0.2)
    )
    attributes.displayDuration = .infinity
    attributes.shadow = .none
    attributes.scroll = .disabled
    attributes.entryInteraction = .absorbTouches
    attributes.screenInteraction = .absorbTouches
    attributes.positionConstraints.maxSize = .init(width: .constant(value: 300), height: .constant(value: 500))
    attributes.positionConstraints.verticalOffset = 12
    attributes.positionConstraints.size = .init(width: .offset(value: 12), height: .intrinsic)
    attributes.roundCorners = .all(radius: 8)
    attributes.screenBackground = .color(color: UIColor.black.withAlphaComponent(0.4))
    attributes.exitAnimation = .init(
    	translate: nil,
    	scale: nil,
    	fade: .init(from: 1.0, to: 0.0, duration: 0.2)
    )
    
    // setting up elements...
    
    let simpleMessage = EKSimpleMessage(image: imageContent, title: titleContent, description: descriptionContent)
    let alertMessage = EKAlertMessage(simpleMessage: simpleMessage, imagePosition: .top, buttonBarContent: buttonBarContent)
    let contentView = EKAlertMessageView(with: alertMessage)
    SwiftEntryKit.display(entry: contentView, using: attributes)
    

    iPhone (please complete the following information):

    • Device: iPhone 8
    • iOS Version: iOS 12
    • Xcode Version: 10
    • Dependency Manager Version: CocoaPods 1.4.0
    • SwiftEntryKit Release # 0.8.2

    Screenshots / Video Links screenrecording_10-27-2018 14-14-32

    bug 
    opened by omidgolparvar 10
  • Error in pod files

    Error in pod files

    Describe the bug 1- Pods/SwiftEntryKit/SwiftEntryKit/Model/EntryAttributes/EKAttributes+DisplayPriority.swift:20:19: Type 'EKAttributes.DisplayPriority' does not conform to protocol 'Hashable'

    opened by Mina-Gerges 10
  • fix: scroll need check safeAreaInsets

    fix: scroll need check safeAreaInsets

    Issue Link ๐Ÿ”—

    none

    Goals ๐Ÿฅ…

    Look at the picture. This is a problem caused by edgeCrossingDisabled and pan gestures. I don't think it should exist, but if it is designed like this, please close it.

    Implementation Details โœ๏ธ

    adjust safeArea by position

    Testing Details ๐Ÿ”

    none

    Screenshot Links ๐Ÿ“ท

    iShot_2022-12-02_12 14 37
    opened by ssyzh 0
  • Use at least one scene even if the conditions are not met

    Use at least one scene even if the conditions are not met

    Issue Link ๐Ÿ”—

    #377 EKWindow does not appear on the screen in version iOS 13 or later

    Goals ๐Ÿฅ…

    Makes the EKWindow appear on the screen even when no scene is active.

    Implementation Details โœ๏ธ

    I've improved it to use at least one scene if there are no candidate scenes.

    Testing Details ๐Ÿ”

    The problematic situation sometimes occurs when you show different types of entries in succession within a short period of time.

    opened by nExmond 0
  • EKWindow does not appear on the screen in version iOS 13 or later

    EKWindow does not appear on the screen in version iOS 13 or later

    Describe the bug Often the EKWindow does not appear on the screen.

    To Reproduce *It is difficult to describe the steps because they are reproducible stochastically.

    iPhone (please complete the following information):

    • Device: iPhone X
    • iOS Version: iOS 15.6.1
    • Xcode Version 13.4.1
    • Dependency Manager Version: CocoaPods 1.11.3
    • SwiftEntryKit Release: 1.2.7

    Additional context Please check the attached screenshot showing the location of the breakpoint. In iOS13+, if UIWindow.windowScene is nil, it won't show on screen.

    Screenshots / Video Links image

    opened by nExmond 1
  • Orientation issues - Autorotation not disabled in iOS 16

    Orientation issues - Autorotation not disabled in iOS 16

    Describe the bug When you display an alert, even when disabling autorotation alert is still allowed to rotate. Would be great if it just used the main view controller's supported orientations.

    To Reproduce Steps to reproduce the behavior:

    1. Set attributes to disable rotation2. attributes.positionConstraints.rotation.isEnabled = false
    2. display alert
    3. Rotate device
    4. Alert rotates when it shouldn't 5.

    Expected behaviour Alert should not show in landscape mode

    Screenshots If applicable, add screenshots to help explain your problem. Simulator Screen Shot - iPhone 14 - 2022-10-13 at 12 12 36

    iPhone (please complete the following information):

    • Device: Any device
    • iOS Version: iOS 16 +
    • Xcode Version 14.0.1
    • Dependency Manager Version N / A
    • SwiftEntryKit Release # 2.0.0

    Additional context Add any other context about the problem here.

    Screenshots / Video Links

    opened by sarahjstacey15 0
  • Support specified orientations

    Support specified orientations

    Issue Link ๐Ÿ”—

    Popview show wrong orientation when app force landscape

    Goals ๐Ÿฅ…

    Support specified orientations

    Implementation Details โœ๏ธ

    I added an extra option to the SupportedInterfaceOrientation enum, where you can define the supported orientation mask

    opened by MarkSpit 0
Owner
Daniel Huri
@daniel-huri at @facebook. Ex @blockchain
Daniel Huri
A Swift package for iOS/tvOS for easy alert presentation

AlertPresenter Listed on the Swift Package Index and originally posted on my blog. It can be fiddly to handle the presentation of alerts on a specific

Chris Mash 14 Jul 1, 2022
Simple Alert View written in Swift, which can be used as a UIAlertController. (AlertController/AlertView/ActionSheet)

DOAlertController Simple Alert View written in Swift, which can be used as a UIAlertController replacement. It supports from iOS7! It is simple and ea

Daiki Okumura 406 Sep 5, 2022
ColorAssetsFinder can help you find particular color assets easily by hex color code.

ColorAssetsFinder ColorAssetsFinder can help you find particular color assets easily by hex color code. Features Find particular color assets in Asset

Phil Chang 2 May 11, 2022
JAlert - This is "Alert View" project for UIKit + SwiftUI. you can use easily

JAlert Example To run the example project, clone the repo, and run pod install from the Example directory first. Requirements Installation JAlert is a

Jackson 3 Feb 22, 2022
Zingle โ€“ An alert will display underneath your UINavigationBar ๐ŸŽ…

Zingle Zingle โ€“ An alert will display underneath your UINavigationBar ?? ?? Note: Zingle has a dependency to have a UINavigationController in your app

Hemang 109 Jun 24, 2022
DropView - A SwiftUI library to display Apple Pencil and Pasteboard-like alerts on iOS.

DropView is a SwiftUI-based library to display alerts inspired by the Apple Pencil and pasteboard stock ones.

Stefano Bertagno 46 Dec 4, 2022
CoffeeToast - A swift package to easily add Toast notifications to your app

CoffeeToast A simple Swift package to add Toast Notifications to your app. Insta

Maegan Wilson 2 Feb 3, 2022
BPStatusBarAlert is a library that allows you to easily make text-based alert that appear on the status bar and below navigation bar.

BPStatusBarAlert BPStatusBarAlert is a library that allows you to easily make text-based alert that appear on the status bar and below navigation bar.

Ben.Park 131 Aug 12, 2022
The easiest way to display highly customizable in app notification banners in iOS

Written in Swift 5 NotificationBanner is an extremely customizable and lightweight library that makes the task of displaying in app notification banne

Dalton Hinterscher 4.5k Jan 9, 2023
Display Apple system-like self-hiding status alerts. It is well suited for notifying user without interrupting user flow in iOS-like way.

StatusAlert is being sponsored by the following tool; please help to support us by takin a look and signing up to a free trial. Dependency managers Fe

Yehor Miroshnychenko 841 Dec 6, 2022
iOS tweak to display toasts for Low Power alerts and charging

Electrode iOS tweak to display toasts for Low Power alerts and charging. Localization Want to help translate Electrode to your language? Sumbit a pull

null 8 Sep 7, 2022
Whisper is a component that will make the task of display messages and in-app notifications simple. It has three different views inside

Description ?? Break the silence of your UI, whispering, shouting or whistling at it. Whisper is a component that will make the task of displaying mes

HyperRedink 3.7k Dec 25, 2022
๐ŸšจUse the iPhone X Notch to display alerts. ๐Ÿšจ

NotchyAlert Prerequisites Xcode 9.x Swift 4.x iPhone X Simulator/Device Demo Installation Cocoapods To install NotchyAlert using Cocoapods, add the fo

Sofiane Beors 70 Nov 20, 2022
A customizable, full-feature, lightweight iOS framework to be used instead of UIAlertController.

A customizable, full-feature, lightweight iOS framework to be used instead of UIAlertController.

Ali Samaiee 11 Jun 6, 2022
A ยตFramework for showing alerts like the one used when copying from pasteboard or connecting Apple pencil

Drops ?? A ยตFramework for showing alerts like the one used when copying from pasteboard or connecting Apple pencil. Features iOS 10+ Can be used in Sw

Omar Albeik 709 Dec 29, 2022
Bottom Sheet component is widely used in Joom application

Bottom Sheet Bottom Sheet component is widely used in Joom application Installation Swift Package Manager Swift Package Manager is a tool for managing

Joom 101 Dec 29, 2022
BottomSheet makes it easy to add custom bottom sheets to your SwiftUI apps.

BottomSheet About BottomSheet BottomSheet makes it easy to add custom bottom sheets to your SwiftUI apps. The result can look like this...or completel

Daniel Saidi 174 Jan 2, 2023
A modern iOS toast view that can fit your notification needs

CRToast CRToast is a library that allows you to easily create notifications that appear on top of or by pushing out the status bar or navigation bar.

Collin Ruffenach 4.2k Dec 30, 2022
AlertView A pop-up framework, Can be simple and convenient to join your project.

RAlertView AlertView A pop-up framework, Can be simple and convenient to join your project. Warning content Installation Depend on the project Masonry

ๆœ่€€่พ‰ 71 Aug 12, 2022