Overview of Popup View
masterUIWindow-based popups to handle different UI requirements such as showing content on top of navigation bars or allowing taps to pass through transparent backgrounds.repository·master·Indexed 26 days ago
https://github.com/exyte/popupviewA SwiftUI library for creating and managing highly customizable UI elements such as toasts, alerts, popups, and sheets. It supports multiple display modes including .window (UIWindow-based), .sheet (fullscreenSheet), and .overlay. The library provides a .popup modifier for general use and a dedicated .scrollPopup modifier for scrollable content with optional header views.
UIWindow-based popups to handle different UI requirements such as showing content on top of navigation bars or allowing taps to pass through transparent backgrounds.Add the following dependency to your Package.swift file to include PopupView in your project.
dependencies: [
.package(url: "https://github.com/exyte/PopupView.git")
]Version 4 replaced the deprecated .isOpaque(Bool) method with the .displayMode(DisplayMode) method:
.displayMode(.sheet) replaces .isOpaque(true)..displayMode(.overlay) replaces .isOpaque(false).// Old way (deprecated)
.popup(isPresented: $toasts.showingTopSecond) {
ToastTopSecond()
} customize: {
$0
.type(.toast)
.isOpaque(true)
}
// New way
.popup(isPresented: $floats.showingTopFirst) {
FloatTopFirst()
} customize: {
$0
.type(.floater())
.displayMode(.sheet)
}In version 3, to support the .zoom type, the AppearFrom enum cases were renamed. Use .topSlide instead of .top (and similar renames for other directions) when configuring .appearFrom().
// Old way
.popup(isPresented: $floats.showingTopFirst) {
FloatTopFirst()
} customize: {
$0
.type(.floater())
.appearFrom(.top)
}
// New way
.popup(isPresented: $floats.showingTopFirst) {
FloatTopFirst()
} customize: {
$0
.type(.floater())
.appearFrom(.topSlide)
}Version 5 introduced several breaking changes:
Popup.DisplayMode instead of Popup<V>.DisplayMode).DismissSource has been renamed to Popup.DismissSource..scrollPopup(...) instead of an enum case in PopupType.To display a popup, add the .popup modifier to a view. You must provide either a isPresented binding (Boolean) or an item binding (optional object). When the binding becomes true or non-nil, the popup is shown.
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)
}
}
}When using the default .window display mode, SwiftUI @State updates inside the popup body might not reflect correctly if the logic is contained entirely within the popup view. To ensure adequate UI updates, pass state via @Binding or use ObservableObject.
// This works: Passing state via @Binding
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)
}
}
}To create a draggable sheet effect (similar to a bottom sheet), use the .toast type, position it at the .bottom, and ensure dragToDismiss is enabled.
.popup(isPresented: $show) {
// your content
} customize: {
$0
.type (.toast)
.position(.bottom)
.dragToDismiss(true)
}When using version 4 or later, you can control how a popup is displayed using the .displayMode() customization. The available modes are:
.window: (Default) Uses UIWindow. Best for showing popups on top of navbars or sheets and allowing multiple popups. Note: Use @Binding or ObservableObject for state updates to ensure they work correctly with this mode..sheet: Uses SwiftUI's fullscreenSheet..overlay: Uses a simple overlay.Note: .isOpaque() is deprecated in favor of .displayMode().
.popup(isPresented: $floats.showingTopFirst) {
FloatTopFirst()
} customize: {
$0
.type(.floater())
.displayMode(.sheet)
}In version 5, scroll popups are no longer part of the PopupType enum. Instead, they are implemented using a dedicated .scrollPopup modifier. This allows you to define the popup body, a header for the scroll view, and customization options separately.
.scrollPopup(isPresented: $show) {
YourPopupBody()
} header: {
scrollViewHeader()
} customize: {
$0
.closeOnTap(false)
.dragToDismiss(dragToDismiss)
}Use the .scrollPopup modifier for popups containing scrollable content. It supports all standard customizations plus:
position: .bottom(CGFloat) (default) or .center(CGFloat).headerView: A view pinned to the top that remains visible while scrolling.Use the customize closure in the .popup modifier to configure the popup's behavior and appearance.
type: .default (center), .toast (fitted to screen), or .floater (with padding).position: .topLeading, .top, .topTrailing, .leading, .center, .trailing, .bottomLeading, .bottom, .bottomTrailing.appearFrom / disappearTo: Animation directions like .topSlide, .bottomSlide, .leftSlide, .rightSlide, .centerScale, or .none.displayMode: .overlay, .sheet (uses fullScreenCover), or .window (uses UIWindow, default).autohideIn(Double): Automatically hide the popup after a specified time.dragToDismiss(Bool): Enable/disable dragging to dismiss (default is true).closeOnTap(Bool): Enable/disable closing when the popup itself is tapped (default is true).closeOnTapOutside(Bool): Enable/disable closing when tapping outside the popup (default is false).backgroundColor(Color): Change the color of the area outside the popup.useKeyboardSafeArea(Bool): If true, the popup moves up when the keyboard appears.willDismissCallback / dismissCallback: Callbacks triggered at the start and end of the dismiss animation.