PopupView

repository·master·Indexed 26 days ago

https://github.com/exyte/popupview

A 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.

Tokens
2.3K
Snippets
8
Records
12
Agent score
38%

What's inside PopupView

  1. Overview of Popup View

    master
    Popup View is a SwiftUI library designed for displaying toasts, alerts, popups, and sheets. It provides various display modes including overlays, fullscreen sheets, and 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.
  2. Migrate to version 4 API changes

    master

    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)
    }
  3. Migrate to version 3 API changes

    master

    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)
    }
  4. Migrate to version 5 API changes

    master

    Version 5 introduced several breaking changes:

    • Popup types are moved out of the main generic class to allow easier storage (e.g., using Popup.DisplayMode instead of Popup<V>.DisplayMode).
    • DismissSource has been renamed to Popup.DismissSource.
    • Scroll popups are now a separate modifier .scrollPopup(...) instead of an enum case in PopupType.
  5. Use the .popup modifier

    master

    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)
                }
        }
    }
  6. Handle state updates in UIWindow-based popups

    master

    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)
            }
        }
    }
  7. Implement a draggable sheet using .toast type

    master

    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)
    }
  8. Choose a DisplayMode for popups

    master

    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)
    }
  9. Use scrollPopup modifier in version 5

    master

    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)
    }
  10. Customize Scroll Popups

    master

    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.
  11. Configure PopupView customizations

    master

    Use the customize closure in the .popup modifier to configure the popup's behavior and appearance.

    Available Customizations:

    • 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.