{"id":15055303,"url":"https://github.com/exyte/popupview","last_synced_at":"2025-05-13T18:07:31.275Z","repository":{"id":37332318,"uuid":"258158475","full_name":"exyte/PopupView","owner":"exyte","description":"Toasts and popups library written with SwiftUI","archived":false,"fork":false,"pushed_at":"2025-04-11T14:44:27.000Z","size":22445,"stargazers_count":3741,"open_issues_count":6,"forks_count":280,"subscribers_count":24,"default_branch":"master","last_synced_at":"2025-04-25T14:50:49.193Z","etag":null,"topics":["popup","swiftui","swiftui-components","swiftui-framework","toast"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/exyte.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2020-04-23T09:43:26.000Z","updated_at":"2025-04-23T22:27:38.000Z","dependencies_parsed_at":"2024-01-05T20:20:15.270Z","dependency_job_id":"50aa96ac-abd9-42c5-b2b5-370f9e3df2a2","html_url":"https://github.com/exyte/PopupView","commit_stats":{"total_commits":201,"total_committers":28,"mean_commits":7.178571428571429,"dds":"0.43781094527363185","last_synced_commit":"89834ce0e2c7fddd62527a19e35570f4c163191d"},"previous_names":[],"tags_count":101,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/exyte%2FPopupView","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/exyte%2FPopupView/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/exyte%2FPopupView/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/exyte%2FPopupView/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/exyte","download_url":"https://codeload.github.com/exyte/PopupView/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254000848,"owners_count":21997441,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["popup","swiftui","swiftui-components","swiftui-framework","toast"],"created_at":"2024-09-24T21:40:35.194Z","updated_at":"2025-05-13T18:07:31.246Z","avatar_url":"https://github.com/exyte.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ca href=\"https://exyte.com/\"\u003e\u003cpicture\u003e\u003csource media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/exyte/media/master/common/header-dark.png\"\u003e\u003cimg src=\"https://raw.githubusercontent.com/exyte/media/master/common/header-light.png\"\u003e\u003c/picture\u003e\u003c/a\u003e\n\n\u003ca href=\"https://exyte.com/\"\u003e\u003cpicture\u003e\u003csource media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/exyte/media/master/common/our-site-dark.png\" width=\"80\" height=\"16\"\u003e\u003cimg src=\"https://raw.githubusercontent.com/exyte/media/master/common/our-site-light.png\" width=\"80\" height=\"16\"\u003e\u003c/picture\u003e\u003c/a\u003e\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u003ca href=\"https://twitter.com/exyteHQ\"\u003e\u003cpicture\u003e\u003csource media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/exyte/media/master/common/twitter-dark.png\" width=\"74\" height=\"16\"\u003e\u003cimg src=\"https://raw.githubusercontent.com/exyte/media/master/common/twitter-light.png\" width=\"74\" height=\"16\"\u003e\n\u003c/picture\u003e\u003c/a\u003e \u003ca href=\"https://exyte.com/contacts\"\u003e\u003cpicture\u003e\u003csource media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/exyte/media/master/common/get-in-touch-dark.png\" width=\"128\" height=\"24\" align=\"right\"\u003e\u003cimg src=\"https://raw.githubusercontent.com/exyte/media/master/common/get-in-touch-light.png\" width=\"128\" height=\"24\" align=\"right\"\u003e\u003c/picture\u003e\u003c/a\u003e\n\n\u003ctable\u003e\n    \u003cthead\u003e\n        \u003ctr\u003e\n            \u003cth\u003eFloaters\u003c/th\u003e\n            \u003cth\u003eToasts\u003c/th\u003e\n            \u003cth\u003ePopups\u003c/th\u003e\n            \u003cth\u003eSheets\u003c/th\u003e\n        \u003c/tr\u003e\n    \u003c/thead\u003e\n    \u003ctbody\u003e\n        \u003ctr\u003e\n            \u003ctd\u003e\n                \u003cimg src=\"https://raw.githubusercontent.com/exyte/media/master/PopupView/1.gif\" /\u003e\n            \u003c/td\u003e\n            \u003ctd\u003e\n                \u003cimg src=\"https://raw.githubusercontent.com/exyte/media/master/PopupView/2.gif\" /\u003e\n            \u003c/td\u003e\n            \u003ctd\u003e\n                \u003cimg src=\"https://raw.githubusercontent.com/exyte/media/master/PopupView/3.gif\" /\u003e\n            \u003c/td\u003e\n            \u003ctd\u003e\n                \u003cimg src=\"https://raw.githubusercontent.com/exyte/media/master/PopupView/4.gif\" /\u003e\n            \u003c/td\u003e\n        \u003c/tr\u003e\n    \u003c/tbody\u003e\n\u003c/table\u003e\n\n\u003cp\u003e\u003ch1 align=\"left\"\u003ePopup View\u003c/h1\u003e\u003c/p\u003e\n\n\u003cp\u003e\u003ch4\u003eToasts, alerts and popups library written with SwiftUI\u003c/h4\u003e\u003c/p\u003e\n\n\u003ca href=\"https://exyte.com/blog/swiftui-tutorial-popupview-library\"\u003eRead Article »\u003c/a\u003e\n\n![](https://img.shields.io/github/v/tag/exyte/popupView?label=Version)\n[![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fexyte%2FPopupView%2Fbadge%3Ftype%3Dswift-versions)](https://swiftpackageindex.com/exyte/PopupView)\n[![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fexyte%2FPopupView%2Fbadge%3Ftype%3Dplatforms)](https://swiftpackageindex.com/exyte/PopupView)\n[![SPM](https://img.shields.io/badge/SPM-Compatible-brightgreen.svg)](https://swiftpackageindex.com/exyte/PopupView)\n[![Cocoapods](https://img.shields.io/badge/Cocoapods-Deprecated%20after%204.0.0-yellow.svg)](https://cocoapods.org/pods/ExytePopupView)\n[![License: MIT](https://img.shields.io/badge/License-MIT-black.svg)](https://opensource.org/licenses/MIT)\n\n# What's new in version 4\nYou can show multiple popups on top of anything, and they can also let the taps pass through to lower views. \nThere 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.\n\u003ctable\u003e\n    \u003cthead\u003e\n        \u003ctr\u003e\n            \u003cth\u003e\u003c/th\u003e\n            \u003cth\u003eOverlay\u003c/th\u003e\n            \u003cth\u003eSheet\u003c/th\u003e\n            \u003cth\u003eWindow\u003c/th\u003e\n        \u003c/tr\u003e\n    \u003c/thead\u003e\n    \u003ctbody\u003e\n        \u003ctr align=center\u003e\n        \u003cth\u003eShow on top of navbar\u003c/th\u003e\n            \u003ctd\u003e ❌ \u003c/td\u003e\n            \u003ctd\u003e ✅ \u003c/td\u003e\n            \u003ctd\u003e ✅ \u003c/td\u003e\n        \u003c/tr\u003e\n        \u003ctr align=center\u003e\n        \u003cth\u003eShow on top of sheet\u003c/th\u003e\n            \u003ctd\u003e ❌ \u003c/td\u003e\n            \u003ctd\u003e ❌ \u003c/td\u003e\n            \u003ctd\u003e ✅ \u003c/td\u003e\n        \u003c/tr\u003e\n        \u003ctr align=center\u003e\n        \u003cth\u003eShow multiple popups\u003c/th\u003e\n            \u003ctd\u003e ✅ \u003c/td\u003e\n            \u003ctd\u003e ❌ \u003c/td\u003e\n            \u003ctd\u003e ✅ \u003c/td\u003e\n        \u003c/tr\u003e\n        \u003ctr align=center\u003e\n        \u003cth\u003eTaps \"pass through\" the transparent bg\u003c/th\u003e\n            \u003ctd\u003e ✅ \u003c/td\u003e\n            \u003ctd\u003e ❌ \u003c/td\u003e\n            \u003ctd\u003e ✅ \u003c/td\u003e\n        \u003c/tr\u003e\n        \u003ctr align=center\u003e\n        \u003cth\u003eSwiftUI @State update mechanism works as expected\u003c/th\u003e\n            \u003ctd\u003e ✅ \u003c/td\u003e\n            \u003ctd\u003e ✅ \u003c/td\u003e\n            \u003ctd\u003e ❌ \u003c/td\u003e\n        \u003c/tr\u003e\n    \u003c/tbody\u003e\n\u003c/table\u003e\n\nBasically 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:\n```swift\nstruct ContentView : View {\n    @State var showPopup = false\n    @State var a = false\n\n    var body: some View {\n        Button(\"Button\") {\n            showPopup.toggle()\n        }\n        .popup(isPresented: $showPopup) {\n            VStack {\n                Button(\"Switch a\") {\n                    a.toggle()\n                }\n                a ? Text(\"on\").foregroundStyle(.green) : Text(\"off\").foregroundStyle(.red)\n            }\n        } customize: {\n            $0\n                .type(.floater())\n                .closeOnTap(false)\n                .position(.top)\n        }\n    }\n}\n```\nThis will work:\n```swift\nstruct ContentView : View {\n    @State var showPopup = false\n    @State var a = false\n\n    var body: some View {\n        Button(\"Button\") {\n            showPopup.toggle()\n        }\n        .popup(isPresented: $showPopup) {\n            PopupContent(a: $a)\n        } customize: {\n            $0\n                .type(.floater())\n                .closeOnTap(false)\n                .position(.top)\n        }\n    }\n}\n\nstruct PopupContent: View {\n    @Binding var a: Bool\n\n    var body: some View {\n        VStack {\n            Button(\"Switch a\") {\n                a.toggle()\n            }\n            a ? Text(\"on\").foregroundStyle(.green) : Text(\"off\").foregroundStyle(.red)\n        }\n    }\n}\n```\n\n# Update to version 4\nNew `DisplayMode` enum was introduced instead of `isOpaque`. `isOpaque` is now deprecated.\nInstead of:\n```swift\n.popup(isPresented: $toasts.showingTopSecond) {\n    ToastTopSecond()\n} customize: {\n    $0\n        .type(.toast)\n        .isOpaque(true) // \u003c-- here\n}\n```\nuse:\n```swift\n.popup(isPresented: $floats.showingTopFirst) {\n    FloatTopFirst()\n} customize: {\n    $0\n        .type(.floater())\n        .displayMode(.sheet) // \u003c-- here\n}\n```\nSo, new `.displayMode(.sheet)` corresponds to old `.isOpaque(true)`, `.displayMode(.overlay)` corresponds to `.isOpaque(false)`.\nDefault `DisplayMode` is `.window`.\n\n# What's new in version 3\n- zoom in/out appear/disappear animations\n- `disappearTo` parameter to specify disappearing animation direction - can be different from `appearFrom`\n\n# Update to version 3\nTo include new .zoom type, `AppearFrom` enum cases were renamed.\nInstead of:\n```swift\n.popup(isPresented: $floats.showingTopFirst) {\n    FloatTopFirst()\n} customize: {\n    $0\n        .type(.floater())\n        .appearFrom(.top) // \u003c-- here\n}\n```\nuse:\n```swift\n.popup(isPresented: $floats.showingTopFirst) {\n    FloatTopFirst()\n} customize: {\n    $0\n        .type(.floater())\n        .appearFrom(.topSlide) // \u003c-- here\n}\n```\n\n# Update to version 2\n\nInstead of:\n```swift\n.popup(isPresented: $floats.showingTopFirst, type: .floater(), position: .top, animation: .spring(), closeOnTapOutside: true, backgroundColor: .black.opacity(0.5)) {\n    FloatTopFirst()\n}\n```\nuse:\n```swift\n.popup(isPresented: $floats.showingTopFirst) {\n    FloatTopFirst()\n} customize: {\n    $0\n        .type(.floater())\n        .position(.top)\n        .animation(.spring())\n        .closeOnTapOutside(true)\n        .backgroundColor(.black.opacity(0.5))\n}\n```\nUsing this API you can pass parameters in any order you like.\n\n# Usage\n1. Add a bool to control popup presentation state\n2. Add `.popup` modifier to your view. \n```swift\nimport PopupView\n\nstruct ContentView: View {\n\n    @State var showingPopup = false\n\n    var body: some View {\n        YourView()\n            .popup(isPresented: $showingPopup) {\n                Text(\"The popup\")\n                    .frame(width: 200, height: 60)\n                    .background(Color(red: 0.85, green: 0.8, blue: 0.95))\n                    .cornerRadius(30.0)\n            } customize: {\n                $0.autohideIn(2)\n            }\n    }\n}\n```\n\n### Required parameters \n`isPresented` - binding to determine if the popup should be seen on screen or hidden     \n`view` - view you want to display on your popup  \n\n#### or\n`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!!     \n`view` - view you want to display on your popup  \n\n### Available customizations - optional parameters\nuse `customize` closure in popup modifier:\n\n`type`:\n- `default` - usual popup in the center of screen\n- toast - fitted to screen i.e. without padding and ignoring safe area\n- floater - has padding and can choose to use or ignore safe area\n- scroll - adds a scroll to your content, if you scroll to top of this scroll - the gesture will continue into popup's drag dismiss.\n\nfloater parameters:     \n- `verticalPadding` - padding which will define padding from the relative vertical edge or will be added to safe area if `useSafeAreaInset` is true   \n- `horizontalPadding` - padding which will define padding from the relative horizontal edge or will be added to safe area if `useSafeAreaInset` is true      \n- `useSafeAreaInset` - whether to include safe area insets in floater padding      \n\nscroll parameters:   \n`headerView` - a view on top which won't be a part of the scroll (if you need one)\n\n`position` - topLeading, top, topTrailing, leading, center, trailing, bottomLeading, bottom, bottomTrailing \n`appearFrom` - `topSlide, bottomSlide, leftSlide, rightSlide, centerScale, none`: determines the direction of appearing animation. If left empty it copies `position` parameter: so appears from .top edge, if `position` is set to .top. `.none` means no animation\n`disappearTo` - same as `appearFrom`, but for disappearing animation. If left empty it copies `appearFrom`.\n`animation` - custom animation for popup sliding onto screen  \n`autohideIn` - time after which popup should disappear    \n`dismissibleIn(Double?, Binding\u003cBool\u003e?)` - only allow dismiss after this time passes (forbids closeOnTap, closeOnTapOutside, and drag). Pass a boolean binding if you'd like to track current status     \n`dragToDismiss` - true by default: enable/disable drag to dismiss (upwards for .top popup types, downwards for .bottom and default type)    \n`closeOnTap` - true by default: enable/disable closing on tap on popup     \n`closeOnTapOutside` - false by default: enable/disable closing on tap on outside of popup     \n`backgroundColor` - Color.clear by default: change background color of outside area     \n`backgroundView` - custom background builder for outside area (if this one is set `backgroundColor` is ignored)    \n`isOpaque` - false by default: if true taps do not pass through popup's background and the popup is displayed on top of navbar. For more see section \"Show over navbar\"     \n`useKeyboardSafeArea` - false by default: if true popup goes up for keyboardHeight when keyboard is displayed\n`dismissCallback` - custom callback to call once the popup is dismissed      \n\n### Draggable card - sheet\nTo implement a sheet (like in 4th gif) enable `dragToDismiss` on bottom toast (see example project for implementation of the card itself)\n```swift\n.popup(isPresented: $show) {\n    // your content \n} customize: {\n    $0\n        .type (.toast)\n        .position(.bottom)\n        .dragToDismiss(true)\n}\n```\n\n## Examples\n\nTo try the PopupView examples:\n- Clone the repo `https://github.com/exyte/PopupView.git`\n- Open `PopupExample.xcodeproj` in the Xcode\n- Try it!\n\n## Installation\n\n### [Swift Package Manager](https://swift.org/package-manager/)\n\n```swift\ndependencies: [\n    .package(url: \"https://github.com/exyte/PopupView.git\")\n]\n```\n\n## Requirements\n\n* iOS 15.0+ / macOS 11.0+ / tvOS 14.0+ / watchOS 7.0+\n* Xcode 12+ \n\n## Our other open source SwiftUI libraries\n[AnchoredPopup](https://github.com/exyte/AnchoredPopup) - Anchored Popup grows \"out\" of a trigger view (similar to Hero animation)    \n[Grid](https://github.com/exyte/Grid) - The most powerful Grid container    \n[ScalingHeaderScrollView](https://github.com/exyte/ScalingHeaderScrollView) - A scroll view with a sticky header which shrinks as you scroll    \n[AnimatedTabBar](https://github.com/exyte/AnimatedTabBar) - A tabbar with a number of preset animations   \n[MediaPicker](https://github.com/exyte/mediapicker) - Customizable media picker     \n[Chat](https://github.com/exyte/chat) - Chat UI framework with fully customizable message cells, input view, and a built-in media picker  \n[OpenAI](https://github.com/exyte/OpenAI) Wrapper lib for [OpenAI REST API](https://platform.openai.com/docs/api-reference/introduction)    \n[AnimatedGradient](https://github.com/exyte/AnimatedGradient) - Animated linear gradient     \n[ConcentricOnboarding](https://github.com/exyte/ConcentricOnboarding) - Animated onboarding flow    \n[FloatingButton](https://github.com/exyte/FloatingButton) - Floating button menu    \n[ActivityIndicatorView](https://github.com/exyte/ActivityIndicatorView) - A number of animated loading indicators    \n[ProgressIndicatorView](https://github.com/exyte/ProgressIndicatorView) - A number of animated progress indicators    \n[FlagAndCountryCode](https://github.com/exyte/FlagAndCountryCode) - Phone codes and flags for every country    \n[SVGView](https://github.com/exyte/SVGView) - SVG parser    \n[LiquidSwipe](https://github.com/exyte/LiquidSwipe) - Liquid navigation animation    \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fexyte%2Fpopupview","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fexyte%2Fpopupview","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fexyte%2Fpopupview/lists"}