Floaters Toasts Popups Sheets

Popup View

Toasts, alerts and popups library written with SwiftUI

Read Article » ![](https://img.shields.io/github/v/tag/exyte/popupView?label=Version) [![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fexyte%2FPopupView%2Fbadge%3Ftype%3Dswift-versions)](https://swiftpackageindex.com/exyte/PopupView) [![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fexyte%2FPopupView%2Fbadge%3Ftype%3Dplatforms)](https://swiftpackageindex.com/exyte/PopupView) [![SPM](https://img.shields.io/badge/SPM-Compatible-brightgreen.svg)](https://swiftpackageindex.com/exyte/PopupView) [![Cocoapods](https://img.shields.io/badge/Cocoapods-Deprecated%20after%204.0.0-yellow.svg)](https://cocoapods.org/pods/ExytePopupView) [![License: MIT](https://img.shields.io/badge/License-MIT-black.svg)](https://opensource.org/licenses/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.DisplayMode` - DismissSource -> Popup.DismissSource - Scroll popup is not a part of PopupType enum anymore, it is created through a separate modifier, like this: ```swift .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.
Overlay Sheet Window
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: ```swift struct ContentView: View { @State var showPopup = false @State var a = false var body: some View { 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: ```swift struct ContentView: View { @State var showPopup = false @State var a = false var body: some View { Button("Button") { showPopup.toggle() } .popup(isPresented: $showPopup) { PopupContent(a: $a) } customize: { $0 .type(.floater()) .closeOnTap(false) .position(.top) } } } struct PopupContent: View { @Binding var a: Bool var body: some View { 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: ```swift .popup(isPresented: $toasts.showingTopSecond) { ToastTopSecond() } customize: { $0 .type(.toast) .isOpaque(true) // <-- here } ``` use: ```swift .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: ```swift .popup(isPresented: $floats.showingTopFirst) { FloatTopFirst() } customize: { $0 .type(.floater()) .appearFrom(.top) // <-- here } ``` use: ```swift .popup(isPresented: $floats.showingTopFirst) { FloatTopFirst() } customize: { $0 .type(.floater()) .appearFrom(.topSlide) // <-- here } ``` # Update to version 2 Instead of: ```swift .popup(isPresented: $floats.showingTopFirst, type: .floater(), position: .top, animation: .spring(), closeOnTapOutside: true, backgroundColor: .black.opacity(0.5)) { FloatTopFirst() } ``` use: ```swift .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. ```swift import PopupView struct ContentView: View { @State var showingPopup = false var body: some View { 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?)` - 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) ```swift .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 ### [Swift Package Manager](https://swift.org/package-manager/) ```swift 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](https://github.com/exyte/AnchoredPopup) - Anchored Popup grows "out" of a trigger view (similar to Hero animation) [Grid](https://github.com/exyte/Grid) - The most powerful Grid container [ScalingHeaderScrollView](https://github.com/exyte/ScalingHeaderScrollView) - A scroll view with a sticky header which shrinks as you scroll [AnimatedTabBar](https://github.com/exyte/AnimatedTabBar) - A tabbar with a number of preset animations [MediaPicker](https://github.com/exyte/mediapicker) - Customizable media picker [Chat](https://github.com/exyte/chat) - Chat UI framework with fully customizable message cells, input view, and a built-in media picker [CalendarView](https://github.com/exyte/CalendarView) - Calendar view with fully customizable month/day cells [OpenAI](https://github.com/exyte/OpenAI) Wrapper lib for [OpenAI REST API](https://platform.openai.com/docs/api-reference/introduction) [AnimatedGradient](https://github.com/exyte/AnimatedGradient) - Animated linear gradient [ConcentricOnboarding](https://github.com/exyte/ConcentricOnboarding) - Animated onboarding flow [FloatingButton](https://github.com/exyte/FloatingButton) - Floating button menu [ActivityIndicatorView](https://github.com/exyte/ActivityIndicatorView) - A number of animated loading indicators [ProgressIndicatorView](https://github.com/exyte/ProgressIndicatorView) - A number of animated progress indicators [FlagAndCountryCode](https://github.com/exyte/FlagAndCountryCode) - Phone codes and flags for every country [SVGView](https://github.com/exyte/SVGView) - SVG parser [LiquidSwipe](https://github.com/exyte/LiquidSwipe) - Liquid navigation animation