Skip to content

Repository files navigation

FloatersToastsPopupsSheets

Popup View

Toasts, alerts and popups library written with SwiftUI

Read Article »

SPMCocoapodsLicense: MIT

Update to version 5

  • Moved all the popup types out of the main class with generic, to allow easier storing in variables, like this Popup.DisplayMode without Popup<V>.DisplayMode
  • DismissSource -> Popup.DismissSource
  • Scroll popup is not a part of PopupType enum anymore, it is created through a separate modifier, like this:
.scrollPopup(isPresented: $show){YourPopupBody()} header:{scrollViewHeader()} customize:{
$0
.closeOnTap(false).dragToDismiss(dragToDismiss)}

What's new in version 4

You can show multiple popups on top of anything, and they can also let the taps pass through to lower views. There are 3 ways to display a popup: as a simple overlay, using SwiftUI's fullscreenSheet, and using UIKit's UIWindow. There are pros and cons for all of these, here is a table.

OverlaySheetWindow
Show on top of navbar
Show on top of sheet
Show multiple popups
Taps "pass through" the transparent bg
SwiftUI @State update mechanism works as expected

Basically UIWindow based popup is the best option for most situations, just remember - to get adequate UI updates, use ObservableObjects or @Bindings instead of @State. This won't work:

structContentView:View{@StatevarshowPopup=false@Statevara=falsevarbody:someView{Button("Button"){
showPopup.toggle()}.popup(isPresented: $showPopup){VStack{Button("Switch a"){
a.toggle()}
a ?Text("on").foregroundStyle(.green):Text("off").foregroundStyle(.red)}} customize:{
$0
.type(.floater()).closeOnTap(false).position(.top)}}}

This will work:

structContentView:View{@StatevarshowPopup=false@Statevara=falsevarbody:someView{Button("Button"){
showPopup.toggle()}.popup(isPresented: $showPopup){PopupContent(a: $a)} customize:{
$0
.type(.floater()).closeOnTap(false).position(.top)}}}structPopupContent:View{@Bindingvara:Boolvarbody:someView{VStack{Button("Switch a"){
a.toggle()}
a ?Text("on").foregroundStyle(.green):Text("off").foregroundStyle(.red)}}}

Update to version 4

New DisplayMode enum was introduced instead of isOpaque. isOpaque is now deprecated. Instead of:

.popup(isPresented: $toasts.showingTopSecond){ToastTopSecond()} customize:{
$0
.type(.toast).isOpaque(true) // <-- here
}

use:

.popup(isPresented: $floats.showingTopFirst){FloatTopFirst()} customize:{
$0
.type(.floater()).displayMode(.sheet) // <-- here
}

So, new .displayMode(.sheet) corresponds to old .isOpaque(true), .displayMode(.overlay) corresponds to .isOpaque(false). Default DisplayMode is .window.

What's new in version 3

  • zoom in/out appear/disappear animations
  • disappearTo parameter to specify disappearing animation direction - can be different from appearFrom

Update to version 3

To include new .zoom type, AppearFrom enum cases were renamed. Instead of:

.popup(isPresented: $floats.showingTopFirst){FloatTopFirst()} customize:{
$0
.type(.floater()).appearFrom(.top) // <-- here
}

use:

.popup(isPresented: $floats.showingTopFirst){FloatTopFirst()} customize:{
$0
.type(.floater()).appearFrom(.topSlide) // <-- here
}

Update to version 2

Instead of:

.popup(isPresented: $floats.showingTopFirst, type:.floater(), position:.top, animation:.spring(), closeOnTapOutside:true, backgroundColor:.black.opacity(0.5)){FloatTopFirst()}

use:

.popup(isPresented: $floats.showingTopFirst){FloatTopFirst()} customize:{
$0
.type(.floater()).position(.top).animation(.spring()).closeOnTapOutside(true).backgroundColor(.black.opacity(0.5))}

Using this API you can pass parameters in any order you like.

Usage

  1. Add a bool to control popup presentation state
  2. Add .popup modifier to your view.
import PopupView
structContentView:View{@StatevarshowingPopup=falsevarbody:someView{YourView().popup(isPresented: $showingPopup){Text("The popup").frame(width:200, height:60).background(Color(red:0.85, green:0.8, blue:0.95)).cornerRadius(30.0)} customize:{
$0.autohideIn(2)}}}

Required parameters

isPresented - binding to determine if the popup should be seen on screen or hidden
view - view you want to display on your popup

or

item - binding to item: if item's value is nil - popup is hidden, if non-nil - displayed. Be careful - library makes a copy of your item during dismiss animation!!
view - view you want to display on your popup

Available customizations - optional parameters

use customize closure in .popup modifier:

type:

  • default - usual popup in the center of screen
  • toast - fitted to screen i.e. without padding and ignoring safe area
  • floater - has padding and can choose to use or ignore safe area
    • verticalPadding - padding from the relative vertical edge, or added to safe area if useSafeAreaInset is true
    • horizontalPadding - padding from the relative horizontal edge, or added to safe area if useSafeAreaInset is true
    • useSafeAreaInset - whether to include safe area insets in floater padding

position - topLeading, top, topTrailing, leading, center, trailing, bottomLeading, bottom, bottomTrailing
appearFrom - topSlide, bottomSlide, leftSlide, rightSlide, centerScale, none: determines the direction of appearing animation. If left empty it copies position parameter: appears from .top edge if position is set to .top. .none means no animation
disappearTo - same as appearFrom, but for disappearing animation. If left empty it copies appearFrom
displayMode - how the popup is rendered: .overlay (placed above content), .sheet (using fullScreenCover), .window (using UIWindow, default)
animation - custom animation for popup sliding onto screen
autohideIn - time after which popup should disappear
dismissibleIn(Double?, Binding<Bool>?) - only allow dismiss after this time passes (forbids closeOnTap, closeOnTapOutside, and drag). Pass a boolean binding if you'd like to track current status
dragToDismiss - true by default: enable/disable drag to dismiss (upwards for .top popup types, downwards for .bottom and default type)
dragToDismissDistance - minimum distance to drag to trigger dismiss
closeOnTap - true by default: enable/disable closing on tap on popup.
NOTE: any gesture or control element you add to popup's body will override tap to close. in this case please close the popup manually if you need it to
closeOnTapOutside - false by default: enable/disable closing on tap outside of popup
allowTapThroughBG - false by default: should allow taps to pass "through" the popup's background down to views "below" it. .sheet popup is always allowTapThroughBG = false
backgroundColor - Color.clear by default: change background color of outside area
backgroundView - custom background builder for outside area (if this one is set, backgroundColor is ignored)
useKeyboardSafeArea - false by default: if true popup goes up for keyboardHeight when keyboard is displayed
becomesKeyWindow - true by default: only relevant for displayMode == .window. Set to false for transient, non-interactive popups (toasts/snackbars) to avoid stealing key window / first responder status — and with it the keyboard — from the presenting screen
willDismissCallback - called when dismiss animation starts
dismissCallback - called when dismiss animation ends

Scroll popup customizations

use customize closure in .scrollPopup modifier. In addition to all base customizations listed above:

position:

  • .bottom(_ topPadding: CGFloat) - default: positioned at the bottom with the specified top padding
  • .center(_ verticalPadding: CGFloat) - centered with the specified vertical padding

headerView - a view pinned to the top that is not part of the scroll

Draggable card - sheet

To implement a sheet (like in 4th gif) enable dragToDismiss on bottom toast (see example project for implementation of the card itself)

.popup(isPresented: $show){
// your content } customize:{
$0
.type(.toast).position(.bottom).dragToDismiss(true)}

Examples

To try the PopupView examples:

  • Clone the repo https://github.com/exyte/PopupView.git
  • Open PopupExample.xcodeproj in the Xcode
  • Try it!

Installation

dependencies:[.package(url:"https://github.com/exyte/PopupView.git")]

Requirements

  • iOS 15.0+ / macOS 11.0+ / tvOS 14.0+ / watchOS 7.0+
  • Xcode 12+

Our other open source SwiftUI libraries

AnchoredPopup - Anchored Popup grows "out" of a trigger view (similar to Hero animation)
Grid - The most powerful Grid container
ScalingHeaderScrollView - A scroll view with a sticky header which shrinks as you scroll
AnimatedTabBar - A tabbar with a number of preset animations
MediaPicker - Customizable media picker
Chat - Chat UI framework with fully customizable message cells, input view, and a built-in media picker
CalendarView - Calendar view with fully customizable month/day cells
OpenAI Wrapper lib for OpenAI REST API
AnimatedGradient - Animated linear gradient
ConcentricOnboarding - Animated onboarding flow
FloatingButton - Floating button menu
ActivityIndicatorView - A number of animated loading indicators
ProgressIndicatorView - A number of animated progress indicators
FlagAndCountryCode - Phone codes and flags for every country
SVGView - SVG parser
LiquidSwipe - Liquid navigation animation

About

Toasts and popups library written with SwiftUI

Topics

Resources

Stars

4.1k stars

Watchers

23 watching

Forks

Releases

Packages

Used by

Contributors

Languages