├── .gitignore ├── .swiftpm └── xcode │ └── package.xcworkspace │ └── contents.xcworkspacedata ├── LICENSE ├── Package.resolved ├── Package.swift ├── PopupExample ├── Assets.xcassets │ ├── AccentColor.colorset │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Color │ │ ├── Contents.json │ │ ├── dark.colorset │ │ │ └── Contents.json │ │ ├── darkBlue.colorset │ │ │ └── Contents.json │ │ ├── darkText.colorset │ │ │ └── Contents.json │ │ ├── light.colorset │ │ │ └── Contents.json │ │ ├── lightBlue.colorset │ │ │ └── Contents.json │ │ ├── purple.colorset │ │ │ └── Contents.json │ │ ├── semiLight.colorset │ │ │ └── Contents.json │ │ ├── semidark.colorset │ │ │ └── Contents.json │ │ └── yellow.colorset │ │ │ └── Contents.json │ ├── Contents.json │ ├── avatar1.imageset │ │ ├── Contents.json │ │ ├── avatar1.png │ │ ├── avatar1@2x.png │ │ └── avatar1@3x.png │ ├── avatar2.imageset │ │ ├── Contents.json │ │ ├── avatar2.png │ │ ├── avatar2@2x.png │ │ └── avatar2@3x.png │ ├── avatar3.imageset │ │ ├── Contents.json │ │ ├── avatar3.png │ │ ├── avatar3@2x.png │ │ └── avatar3@3x.png │ ├── avatar4.imageset │ │ ├── Contents.json │ │ └── avatar4.pdf │ ├── avatar5.imageset │ │ ├── Contents.json │ │ └── avatar5.pdf │ ├── avatar6.imageset │ │ ├── Contents.json │ │ └── avatar6.pdf │ ├── check_circle.imageset │ │ ├── Check Circle.pdf │ │ └── Contents.json │ ├── checkmark.imageset │ │ ├── Contents.json │ │ ├── checkmark.png │ │ ├── checkmark@2x.png │ │ └── checkmark@3x.png │ ├── chest.imageset │ │ ├── Contents.json │ │ └── chest.pdf │ ├── cross.imageset │ │ ├── Contents.json │ │ └── Vector.pdf │ ├── file.imageset │ │ ├── Contents.json │ │ └── file.pdf │ ├── fitness.imageset │ │ ├── Contents.json │ │ ├── fitness.png │ │ ├── fitness@2x.png │ │ └── fitness@3x.png │ ├── gift.imageset │ │ ├── Contents.json │ │ └── gift.pdf │ ├── gift2.imageset │ │ ├── Contents.json │ │ └── Gift.pdf │ ├── grapes.imageset │ │ ├── Contents.json │ │ └── grapes (7).png │ ├── okay.imageset │ │ ├── Contents.json │ │ └── okay-svg (1).png │ ├── phone_call.imageset │ │ ├── Contents.json │ │ └── phone_call.pdf │ ├── phone_call2.imageset │ │ ├── Contents.json │ │ └── phone_call2.pdf │ ├── shop_NA.imageset │ │ ├── Contents.json │ │ └── shop_NA.png │ ├── shop_coffee.imageset │ │ ├── Contents.json │ │ └── shop_coffee.png │ ├── transaction_coffee.imageset │ │ ├── Contents.json │ │ └── transaction_coffee.png │ └── winner.imageset │ │ ├── Contents.json │ │ └── winner.pdf ├── Font │ ├── NunitoSans_7pt-Bold.ttf │ ├── NunitoSans_7pt-Light.ttf │ ├── NunitoSans_7pt-Medium.ttf │ └── NunitoSans_7pt-Regular.ttf ├── PopupExample.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── swiftpm │ │ │ └── Package.resolved │ └── xcshareddata │ │ └── xcschemes │ │ └── PopupExample.xcscheme ├── PopupExample │ ├── Components │ │ ├── Images.swift │ │ └── PopupButton.swift │ ├── ContentView.swift │ ├── Examples │ │ ├── ActionSheets.swift │ │ ├── FloatsBig.swift │ │ ├── FloatsSmall.swift │ │ ├── InputSheets.swift │ │ ├── Popups.swift │ │ └── Toasts.swift │ ├── Info.plist │ ├── PopupExample.entitlements │ ├── PopupExampleApp.swift │ ├── PopupsList.swift │ ├── Preview Content │ │ └── Preview Assets.xcassets │ │ │ └── Contents.json │ └── Utils.swift └── PopupWatchExample Watch App │ ├── ContentView.swift │ ├── PopupWatchExampleApp.swift │ └── Preview Content │ └── Preview Assets.xcassets │ └── Contents.json ├── README.md └── Sources └── PopupView ├── FullscreenPopup.swift ├── Info.plist ├── Modifiers.swift ├── PopupBackgroundView.swift ├── PopupScrollViewDelegate.swift ├── PopupView.h ├── PopupView.swift ├── PublicAPI.swift └── Utils.swift /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/swift,macos,carthage,cocoapods 3 | # Edit at https://www.gitignore.io/?templates=swift,macos,carthage,cocoapods 4 | 5 | ### Carthage ### 6 | # Carthage 7 | # 8 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 9 | # Carthage/Checkouts 10 | 11 | Carthage/Build 12 | 13 | ### CocoaPods ### 14 | ## CocoaPods GitIgnore Template 15 | 16 | # CocoaPods - Only use to conserve bandwidth / Save time on Pushing 17 | # - Also handy if you have a large number of dependant pods 18 | # - AS PER https://guides.cocoapods.org/using/using-cocoapods.html NEVER IGNORE THE LOCK FILE 19 | Pods/ 20 | Podfile.lock 21 | Example.xcworkspace 22 | 23 | ### macOS ### 24 | # General 25 | .DS_Store 26 | .AppleDouble 27 | .LSOverride 28 | 29 | # Icon must end with two \r 30 | Icon 31 | 32 | # Thumbnails 33 | ._* 34 | 35 | # Files that might appear in the root of a volume 36 | .DocumentRevisions-V100 37 | .fseventsd 38 | .Spotlight-V100 39 | .TemporaryItems 40 | .Trashes 41 | .VolumeIcon.icns 42 | .com.apple.timemachine.donotpresent 43 | 44 | # Directories potentially created on remote AFP share 45 | .AppleDB 46 | .AppleDesktop 47 | Network Trash Folder 48 | Temporary Items 49 | .apdisk 50 | 51 | ### Swift ### 52 | # Xcode 53 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 54 | 55 | ## Build generated 56 | build/ 57 | DerivedData/ 58 | 59 | ## Various settings 60 | *.pbxuser 61 | !default.pbxuser 62 | *.mode1v3 63 | !default.mode1v3 64 | *.mode2v3 65 | !default.mode2v3 66 | *.perspectivev3 67 | !default.perspectivev3 68 | xcuserdata/ 69 | 70 | ## Other 71 | *.moved-aside 72 | *.xccheckout 73 | *.xcscmblueprint 74 | 75 | ## Obj-C/Swift specific 76 | *.hmap 77 | *.ipa 78 | *.dSYM.zip 79 | *.dSYM 80 | 81 | ## Playgrounds 82 | timeline.xctimeline 83 | playground.xcworkspace 84 | 85 | # Swift Package Manager 86 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 87 | # Packages/ 88 | # Package.pins 89 | # Package.resolved 90 | .build/ 91 | # Add this line if you want to avoid checking in Xcode SPM integration. 92 | # .swiftpm/xcode 93 | 94 | # CocoaPods 95 | # We recommend against adding the Pods directory to your .gitignore. However 96 | # you should judge for yourself, the pros and cons are mentioned at: 97 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 98 | # Pods/ 99 | # Add this line if you want to avoid checking in source code from the Xcode workspace 100 | # *.xcworkspace 101 | 102 | # Carthage 103 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 104 | # Carthage/Checkouts 105 | 106 | 107 | # Accio dependency management 108 | Dependencies/ 109 | .accio/ 110 | 111 | # fastlane 112 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 113 | # screenshots whenever they are needed. 114 | # For more information about the recommended setup visit: 115 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 116 | 117 | fastlane/report.xml 118 | fastlane/Preview.html 119 | fastlane/screenshots/**/*.png 120 | fastlane/test_output 121 | 122 | # Code Injection 123 | # After new code Injection tools there's a generated folder /iOSInjectionProject 124 | # https://github.com/johnno1962/injectionforxcode 125 | 126 | iOSInjectionProject/ 127 | 128 | # End of https://www.gitignore.io/api/swift,macos,carthage,cocoapods 129 | .idea/ 130 | -------------------------------------------------------------------------------- /.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 exyte 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "pins" : [ 3 | { 4 | "identity" : "swiftui-introspect", 5 | "kind" : "remoteSourceControl", 6 | "location" : "https://github.com/siteline/swiftui-introspect", 7 | "state" : { 8 | "revision" : "807f73ce09a9b9723f12385e592b4e0aaebd3336", 9 | "version" : "1.3.0" 10 | } 11 | } 12 | ], 13 | "version" : 2 14 | } 15 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 5.9 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "PopupView", 7 | platforms: [ 8 | .iOS(.v15), 9 | .macOS(.v11), 10 | .tvOS(.v14), 11 | .watchOS(.v7) 12 | ], 13 | products: [ 14 | .library(name: "PopupView", targets: ["PopupView"]), 15 | ], 16 | dependencies: [ 17 | .package( 18 | url: "https://github.com/siteline/swiftui-introspect", 19 | from: "1.0.0" 20 | ) 21 | ], 22 | targets: [ 23 | .target( 24 | name: "PopupView", 25 | dependencies: [ 26 | .product(name: "SwiftUIIntrospect", package: "swiftui-introspect"), 27 | ], 28 | swiftSettings: [ 29 | .enableExperimentalFeature("StrictConcurrency") 30 | ] 31 | ) 32 | ] 33 | ) 34 | -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0xEE", 9 | "green" : "0x6C", 10 | "red" : "0xAC" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "dark" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "srgb", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "0xFF", 27 | "green" : "0xFF", 28 | "red" : "0xFE" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/Color/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/Color/dark.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.169", 9 | "green" : "0.114", 10 | "red" : "0.122" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "dark" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "srgb", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "1.000", 27 | "green" : "1.000", 28 | "red" : "1.000" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/Color/darkBlue.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "1.000", 9 | "green" : "0.384", 10 | "red" : "0.286" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "dark" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "srgb", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "1.000", 27 | "green" : "1.000", 28 | "red" : "1.000" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/Color/darkText.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.263", 9 | "green" : "0.235", 10 | "red" : "0.235" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "dark" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "srgb", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "1.000", 27 | "green" : "1.000", 28 | "red" : "1.000" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/Color/light.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.918", 9 | "green" : "0.906", 10 | "red" : "0.906" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "dark" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "srgb", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "1.000", 27 | "green" : "1.000", 28 | "red" : "1.000" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/Color/lightBlue.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.922", 9 | "green" : "0.533", 10 | "red" : "0.149" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "dark" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "srgb", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "1.000", 27 | "green" : "1.000", 28 | "red" : "1.000" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/Color/purple.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.973", 9 | "green" : "0.396", 10 | "red" : "0.573" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "dark" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "srgb", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "1.000", 27 | "green" : "1.000", 28 | "red" : "1.000" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/Color/semiLight.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "0.520", 8 | "blue" : "0.855", 9 | "green" : "0.855", 10 | "red" : "0.855" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "dark" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "srgb", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "1.000", 27 | "green" : "1.000", 28 | "red" : "1.000" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/Color/semidark.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "0.700", 8 | "blue" : "0.118", 9 | "green" : "0.118", 10 | "red" : "0.118" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "dark" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "srgb", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "1.000", 27 | "green" : "1.000", 28 | "red" : "1.000" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/Color/yellow.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.239", 9 | "green" : "0.725", 10 | "red" : "1.000" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "dark" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "srgb", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "1.000", 27 | "green" : "1.000", 28 | "red" : "1.000" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/avatar1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "avatar1.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "avatar1@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "avatar1@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/avatar1.imageset/avatar1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exyte/PopupView/676d4609495f144b48a583af40b936eedfb49f3e/PopupExample/Assets.xcassets/avatar1.imageset/avatar1.png -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/avatar1.imageset/avatar1@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exyte/PopupView/676d4609495f144b48a583af40b936eedfb49f3e/PopupExample/Assets.xcassets/avatar1.imageset/avatar1@2x.png -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/avatar1.imageset/avatar1@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exyte/PopupView/676d4609495f144b48a583af40b936eedfb49f3e/PopupExample/Assets.xcassets/avatar1.imageset/avatar1@3x.png -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/avatar2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "avatar2.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "avatar2@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "avatar2@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/avatar2.imageset/avatar2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exyte/PopupView/676d4609495f144b48a583af40b936eedfb49f3e/PopupExample/Assets.xcassets/avatar2.imageset/avatar2.png -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/avatar2.imageset/avatar2@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exyte/PopupView/676d4609495f144b48a583af40b936eedfb49f3e/PopupExample/Assets.xcassets/avatar2.imageset/avatar2@2x.png -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/avatar2.imageset/avatar2@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exyte/PopupView/676d4609495f144b48a583af40b936eedfb49f3e/PopupExample/Assets.xcassets/avatar2.imageset/avatar2@3x.png -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/avatar3.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "avatar3.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "avatar3@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "avatar3@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/avatar3.imageset/avatar3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exyte/PopupView/676d4609495f144b48a583af40b936eedfb49f3e/PopupExample/Assets.xcassets/avatar3.imageset/avatar3.png -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/avatar3.imageset/avatar3@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exyte/PopupView/676d4609495f144b48a583af40b936eedfb49f3e/PopupExample/Assets.xcassets/avatar3.imageset/avatar3@2x.png -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/avatar3.imageset/avatar3@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exyte/PopupView/676d4609495f144b48a583af40b936eedfb49f3e/PopupExample/Assets.xcassets/avatar3.imageset/avatar3@3x.png -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/avatar4.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "avatar4.pdf", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/avatar4.imageset/avatar4.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exyte/PopupView/676d4609495f144b48a583af40b936eedfb49f3e/PopupExample/Assets.xcassets/avatar4.imageset/avatar4.pdf -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/avatar5.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "avatar5.pdf", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/avatar5.imageset/avatar5.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exyte/PopupView/676d4609495f144b48a583af40b936eedfb49f3e/PopupExample/Assets.xcassets/avatar5.imageset/avatar5.pdf -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/avatar6.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "avatar6.pdf", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/avatar6.imageset/avatar6.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exyte/PopupView/676d4609495f144b48a583af40b936eedfb49f3e/PopupExample/Assets.xcassets/avatar6.imageset/avatar6.pdf -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/check_circle.imageset/Check Circle.pdf: -------------------------------------------------------------------------------- 1 | %PDF-1.7 2 | 3 | 1 0 obj 4 | << >> 5 | endobj 6 | 7 | 2 0 obj 8 | << /Length 3 0 R >> 9 | stream 10 | /DeviceRGB CS 11 | /DeviceRGB cs 12 | q 13 | 1.000000 0.000000 -0.000000 1.000000 2.666626 1.166666 cm 14 | 0.002986 0.716667 0.304318 scn 15 | 25.916666 14.833333 m 16 | 25.916666 7.883751 20.282915 2.250000 13.333333 2.250000 c 17 | 13.333333 0.750000 l 18 | 21.111343 0.750000 27.416666 7.055323 27.416666 14.833333 c 19 | 25.916666 14.833333 l 20 | h 21 | 13.333333 2.250000 m 22 | 6.383750 2.250000 0.750000 7.883751 0.750000 14.833333 c 23 | -0.750000 14.833333 l 24 | -0.750000 7.055323 5.555323 0.750000 13.333333 0.750000 c 25 | 13.333333 2.250000 l 26 | h 27 | 0.750000 14.833333 m 28 | 0.750000 21.782917 6.383750 27.416666 13.333333 27.416666 c 29 | 13.333333 28.916666 l 30 | 5.555323 28.916666 -0.750000 22.611343 -0.750000 14.833333 c 31 | 0.750000 14.833333 l 32 | h 33 | 13.333333 27.416666 m 34 | 20.282915 27.416666 25.916666 21.782917 25.916666 14.833333 c 35 | 27.416666 14.833333 l 36 | 27.416666 22.611343 21.111343 28.916666 13.333333 28.916666 c 37 | 13.333333 27.416666 l 38 | h 39 | f 40 | n 41 | Q 42 | q 43 | 1.000000 0.000000 -0.000000 1.000000 11.333374 11.020220 cm 44 | 0.002986 0.716667 0.304318 scn 45 | 0.530330 4.843443 m 46 | 0.237437 5.136336 -0.237437 5.136336 -0.530330 4.843443 c 47 | -0.823223 4.550550 -0.823223 4.075675 -0.530330 3.782783 c 48 | 0.530330 4.843443 l 49 | h 50 | 2.666667 1.646446 m 51 | 2.136336 1.116116 l 52 | 2.429230 0.823223 2.904103 0.823223 3.196997 1.116116 c 53 | 2.666667 1.646446 l 54 | h 55 | 9.863663 7.782782 m 56 | 10.156556 8.075675 10.156556 8.550550 9.863663 8.843442 c 57 | 9.570769 9.136335 9.095896 9.136335 8.803002 8.843442 c 58 | 9.863663 7.782782 l 59 | h 60 | -0.530330 3.782783 m 61 | 2.136336 1.116116 l 62 | 3.196997 2.176776 l 63 | 0.530330 4.843443 l 64 | -0.530330 3.782783 l 65 | h 66 | 3.196997 1.116116 m 67 | 9.863663 7.782782 l 68 | 8.803002 8.843442 l 69 | 2.136336 2.176776 l 70 | 3.196997 1.116116 l 71 | h 72 | f 73 | n 74 | Q 75 | 76 | endstream 77 | endobj 78 | 79 | 3 0 obj 80 | 1609 81 | endobj 82 | 83 | 4 0 obj 84 | << /Annots [] 85 | /Type /Page 86 | /MediaBox [ 0.000000 0.000000 32.000000 32.000000 ] 87 | /Resources 1 0 R 88 | /Contents 2 0 R 89 | /Parent 5 0 R 90 | >> 91 | endobj 92 | 93 | 5 0 obj 94 | << /Kids [ 4 0 R ] 95 | /Count 1 96 | /Type /Pages 97 | >> 98 | endobj 99 | 100 | 6 0 obj 101 | << /Pages 5 0 R 102 | /Type /Catalog 103 | >> 104 | endobj 105 | 106 | xref 107 | 0 7 108 | 0000000000 65535 f 109 | 0000000010 00000 n 110 | 0000000034 00000 n 111 | 0000001699 00000 n 112 | 0000001722 00000 n 113 | 0000001895 00000 n 114 | 0000001969 00000 n 115 | trailer 116 | << /ID [ (some) (id) ] 117 | /Root 6 0 R 118 | /Size 7 119 | >> 120 | startxref 121 | 2028 122 | %%EOF -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/check_circle.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "Check Circle.pdf", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/checkmark.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "checkmark.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "checkmark@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "checkmark@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/checkmark.imageset/checkmark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exyte/PopupView/676d4609495f144b48a583af40b936eedfb49f3e/PopupExample/Assets.xcassets/checkmark.imageset/checkmark.png -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/checkmark.imageset/checkmark@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exyte/PopupView/676d4609495f144b48a583af40b936eedfb49f3e/PopupExample/Assets.xcassets/checkmark.imageset/checkmark@2x.png -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/checkmark.imageset/checkmark@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exyte/PopupView/676d4609495f144b48a583af40b936eedfb49f3e/PopupExample/Assets.xcassets/checkmark.imageset/checkmark@3x.png -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/chest.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "chest.pdf", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/cross.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "Vector.pdf", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/cross.imageset/Vector.pdf: -------------------------------------------------------------------------------- 1 | %PDF-1.7 2 | 3 | 1 0 obj 4 | << /ExtGState << /E1 << /ca 0.400000 >> >> >> 5 | endobj 6 | 7 | 2 0 obj 8 | << /Length 3 0 R >> 9 | stream 10 | /DeviceRGB CS 11 | /DeviceRGB cs 12 | q 13 | /E1 gs 14 | 1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm 15 | 1.000000 1.000000 1.000000 scn 16 | 6.179832 5.004830 m 17 | 9.763165 8.579830 l 18 | 9.920085 8.736750 10.008242 8.949578 10.008242 9.171496 c 19 | 10.008242 9.393414 9.920085 9.606243 9.763165 9.763164 c 20 | 9.606245 9.920083 9.393417 10.008240 9.171499 10.008240 c 21 | 8.949581 10.008240 8.736753 9.920083 8.579833 9.763164 c 22 | 5.004832 6.179831 l 23 | 1.429832 9.763164 l 24 | 1.272912 9.920083 1.060083 10.008240 0.838165 10.008240 c 25 | 0.616247 10.008240 0.403418 9.920083 0.246499 9.763164 c 26 | 0.089579 9.606243 0.001422 9.393414 0.001422 9.171496 c 27 | 0.001422 8.949578 0.089579 8.736750 0.246499 8.579830 c 28 | 3.829832 5.004830 l 29 | 0.246499 1.429831 l 30 | 0.168392 1.352362 0.106397 1.260195 0.064089 1.158646 c 31 | 0.021782 1.057096 0.000000 0.948174 0.000000 0.838164 c 32 | 0.000000 0.728155 0.021782 0.619233 0.064089 0.517684 c 33 | 0.106397 0.416135 0.168392 0.323968 0.246499 0.246499 c 34 | 0.323968 0.168391 0.416135 0.106395 0.517685 0.064088 c 35 | 0.619234 0.021781 0.728155 -0.000001 0.838165 -0.000001 c 36 | 0.948175 -0.000001 1.057097 0.021781 1.158646 0.064088 c 37 | 1.260195 0.106395 1.352363 0.168391 1.429832 0.246499 c 38 | 5.004832 3.829831 l 39 | 8.579833 0.246499 l 40 | 8.657302 0.168391 8.749469 0.106395 8.851018 0.064088 c 41 | 8.952568 0.021781 9.061489 -0.000001 9.171499 -0.000001 c 42 | 9.281509 -0.000001 9.390430 0.021781 9.491980 0.064088 c 43 | 9.593529 0.106395 9.685696 0.168391 9.763165 0.246499 c 44 | 9.841272 0.323968 9.903269 0.416135 9.945576 0.517684 c 45 | 9.987884 0.619233 10.009665 0.728155 10.009665 0.838164 c 46 | 10.009665 0.948174 9.987884 1.057096 9.945576 1.158646 c 47 | 9.903269 1.260195 9.841272 1.352362 9.763165 1.429831 c 48 | 6.179832 5.004830 l 49 | h 50 | f 51 | n 52 | Q 53 | 54 | endstream 55 | endobj 56 | 57 | 3 0 obj 58 | 1676 59 | endobj 60 | 61 | 4 0 obj 62 | << /Annots [] 63 | /Type /Page 64 | /MediaBox [ 0.000000 0.000000 10.009644 10.008240 ] 65 | /Resources 1 0 R 66 | /Contents 2 0 R 67 | /Parent 5 0 R 68 | >> 69 | endobj 70 | 71 | 5 0 obj 72 | << /Kids [ 4 0 R ] 73 | /Count 1 74 | /Type /Pages 75 | >> 76 | endobj 77 | 78 | 6 0 obj 79 | << /Pages 5 0 R 80 | /Type /Catalog 81 | >> 82 | endobj 83 | 84 | xref 85 | 0 7 86 | 0000000000 65535 f 87 | 0000000010 00000 n 88 | 0000000074 00000 n 89 | 0000001806 00000 n 90 | 0000001829 00000 n 91 | 0000002002 00000 n 92 | 0000002076 00000 n 93 | trailer 94 | << /ID [ (some) (id) ] 95 | /Root 6 0 R 96 | /Size 7 97 | >> 98 | startxref 99 | 2135 100 | %%EOF -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/file.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "file.pdf", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/file.imageset/file.pdf: -------------------------------------------------------------------------------- 1 | %PDF-1.7 2 | 3 | 1 0 obj 4 | << >> 5 | endobj 6 | 7 | 2 0 obj 8 | << /Length 3 0 R >> 9 | stream 10 | /DeviceRGB CS 11 | /DeviceRGB cs 12 | q 13 | 1.000000 0.000000 -0.000000 1.000000 13.000000 13.248413 cm 14 | 1.000000 1.000000 1.000000 scn 15 | -0.750000 8.251587 m 16 | -0.750000 5.751587 l 17 | 0.750000 5.751587 l 18 | 0.750000 8.251587 l 19 | -0.750000 8.251587 l 20 | h 21 | 5.000000 0.001587 m 22 | 9.000000 0.001587 l 23 | 9.000000 1.501587 l 24 | 5.000000 1.501587 l 25 | 5.000000 0.001587 l 26 | h 27 | -0.750000 5.751587 m 28 | -0.750000 4.594278 -0.751593 3.650847 -0.651783 2.908472 c 29 | -0.549072 2.144520 -0.326790 1.482183 0.201903 0.953490 c 30 | 1.262563 2.014150 l 31 | 1.059023 2.217690 0.915189 2.510725 0.834841 3.108343 c 32 | 0.751593 3.727537 0.750000 4.551873 0.750000 5.751587 c 33 | -0.750000 5.751587 l 34 | h 35 | 5.000000 1.501587 m 36 | 3.800286 1.501587 2.975950 1.503180 2.356756 1.586428 c 37 | 1.759138 1.666776 1.466103 1.810610 1.262563 2.014150 c 38 | 0.201903 0.953490 l 39 | 0.730596 0.424797 1.392934 0.202515 2.156885 0.099804 c 40 | 2.899261 -0.000006 3.842691 0.001587 5.000000 0.001587 c 41 | 5.000000 1.501587 l 42 | h 43 | f 44 | n 45 | Q 46 | q 47 | 1.000000 0.000000 -0.000000 1.000000 2.000000 0.486816 cm 48 | 1.000000 1.000000 1.000000 scn 49 | 13.397319 19.459538 m 50 | 12.895686 18.901985 l 51 | 12.895686 18.901985 l 52 | 13.397319 19.459538 l 53 | h 54 | 17.357389 15.896639 m 55 | 16.855757 15.339087 l 56 | 16.855757 15.339087 l 57 | 17.357389 15.896639 l 58 | h 59 | 19.660423 13.359088 m 60 | 18.975336 13.053877 l 61 | 18.975336 13.053874 l 62 | 19.660423 13.359088 l 63 | h 64 | 1.171956 2.684757 m 65 | 1.702199 3.215174 l 66 | 1.702198 3.215176 l 67 | 1.171956 2.684757 l 68 | h 69 | 18.834581 2.684757 m 70 | 18.304338 3.215174 l 71 | 18.304337 3.215174 l 72 | 18.834581 2.684757 l 73 | h 74 | 16.291039 10.656041 m 75 | 16.706970 11.280142 l 76 | 16.706968 11.280142 l 77 | 16.291039 10.656041 l 78 | h 79 | 19.720732 10.656041 m 80 | 19.304802 11.280143 l 81 | 19.304800 11.280142 l 82 | 19.720732 10.656041 l 83 | h 84 | 7.716807 10.656041 m 85 | 7.300877 11.280142 l 86 | 7.300877 11.280142 l 87 | 7.716807 10.656041 l 88 | h 89 | 10.289076 10.656041 m 90 | 10.705008 11.280142 l 91 | 10.705008 11.280142 l 92 | 10.289076 10.656041 l 93 | h 94 | 4.287115 10.656041 m 95 | 3.871184 10.031940 l 96 | 3.871184 10.031940 l 97 | 4.287115 10.656041 l 98 | h 99 | 1.714846 10.656041 m 100 | 1.298915 11.280142 l 101 | 1.298915 11.280141 l 102 | 1.714846 10.656041 l 103 | h 104 | 16.291039 6.656040 m 105 | 16.706970 7.280141 l 106 | 16.706968 7.280141 l 107 | 16.291039 6.656040 l 108 | h 109 | 19.720732 6.656041 m 110 | 20.136662 6.031940 l 111 | 20.136663 6.031941 l 112 | 19.720732 6.656041 l 113 | h 114 | 13.718769 6.656040 m 115 | 13.302837 7.280141 l 116 | 13.302837 7.280141 l 117 | 13.718769 6.656040 l 118 | h 119 | 10.289076 6.656040 m 120 | 10.705008 7.280141 l 121 | 10.705008 7.280141 l 122 | 10.289076 6.656040 l 123 | h 124 | 7.716807 6.656040 m 125 | 7.300877 7.280141 l 126 | 7.300877 7.280141 l 127 | 7.716807 6.656040 l 128 | h 129 | 13.718769 10.656041 m 130 | 13.302837 11.280142 l 131 | 13.302837 11.280142 l 132 | 13.718769 10.656041 l 133 | h 134 | 4.287115 6.656040 m 135 | 3.871184 6.031940 l 136 | 3.871184 6.031940 l 137 | 4.287115 6.656040 l 138 | h 139 | 1.714846 6.656041 m 140 | 1.298915 7.280142 l 141 | 1.298915 7.280142 l 142 | 1.714846 6.656041 l 143 | h 144 | 1.088646 6.238710 m 145 | 1.504576 5.614610 l 146 | 1.504577 5.614610 l 147 | 1.088646 6.238710 l 148 | h 149 | 0.278273 4.398304 m 150 | 1.007832 4.572212 l 151 | 1.007832 4.572212 l 152 | 0.278273 4.398304 l 153 | h 154 | 19.971657 6.515432 m 155 | 20.721012 6.484366 l 156 | 20.721012 6.484366 l 157 | 19.971657 6.515432 l 158 | h 159 | 19.999893 11.185890 m 160 | 20.749765 11.199770 l 161 | 20.749765 11.199771 l 162 | 19.999893 11.185890 l 163 | h 164 | 0.214360 5.440413 m 165 | 0.912367 5.166027 l 166 | 0.912367 5.166029 l 167 | 0.214360 5.440413 l 168 | h 169 | 0.006134 11.271131 m 170 | 0.751532 11.354085 l 171 | 0.751532 11.354086 l 172 | 0.006134 11.271131 l 173 | h 174 | 1.459776 10.493419 m 175 | 1.804160 9.827161 l 176 | 1.804163 9.827162 l 177 | 1.459776 10.493419 l 178 | h 179 | 12.003922 2.263184 m 180 | 8.002614 2.263184 l 181 | 8.002614 0.763184 l 182 | 12.003922 0.763184 l 183 | 12.003922 2.263184 l 184 | h 185 | 0.750000 11.573499 m 186 | 0.750000 13.513184 l 187 | -0.750000 13.513184 l 188 | -0.750000 11.573499 l 189 | 0.750000 11.573499 l 190 | h 191 | 12.895686 18.901985 m 192 | 16.855757 15.339087 l 193 | 17.859022 16.454191 l 194 | 13.898952 20.017090 l 195 | 12.895686 18.901985 l 196 | h 197 | 16.855757 15.339087 m 198 | 18.209637 14.120993 18.709270 13.651097 18.975336 13.053877 c 199 | 20.345512 13.664299 l 200 | 19.919353 14.620869 19.114374 15.324743 17.859022 16.454191 c 201 | 16.855757 15.339087 l 202 | h 203 | 8.032416 20.763184 m 204 | 9.614695 20.763184 10.211914 20.751612 10.744201 20.547421 c 205 | 11.281443 21.947910 l 206 | 10.429418 22.274755 9.501137 22.263184 8.032416 22.263184 c 207 | 8.032416 20.763184 l 208 | h 209 | 13.898952 20.017090 m 210 | 12.812582 20.994503 12.133384 21.621096 11.281443 21.947910 c 211 | 10.744201 20.547421 l 212 | 11.276571 20.343197 11.725601 19.954718 12.895686 18.901985 c 213 | 13.898952 20.017090 l 214 | h 215 | 8.002614 2.263184 m 216 | 6.095184 2.263184 4.740036 2.264776 3.711997 2.402946 c 217 | 2.705507 2.538221 2.125591 2.791920 1.702199 3.215174 c 218 | 0.641712 2.154341 l 219 | 1.390277 1.406021 2.339456 1.073933 3.512189 0.916313 c 220 | 4.663373 0.761591 6.137576 0.763184 8.002614 0.763184 c 221 | 8.002614 2.263184 l 222 | h 223 | 12.003922 0.763184 m 224 | 13.868961 0.763184 15.343165 0.761591 16.494349 0.916313 c 225 | 17.667082 1.073933 18.616261 1.406021 19.364824 2.154341 c 226 | 18.304337 3.215174 l 227 | 17.880945 2.791920 17.301031 2.538221 16.294540 2.402946 c 228 | 15.266500 2.264776 13.911353 2.263184 12.003922 2.263184 c 229 | 12.003922 0.763184 l 230 | h 231 | 0.750000 13.513184 m 232 | 0.750000 15.420012 0.751594 16.774658 0.889802 17.802303 c 233 | 1.025106 18.808348 1.278841 19.387974 1.702199 19.811193 c 234 | 0.641712 20.872028 l 235 | -0.106886 20.123674 -0.439128 19.174706 -0.596813 18.002239 c 236 | -0.751594 16.851372 -0.750000 15.377593 -0.750000 13.513184 c 237 | 0.750000 13.513184 l 238 | h 239 | 8.032416 22.263184 m 240 | 6.157373 22.263184 4.675969 22.264763 3.520085 22.110113 c 241 | 2.343219 21.952658 1.390861 21.620932 0.641712 20.872028 c 242 | 1.702199 19.811193 l 243 | 2.125006 20.233862 2.706711 20.487925 3.719002 20.623362 c 244 | 4.752275 20.761604 6.115123 20.763184 8.032416 20.763184 c 245 | 8.032416 22.263184 l 246 | h 247 | 15.875108 10.031940 m 248 | 17.165417 9.172016 18.846352 9.172016 20.136662 10.031940 c 249 | 19.304800 11.280142 l 250 | 18.518251 10.755949 17.493515 10.755949 16.706970 11.280142 c 251 | 15.875108 10.031940 l 252 | h 253 | 8.132738 10.031940 m 254 | 8.659678 10.383119 9.346206 10.383119 9.873145 10.031940 c 255 | 10.705008 11.280142 l 256 | 9.674305 11.967052 8.331579 11.967052 7.300877 11.280142 c 257 | 8.132738 10.031940 l 258 | h 259 | 3.871184 10.031940 m 260 | 5.161493 9.172016 6.842429 9.172016 8.132738 10.031940 c 261 | 7.300877 11.280142 l 262 | 6.514330 10.755949 5.489592 10.755949 4.703046 11.280142 c 263 | 3.871184 10.031940 l 264 | h 265 | 2.130777 10.031940 m 266 | 2.657717 10.383119 3.344245 10.383119 3.871184 10.031940 c 267 | 4.703046 11.280142 l 268 | 3.672344 11.967052 2.329617 11.967052 1.298915 11.280142 c 269 | 2.130777 10.031940 l 270 | h 271 | 15.875108 6.031940 m 272 | 17.165417 5.172014 18.846352 5.172016 20.136662 6.031940 c 273 | 19.304800 7.280142 l 274 | 18.518251 6.755948 17.493515 6.755948 16.706970 7.280141 c 275 | 15.875108 6.031940 l 276 | h 277 | 14.134700 6.031940 m 278 | 14.661639 6.383118 15.348167 6.383118 15.875108 6.031940 c 279 | 16.706968 7.280141 l 280 | 15.676266 7.967051 14.333540 7.967051 13.302837 7.280141 c 281 | 14.134700 6.031940 l 282 | h 283 | 9.873145 6.031940 m 284 | 11.163454 5.172014 12.844391 5.172014 14.134700 6.031940 c 285 | 13.302837 7.280141 l 286 | 12.516292 6.755948 11.491553 6.755948 10.705008 7.280141 c 287 | 9.873145 6.031940 l 288 | h 289 | 8.132738 6.031940 m 290 | 8.659678 6.383118 9.346206 6.383118 9.873145 6.031940 c 291 | 10.705008 7.280141 l 292 | 9.674305 7.967051 8.331579 7.967051 7.300877 7.280141 c 293 | 8.132738 6.031940 l 294 | h 295 | 14.134700 10.031940 m 296 | 14.661639 10.383119 15.348167 10.383119 15.875108 10.031940 c 297 | 16.706968 11.280142 l 298 | 15.676266 11.967052 14.333540 11.967052 13.302837 11.280142 c 299 | 14.134700 10.031940 l 300 | h 301 | 3.871184 6.031940 m 302 | 5.161493 5.172014 6.842429 5.172014 8.132738 6.031940 c 303 | 7.300877 7.280141 l 304 | 6.514330 6.755948 5.489592 6.755948 4.703046 7.280141 c 305 | 3.871184 6.031940 l 306 | h 307 | 9.873145 10.031940 m 308 | 11.163454 9.172016 12.844391 9.172016 14.134700 10.031940 c 309 | 13.302837 11.280142 l 310 | 12.516292 10.755949 11.491553 10.755949 10.705008 11.280142 c 311 | 9.873145 10.031940 l 312 | h 313 | 2.130777 6.031940 m 314 | 2.657716 6.383119 3.344244 6.383118 3.871184 6.031940 c 315 | 4.703046 7.280141 l 316 | 3.672345 7.967051 2.329618 7.967052 1.298915 7.280142 c 317 | 2.130777 6.031940 l 318 | h 319 | 1.504577 5.614610 m 320 | 2.130777 6.031940 l 321 | 1.298915 7.280142 l 322 | 0.672715 6.862811 l 323 | 1.504577 5.614610 l 324 | h 325 | -0.451286 4.224396 m 326 | -0.256317 3.406483 0.074721 2.721144 0.641713 2.154339 c 327 | 1.702198 3.215176 l 328 | 1.376703 3.540564 1.155085 3.954472 1.007832 4.572212 c 329 | -0.451286 4.224396 l 330 | h 331 | 19.222300 6.546498 m 332 | 19.144999 4.681908 18.892281 3.802925 18.304338 3.215174 c 333 | 19.364824 2.154341 l 334 | 20.394405 3.183582 20.642962 4.601696 20.721012 6.484366 c 335 | 19.222300 6.546498 l 336 | h 337 | 20.749765 11.199771 m 338 | 20.731390 12.192454 20.664989 12.947197 20.345510 13.664302 c 339 | 18.975336 13.053874 l 340 | 19.162746 12.633213 19.232058 12.142510 19.250023 11.172009 c 341 | 20.749765 11.199771 l 342 | h 343 | 20.136660 10.031940 m 344 | 20.522711 10.289222 20.758514 10.727117 20.749765 11.199770 c 345 | 19.250023 11.172010 l 346 | 19.249142 11.219550 19.272644 11.258712 19.304802 11.280143 c 347 | 20.136660 10.031940 l 348 | h 349 | 0.672715 6.862811 m 350 | 0.434373 6.703969 0.199842 6.549189 0.016436 6.391880 c 351 | -0.186187 6.218088 -0.368468 6.007800 -0.483647 5.714796 c 352 | 0.912367 5.166029 l 353 | 0.914552 5.171587 0.916551 5.175688 0.922846 5.183876 c 354 | 0.930993 5.194475 0.950079 5.216505 0.992994 5.253313 c 355 | 1.091445 5.337755 1.236630 5.436037 1.504576 5.614610 c 356 | 0.672715 6.862811 l 357 | h 358 | 1.007832 4.572212 m 359 | 0.943789 4.840878 0.914517 4.970667 0.905153 5.068638 c 360 | 0.898916 5.133884 0.905281 5.148003 0.912367 5.166027 c 361 | -0.483646 5.714798 l 362 | -0.701090 5.161642 -0.555377 4.661068 -0.451286 4.224396 c 363 | 1.007832 4.572212 l 364 | h 365 | 20.136663 6.031941 m 366 | 19.739531 5.767271 19.202213 6.061961 19.222300 6.546498 c 367 | 20.721012 6.484366 l 368 | 20.752035 7.232711 19.921692 7.691271 19.304798 7.280141 c 369 | 20.136663 6.031941 l 370 | h 371 | -0.750000 11.573499 m 372 | -0.750000 11.434543 -0.751280 11.296141 -0.739264 11.188175 c 373 | 0.751532 11.354086 l 374 | 0.752536 11.345060 0.751351 11.350513 0.750671 11.393123 c 375 | 0.750023 11.433824 0.750000 11.488574 0.750000 11.573499 c 376 | -0.750000 11.573499 l 377 | h 378 | 1.298915 11.280141 m 379 | 1.228231 11.233034 1.182645 11.202681 1.148405 11.180640 c 380 | 1.112560 11.157568 1.107345 11.155518 1.115389 11.159676 c 381 | 1.804163 9.827162 l 382 | 1.900654 9.877038 2.015105 9.954851 2.130778 10.031941 c 383 | 1.298915 11.280141 l 384 | h 385 | -0.739264 11.188176 m 386 | -0.603460 9.967891 0.713858 9.263590 1.804160 9.827161 c 387 | 1.115391 11.159677 l 388 | 0.959110 11.078896 0.770898 11.180066 0.751532 11.354085 c 389 | -0.739264 11.188176 l 390 | h 391 | f 392 | n 393 | Q 394 | 395 | endstream 396 | endobj 397 | 398 | 3 0 obj 399 | 9806 400 | endobj 401 | 402 | 4 0 obj 403 | << /Annots [] 404 | /Type /Page 405 | /MediaBox [ 0.000000 0.000000 24.000000 24.000000 ] 406 | /Resources 1 0 R 407 | /Contents 2 0 R 408 | /Parent 5 0 R 409 | >> 410 | endobj 411 | 412 | 5 0 obj 413 | << /Kids [ 4 0 R ] 414 | /Count 1 415 | /Type /Pages 416 | >> 417 | endobj 418 | 419 | 6 0 obj 420 | << /Pages 5 0 R 421 | /Type /Catalog 422 | >> 423 | endobj 424 | 425 | xref 426 | 0 7 427 | 0000000000 65535 f 428 | 0000000010 00000 n 429 | 0000000034 00000 n 430 | 0000009896 00000 n 431 | 0000009919 00000 n 432 | 0000010092 00000 n 433 | 0000010166 00000 n 434 | trailer 435 | << /ID [ (some) (id) ] 436 | /Root 6 0 R 437 | /Size 7 438 | >> 439 | startxref 440 | 10225 441 | %%EOF -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/fitness.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "fitness.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "fitness@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "fitness@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/fitness.imageset/fitness.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exyte/PopupView/676d4609495f144b48a583af40b936eedfb49f3e/PopupExample/Assets.xcassets/fitness.imageset/fitness.png -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/fitness.imageset/fitness@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exyte/PopupView/676d4609495f144b48a583af40b936eedfb49f3e/PopupExample/Assets.xcassets/fitness.imageset/fitness@2x.png -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/fitness.imageset/fitness@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exyte/PopupView/676d4609495f144b48a583af40b936eedfb49f3e/PopupExample/Assets.xcassets/fitness.imageset/fitness@3x.png -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/gift.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "gift.pdf", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/gift.imageset/gift.pdf: -------------------------------------------------------------------------------- 1 | %PDF-1.7 2 | 3 | 1 0 obj 4 | << >> 5 | endobj 6 | 7 | 2 0 obj 8 | << /Length 3 0 R >> 9 | stream 10 | /DeviceRGB CS 11 | /DeviceRGB cs 12 | q 13 | 1.000000 0.000000 -0.000000 1.000000 11.548218 3.000000 cm 14 | 0.827451 0.756863 0.988235 scn 15 | 0.800000 34.465820 m 16 | 40.102154 34.465820 l 17 | 40.102154 36.465820 l 18 | 0.800000 36.465820 l 19 | 0.800000 34.465820 l 20 | h 21 | 39.902157 34.665821 m 22 | 39.902157 2.800419 l 23 | 41.902157 2.800419 l 24 | 41.902157 34.665821 l 25 | 39.902157 34.665821 l 26 | h 27 | 40.102158 3.000420 m 28 | 0.800001 3.000420 l 29 | 0.800001 1.000420 l 30 | 40.102158 1.000420 l 31 | 40.102158 3.000420 l 32 | h 33 | 1.000000 2.800419 m 34 | 1.000000 34.665821 l 35 | -1.000000 34.665821 l 36 | -1.000000 2.800419 l 37 | 1.000000 2.800419 l 38 | h 39 | 0.800001 3.000420 m 40 | 0.910456 3.000420 1.000000 2.910877 1.000000 2.800419 c 41 | -1.000000 2.800419 l 42 | -1.000000 1.806305 -0.194109 1.000420 0.800001 1.000420 c 43 | 0.800001 3.000420 l 44 | h 45 | 39.902157 2.800419 m 46 | 39.902157 2.910877 39.991703 3.000420 40.102158 3.000420 c 47 | 40.102158 1.000420 l 48 | 41.096264 1.000420 41.902157 1.806301 41.902157 2.800419 c 49 | 39.902157 2.800419 l 50 | h 51 | 40.102154 34.465820 m 52 | 39.991699 34.465820 39.902157 34.555363 39.902157 34.665821 c 53 | 41.902157 34.665821 l 54 | 41.902157 35.659935 41.096264 36.465820 40.102154 36.465820 c 55 | 40.102154 34.465820 l 56 | h 57 | 0.800000 36.465820 m 58 | -0.194109 36.465820 -1.000000 35.659939 -1.000000 34.665821 c 59 | 1.000000 34.665821 l 60 | 1.000000 34.555363 0.910453 34.465820 0.800000 34.465820 c 61 | 0.800000 36.465820 l 62 | h 63 | f 64 | n 65 | Q 66 | q 67 | 1.000000 0.000000 -0.000000 1.000000 7.829956 36.465820 cm 68 | 0.827451 0.756863 0.988235 scn 69 | 0.800000 10.295898 m 70 | 47.538914 10.295898 l 71 | 47.538914 12.295898 l 72 | 0.800000 12.295898 l 73 | 0.800000 10.295898 l 74 | h 75 | 47.338913 10.495898 m 76 | 47.338913 2.799953 l 77 | 49.338913 2.799953 l 78 | 49.338913 10.495898 l 79 | 47.338913 10.495898 l 80 | h 81 | 47.538914 2.999954 m 82 | 0.800001 2.999954 l 83 | 0.800001 0.999954 l 84 | 47.538914 0.999954 l 85 | 47.538914 2.999954 l 86 | h 87 | 1.000000 2.799954 m 88 | 1.000000 10.495899 l 89 | -1.000000 10.495899 l 90 | -1.000000 2.799954 l 91 | 1.000000 2.799954 l 92 | h 93 | 0.800001 2.999954 m 94 | 0.910457 2.999954 1.000000 2.910412 1.000000 2.799954 c 95 | -1.000000 2.799954 l 96 | -1.000000 1.805840 -0.194111 0.999954 0.800001 0.999954 c 97 | 0.800001 2.999954 l 98 | h 99 | 47.338913 2.799953 m 100 | 47.338913 2.910412 47.428459 2.999954 47.538914 2.999954 c 101 | 47.538914 0.999954 l 102 | 48.533024 0.999954 49.338913 1.805839 49.338913 2.799953 c 103 | 47.338913 2.799953 l 104 | h 105 | 47.538914 10.295898 m 106 | 47.428455 10.295898 47.338913 10.385441 47.338913 10.495898 c 107 | 49.338913 10.495898 l 108 | 49.338913 11.490012 48.533024 12.295898 47.538914 12.295898 c 109 | 47.538914 10.295898 l 110 | h 111 | 0.800000 12.295898 m 112 | -0.194111 12.295898 -1.000000 11.490014 -1.000000 10.495899 c 113 | 1.000000 10.495899 l 114 | 1.000000 10.385441 0.910455 10.295898 0.800000 10.295898 c 115 | 0.800000 12.295898 l 116 | h 117 | f 118 | n 119 | Q 120 | q 121 | -0.000000 1.000000 -1.000000 -0.000000 72.113159 5.000002 cm 122 | 0.827451 0.756863 0.988235 scn 123 | 0.000000 44.761719 m 124 | 0.000000 45.761719 l 125 | -1.000000 45.761719 l 126 | -1.000000 44.761719 l 127 | 0.000000 44.761719 l 128 | h 129 | 42.761345 44.761719 m 130 | 43.761345 44.761719 l 131 | 43.761345 45.761719 l 132 | 42.761345 45.761719 l 133 | 42.761345 44.761719 l 134 | h 135 | 42.761345 35.465775 m 136 | 42.761345 34.465775 l 137 | 43.761345 34.465775 l 138 | 43.761345 35.465775 l 139 | 42.761345 35.465775 l 140 | h 141 | 0.000000 35.465775 m 142 | -1.000000 35.465775 l 143 | -1.000000 34.465775 l 144 | 0.000000 34.465775 l 145 | 0.000000 35.465775 l 146 | h 147 | 0.000000 43.761719 m 148 | 42.761345 43.761719 l 149 | 42.761345 45.761719 l 150 | 0.000000 45.761719 l 151 | 0.000000 43.761719 l 152 | h 153 | 41.761345 44.761719 m 154 | 41.761345 35.465775 l 155 | 43.761345 35.465775 l 156 | 43.761345 44.761719 l 157 | 41.761345 44.761719 l 158 | h 159 | 42.761345 36.465775 m 160 | 0.000000 36.465775 l 161 | 0.000000 34.465775 l 162 | 42.761345 34.465775 l 163 | 42.761345 36.465775 l 164 | h 165 | 1.000000 35.465775 m 166 | 1.000000 44.761719 l 167 | -1.000000 44.761719 l 168 | -1.000000 35.465775 l 169 | 1.000000 35.465775 l 170 | h 171 | f 172 | n 173 | Q 174 | q 175 | 0.707107 -0.707107 -0.707107 -0.707107 41.293781 62.373005 cm 176 | 0.827451 0.756863 0.988235 scn 177 | 4.647972 4.769764 m 178 | 4.647972 3.769764 l 179 | 4.647972 4.769764 l 180 | h 181 | 3.718378 16.854492 m 182 | 4.318378 17.654491 l 183 | 3.631667 18.169525 l 184 | 3.018528 17.568783 l 185 | 3.718378 16.854492 l 186 | h 187 | 6.436756 10.207891 m 188 | 6.436756 8.018752 6.091123 6.897692 5.729475 6.347184 c 189 | 5.427681 5.887786 5.080161 5.769764 4.647972 5.769764 c 190 | 4.647972 3.769764 l 191 | 5.610176 3.769764 6.657047 4.116540 7.401046 5.249072 c 192 | 8.085193 6.290494 8.436756 7.888498 8.436756 10.207891 c 193 | 6.436756 10.207891 l 194 | h 195 | 4.647972 5.769764 m 196 | 3.986962 5.769764 3.073215 6.188008 2.289848 7.039577 c 197 | 1.525780 7.870164 1.000000 8.996224 1.000000 10.207891 c 198 | -1.000000 10.207891 l 199 | -1.000000 8.416162 -0.228596 6.823160 0.817922 5.685533 c 200 | 1.845139 4.568886 3.255379 3.769764 4.647972 3.769764 c 201 | 4.647972 5.769764 l 202 | h 203 | 1.000000 10.207891 m 204 | 1.000000 11.366888 1.752103 12.802116 2.669673 14.071704 c 205 | 3.108615 14.679043 3.549703 15.200510 3.881659 15.570442 c 206 | 4.047126 15.754840 4.184189 15.900154 4.278595 15.998099 c 207 | 4.325767 16.047039 4.362194 16.084053 4.386085 16.108105 c 208 | 4.398027 16.120127 4.406828 16.128901 4.412262 16.134296 c 209 | 4.414979 16.136993 4.416854 16.138845 4.417858 16.139835 c 210 | 4.418359 16.140331 4.418643 16.140610 4.418706 16.140671 c 211 | 4.418738 16.140703 4.418714 16.140678 4.418634 16.140600 c 212 | 4.418594 16.140562 4.418493 16.140461 4.418473 16.140442 c 213 | 4.418357 16.140329 4.418228 16.140202 3.718378 16.854492 c 214 | 3.018528 17.568783 3.018370 17.568626 3.018198 17.568459 c 215 | 3.018121 17.568384 3.017935 17.568201 3.017782 17.568050 c 216 | 3.017476 17.567751 3.017113 17.567394 3.016695 17.566982 c 217 | 3.015857 17.566158 3.014793 17.565113 3.013508 17.563845 c 218 | 3.010937 17.561310 3.007478 17.557890 3.003160 17.553602 c 219 | 2.994523 17.545029 2.982444 17.532978 2.967152 17.517584 c 220 | 2.936575 17.486801 2.893115 17.442610 2.838612 17.386065 c 221 | 2.729670 17.273041 2.576234 17.110266 2.393104 16.906183 c 222 | 2.027864 16.499159 1.539357 15.922112 1.048705 15.243226 c 223 | 0.107086 13.940362 -1.000000 12.052290 -1.000000 10.207891 c 224 | 1.000000 10.207891 l 225 | h 226 | 3.718378 16.854492 m 227 | 3.118378 16.054493 3.118132 16.054676 3.117901 16.054850 c 228 | 3.117842 16.054893 3.117625 16.055058 3.117507 16.055145 c 229 | 3.117273 16.055323 3.117094 16.055456 3.116970 16.055550 c 230 | 3.116722 16.055738 3.116694 16.055759 3.116882 16.055616 c 231 | 3.117259 16.055330 3.118500 16.054388 3.120579 16.052794 c 232 | 3.124738 16.049604 3.132241 16.043816 3.142866 16.035480 c 233 | 3.164123 16.018803 3.197818 15.991974 3.242179 15.955416 c 234 | 3.330987 15.882228 3.462000 15.770494 3.621158 15.623604 c 235 | 3.940624 15.328764 4.367190 14.898074 4.792189 14.358856 c 236 | 5.655714 13.263259 6.436756 11.817486 6.436756 10.207891 c 237 | 8.436756 10.207891 l 238 | 8.436755 12.456114 7.358608 14.333641 6.362945 15.596890 c 239 | 5.858349 16.237095 5.355320 16.744715 4.977591 17.093328 c 240 | 4.788151 17.268166 4.628665 17.404446 4.514125 17.498838 c 241 | 4.456811 17.546070 4.410620 17.582924 4.377408 17.608982 c 242 | 4.360798 17.622013 4.347422 17.632355 4.337510 17.639956 c 243 | 4.332553 17.643755 4.328462 17.646872 4.325264 17.649296 c 244 | 4.323665 17.650509 4.322289 17.651548 4.321140 17.652414 c 245 | 4.320566 17.652847 4.320048 17.653236 4.319588 17.653584 c 246 | 4.319357 17.653757 4.319055 17.653984 4.318940 17.654070 c 247 | 4.318652 17.654287 4.318378 17.654491 3.718378 16.854492 c 248 | h 249 | f 250 | n 251 | Q 252 | q 253 | -0.707107 -0.707107 0.707107 -0.707107 22.720867 62.367146 cm 254 | 0.827451 0.756863 0.988235 scn 255 | 4.647972 4.769764 m 256 | 4.647972 3.769764 l 257 | 4.647972 4.769764 l 258 | h 259 | 3.718378 16.854492 m 260 | 4.318378 17.654491 l 261 | 3.631667 18.169525 l 262 | 3.018528 17.568783 l 263 | 3.718378 16.854492 l 264 | h 265 | 6.436756 10.207891 m 266 | 6.436756 8.018752 6.091123 6.897692 5.729475 6.347184 c 267 | 5.427681 5.887786 5.080161 5.769764 4.647972 5.769764 c 268 | 4.647972 3.769764 l 269 | 5.610176 3.769764 6.657047 4.116540 7.401046 5.249072 c 270 | 8.085193 6.290494 8.436756 7.888498 8.436756 10.207891 c 271 | 6.436756 10.207891 l 272 | h 273 | 4.647972 5.769764 m 274 | 3.986962 5.769764 3.073215 6.188008 2.289848 7.039577 c 275 | 1.525780 7.870164 1.000000 8.996224 1.000000 10.207891 c 276 | -1.000000 10.207891 l 277 | -1.000000 8.416162 -0.228596 6.823160 0.817922 5.685533 c 278 | 1.845139 4.568886 3.255379 3.769764 4.647972 3.769764 c 279 | 4.647972 5.769764 l 280 | h 281 | 1.000000 10.207891 m 282 | 1.000000 11.366888 1.752103 12.802116 2.669673 14.071704 c 283 | 3.108615 14.679043 3.549703 15.200510 3.881659 15.570442 c 284 | 4.047126 15.754840 4.184189 15.900154 4.278595 15.998099 c 285 | 4.325767 16.047039 4.362194 16.084053 4.386085 16.108105 c 286 | 4.398027 16.120127 4.406828 16.128901 4.412262 16.134296 c 287 | 4.414979 16.136993 4.416854 16.138845 4.417858 16.139835 c 288 | 4.418359 16.140331 4.418643 16.140610 4.418706 16.140671 c 289 | 4.418738 16.140703 4.418714 16.140678 4.418634 16.140600 c 290 | 4.418594 16.140562 4.418493 16.140461 4.418473 16.140442 c 291 | 4.418357 16.140329 4.418228 16.140202 3.718378 16.854492 c 292 | 3.018528 17.568783 3.018370 17.568626 3.018198 17.568459 c 293 | 3.018121 17.568384 3.017935 17.568201 3.017782 17.568050 c 294 | 3.017476 17.567751 3.017113 17.567394 3.016695 17.566982 c 295 | 3.015857 17.566158 3.014793 17.565113 3.013508 17.563845 c 296 | 3.010937 17.561310 3.007478 17.557890 3.003160 17.553602 c 297 | 2.994523 17.545029 2.982444 17.532978 2.967152 17.517584 c 298 | 2.936575 17.486801 2.893115 17.442610 2.838612 17.386065 c 299 | 2.729670 17.273041 2.576234 17.110266 2.393104 16.906183 c 300 | 2.027864 16.499159 1.539357 15.922112 1.048705 15.243226 c 301 | 0.107086 13.940362 -1.000000 12.052290 -1.000000 10.207891 c 302 | 1.000000 10.207891 l 303 | h 304 | 3.718378 16.854492 m 305 | 3.118378 16.054493 3.118132 16.054676 3.117901 16.054850 c 306 | 3.117842 16.054893 3.117625 16.055058 3.117507 16.055145 c 307 | 3.117273 16.055323 3.117094 16.055456 3.116970 16.055550 c 308 | 3.116722 16.055738 3.116694 16.055759 3.116882 16.055616 c 309 | 3.117259 16.055330 3.118500 16.054388 3.120579 16.052794 c 310 | 3.124738 16.049604 3.132241 16.043816 3.142866 16.035480 c 311 | 3.164123 16.018803 3.197818 15.991974 3.242179 15.955416 c 312 | 3.330987 15.882228 3.462000 15.770494 3.621158 15.623604 c 313 | 3.940624 15.328764 4.367190 14.898074 4.792189 14.358856 c 314 | 5.655714 13.263259 6.436756 11.817486 6.436756 10.207891 c 315 | 8.436756 10.207891 l 316 | 8.436755 12.456114 7.358608 14.333641 6.362945 15.596890 c 317 | 5.858349 16.237095 5.355320 16.744715 4.977591 17.093328 c 318 | 4.788151 17.268166 4.628665 17.404446 4.514125 17.498838 c 319 | 4.456811 17.546070 4.410620 17.582924 4.377408 17.608982 c 320 | 4.360798 17.622013 4.347422 17.632355 4.337510 17.639956 c 321 | 4.332553 17.643755 4.328462 17.646872 4.325264 17.649296 c 322 | 4.323665 17.650509 4.322289 17.651548 4.321140 17.652414 c 323 | 4.320566 17.652847 4.320048 17.653236 4.319588 17.653584 c 324 | 4.319357 17.653757 4.319055 17.653984 4.318940 17.654070 c 325 | 4.318652 17.654287 4.318378 17.654491 3.718378 16.854492 c 326 | h 327 | f 328 | n 329 | Q 330 | 331 | endstream 332 | endobj 333 | 334 | 3 0 obj 335 | 10142 336 | endobj 337 | 338 | 4 0 obj 339 | << /Annots [] 340 | /Type /Page 341 | /MediaBox [ 0.000000 0.000000 64.000000 64.000000 ] 342 | /Resources 1 0 R 343 | /Contents 2 0 R 344 | /Parent 5 0 R 345 | >> 346 | endobj 347 | 348 | 5 0 obj 349 | << /Kids [ 4 0 R ] 350 | /Count 1 351 | /Type /Pages 352 | >> 353 | endobj 354 | 355 | 6 0 obj 356 | << /Pages 5 0 R 357 | /Type /Catalog 358 | >> 359 | endobj 360 | 361 | xref 362 | 0 7 363 | 0000000000 65535 f 364 | 0000000010 00000 n 365 | 0000000034 00000 n 366 | 0000010232 00000 n 367 | 0000010256 00000 n 368 | 0000010429 00000 n 369 | 0000010503 00000 n 370 | trailer 371 | << /ID [ (some) (id) ] 372 | /Root 6 0 R 373 | /Size 7 374 | >> 375 | startxref 376 | 10562 377 | %%EOF -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/gift2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "Gift.pdf", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/gift2.imageset/Gift.pdf: -------------------------------------------------------------------------------- 1 | %PDF-1.7 2 | 3 | 1 0 obj 4 | << >> 5 | endobj 6 | 7 | 2 0 obj 8 | << /Length 3 0 R >> 9 | stream 10 | /DeviceRGB CS 11 | /DeviceRGB cs 12 | q 13 | 1.000000 0.000000 -0.000000 1.000000 3.333313 21.250389 cm 14 | 1.000000 1.000000 1.000000 scn 15 | 15.416273 15.416275 m 16 | 8.385745 15.409420 4.748133 15.283645 2.440382 12.975893 c 17 | 0.132631 10.668142 0.006855 7.030530 0.000000 0.000002 c 18 | 8.189196 0.000002 l 19 | 7.604244 0.730662 7.167176 1.592505 6.928849 2.545813 c 20 | 6.031759 6.134171 9.282106 9.384516 12.870464 8.487427 c 21 | 13.823773 8.249100 14.685613 7.812031 15.416273 7.227080 c 22 | 15.416273 15.416275 l 23 | h 24 | f 25 | n 26 | Q 27 | q 28 | 1.000000 0.000000 -0.000000 1.000000 3.333313 21.250393 cm 29 | 1.000000 1.000000 1.000000 scn 30 | 0.000000 -2.500002 m 31 | 0.006855 -9.530529 0.132631 -13.168140 2.440382 -15.475893 c 32 | 4.748133 -17.783646 8.385745 -17.909420 15.416273 -17.916275 c 33 | 15.416273 -4.789415 l 34 | 14.116873 -7.384815 11.432992 -9.166670 8.332939 -9.166670 c 35 | 7.642582 -9.166670 7.082939 -8.607025 7.082939 -7.916670 c 36 | 7.082939 -7.226315 7.642582 -6.666670 8.332939 -6.666670 c 37 | 10.894257 -6.666670 13.040359 -4.888918 13.604664 -2.500002 c 38 | 0.000000 -2.500002 l 39 | h 40 | f 41 | n 42 | Q 43 | q 44 | 1.000000 0.000000 -0.000000 1.000000 3.333313 21.250393 cm 45 | 1.000000 1.000000 1.000000 scn 46 | 17.916273 -17.916275 m 47 | 24.946800 -17.909420 28.584412 -17.783646 30.892164 -15.475893 c 48 | 33.199917 -13.168140 33.325691 -9.530529 33.332546 -2.500002 c 49 | 19.727882 -2.500002 l 50 | 20.292185 -4.888918 22.438290 -6.666670 24.999607 -6.666670 c 51 | 25.689962 -6.666670 26.249607 -7.226315 26.249607 -7.916670 c 52 | 26.249607 -8.607025 25.689962 -9.166670 24.999607 -9.166670 c 53 | 21.899551 -9.166670 19.215673 -7.384815 17.916273 -4.789415 c 54 | 17.916273 -17.916275 l 55 | h 56 | f 57 | n 58 | Q 59 | q 60 | 1.000000 0.000000 -0.000000 1.000000 3.333313 21.250389 cm 61 | 1.000000 1.000000 1.000000 scn 62 | 33.332546 0.000002 m 63 | 33.325691 7.030530 33.199917 10.668142 30.892164 12.975893 c 64 | 28.584412 15.283645 24.946800 15.409420 17.916273 15.416275 c 65 | 17.916273 7.227080 l 66 | 18.646935 7.812031 19.508772 8.249100 20.462082 8.487427 c 67 | 24.050438 9.384516 27.300785 6.134171 26.403696 2.545813 c 68 | 26.165365 1.592505 25.728300 0.730662 25.143349 0.000002 c 69 | 33.332546 0.000002 l 70 | h 71 | f 72 | n 73 | Q 74 | q 75 | 1.000000 0.000000 -0.000000 1.000000 3.333313 30.165241 cm 76 | 1.000000 1.000000 1.000000 scn 77 | 12.264124 -2.852782 m 78 | 14.116663 -3.315917 15.416273 -4.980426 15.416273 -6.889980 c 79 | 15.416273 -8.914850 l 80 | 13.391403 -8.914850 l 81 | 11.481850 -8.914850 9.817340 -7.615240 9.354205 -5.762700 c 82 | 8.914853 -4.005294 10.506717 -2.413430 12.264124 -2.852782 c 83 | h 84 | f 85 | n 86 | Q 87 | q 88 | 1.000000 0.000000 -0.000000 1.000000 3.333313 30.165241 cm 89 | 1.000000 1.000000 1.000000 scn 90 | 17.916273 -6.889980 m 91 | 17.916273 -8.914850 l 92 | 19.941139 -8.914850 l 93 | 21.850695 -8.914850 23.515207 -7.615240 23.978340 -5.762700 c 94 | 24.417690 -4.005294 22.825827 -2.413430 21.068422 -2.852782 c 95 | 19.215879 -3.315917 17.916273 -4.980426 17.916273 -6.889980 c 96 | h 97 | f 98 | n 99 | Q 100 | 101 | endstream 102 | endobj 103 | 104 | 3 0 obj 105 | 2717 106 | endobj 107 | 108 | 4 0 obj 109 | << /Annots [] 110 | /Type /Page 111 | /MediaBox [ 0.000000 0.000000 40.000000 40.000000 ] 112 | /Resources 1 0 R 113 | /Contents 2 0 R 114 | /Parent 5 0 R 115 | >> 116 | endobj 117 | 118 | 5 0 obj 119 | << /Kids [ 4 0 R ] 120 | /Count 1 121 | /Type /Pages 122 | >> 123 | endobj 124 | 125 | 6 0 obj 126 | << /Pages 5 0 R 127 | /Type /Catalog 128 | >> 129 | endobj 130 | 131 | xref 132 | 0 7 133 | 0000000000 65535 f 134 | 0000000010 00000 n 135 | 0000000034 00000 n 136 | 0000002807 00000 n 137 | 0000002830 00000 n 138 | 0000003003 00000 n 139 | 0000003077 00000 n 140 | trailer 141 | << /ID [ (some) (id) ] 142 | /Root 6 0 R 143 | /Size 7 144 | >> 145 | startxref 146 | 3136 147 | %%EOF -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/grapes.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "grapes (7).png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/grapes.imageset/grapes (7).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exyte/PopupView/676d4609495f144b48a583af40b936eedfb49f3e/PopupExample/Assets.xcassets/grapes.imageset/grapes (7).png -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/okay.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "okay-svg (1).png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/okay.imageset/okay-svg (1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exyte/PopupView/676d4609495f144b48a583af40b936eedfb49f3e/PopupExample/Assets.xcassets/okay.imageset/okay-svg (1).png -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/phone_call.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "phone_call.pdf", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/phone_call.imageset/phone_call.pdf: -------------------------------------------------------------------------------- 1 | %PDF-1.7 2 | 3 | 1 0 obj 4 | << >> 5 | endobj 6 | 7 | 2 0 obj 8 | << /Length 3 0 R >> 9 | stream 10 | /DeviceRGB CS 11 | /DeviceRGB cs 12 | q 13 | 1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm 14 | 1.000000 0.270588 0.227451 scn 15 | 36.000000 18.000000 m 16 | 36.000000 8.058874 27.941126 0.000000 18.000000 0.000000 c 17 | 8.058874 0.000000 0.000000 8.058874 0.000000 18.000000 c 18 | 0.000000 27.941126 8.058874 36.000000 18.000000 36.000000 c 19 | 27.941126 36.000000 36.000000 27.941126 36.000000 18.000000 c 20 | h 21 | f 22 | n 23 | Q 24 | q 25 | 1.000000 0.000000 -0.000000 1.000000 8.000000 14.673157 cm 26 | 1.000000 1.000000 1.000000 scn 27 | 9.758739 7.326843 m 28 | 8.557892 7.326843 7.401317 7.238302 6.289012 7.061218 c 29 | 5.182241 6.889669 4.180614 6.624044 3.284130 6.264343 c 30 | 2.387645 5.904643 1.659943 5.445333 1.101024 4.886414 c 31 | 0.719188 4.504578 0.434195 4.081238 0.246044 3.616394 c 32 | 0.057893 3.157084 -0.022348 2.647970 0.005321 2.089050 c 33 | 0.021923 1.751485 0.080028 1.438822 0.179637 1.151062 c 34 | 0.284781 0.863302 0.436962 0.622579 0.636180 0.428894 c 35 | 0.785594 0.279480 0.957144 0.166036 1.150829 0.088562 c 36 | 1.350048 0.016622 1.568635 0.000021 1.806591 0.038757 c 37 | 4.886180 0.561707 l 38 | 5.339956 0.639181 5.671988 0.783061 5.882274 0.993347 c 39 | 6.020620 1.126160 6.114696 1.292175 6.164501 1.491394 c 40 | 6.214305 1.690613 6.239208 1.920268 6.239208 2.180359 c 41 | 6.239208 3.018738 l 42 | 6.239208 3.129415 6.286245 3.234558 6.380321 3.334167 c 43 | 6.419058 3.383972 6.466096 3.419942 6.521434 3.442078 c 44 | 6.582307 3.469747 6.634878 3.489115 6.679149 3.500183 c 45 | 6.961376 3.566589 7.373648 3.624695 7.915966 3.674500 c 46 | 8.463818 3.729838 9.078074 3.757507 9.758739 3.757507 c 47 | 10.439403 3.757507 11.050894 3.732605 11.593211 3.682800 c 48 | 12.135529 3.638530 12.550568 3.574890 12.838328 3.491882 c 49 | 12.882599 3.480815 12.929637 3.461446 12.979442 3.433777 c 50 | 13.034781 3.411641 13.084585 3.378438 13.128856 3.334167 c 51 | 13.211864 3.245626 13.256134 3.143250 13.261668 3.027039 c 52 | 13.269969 2.180359 l 53 | 13.275503 1.920268 13.303172 1.690613 13.352977 1.491394 c 54 | 13.402781 1.292175 13.494090 1.126160 13.626903 0.993347 c 55 | 13.732046 0.888204 13.867625 0.799662 14.033641 0.727722 c 56 | 14.199656 0.655782 14.398875 0.600443 14.631297 0.561707 c 57 | 17.669384 0.047058 l 58 | 17.912874 0.002787 18.136995 0.019389 18.341747 0.096863 c 59 | 18.552034 0.179871 18.737417 0.298849 18.897900 0.453796 c 60 | 19.296337 0.841166 19.503857 1.389018 19.520458 2.097351 c 61 | 19.531527 2.656271 19.434683 3.165385 19.229931 3.624695 c 62 | 19.030712 4.089539 18.742952 4.510112 18.366650 4.886414 c 63 | 17.807730 5.450867 17.082796 5.912943 16.191845 6.272644 c 64 | 15.300895 6.632345 14.304800 6.897970 13.203563 7.069519 c 65 | 12.102325 7.241068 10.954051 7.326843 9.758739 7.326843 c 66 | h 67 | f 68 | n 69 | Q 70 | 71 | endstream 72 | endobj 73 | 74 | 3 0 obj 75 | 2544 76 | endobj 77 | 78 | 4 0 obj 79 | << /Annots [] 80 | /Type /Page 81 | /MediaBox [ 0.000000 0.000000 36.000000 36.000000 ] 82 | /Resources 1 0 R 83 | /Contents 2 0 R 84 | /Parent 5 0 R 85 | >> 86 | endobj 87 | 88 | 5 0 obj 89 | << /Kids [ 4 0 R ] 90 | /Count 1 91 | /Type /Pages 92 | >> 93 | endobj 94 | 95 | 6 0 obj 96 | << /Pages 5 0 R 97 | /Type /Catalog 98 | >> 99 | endobj 100 | 101 | xref 102 | 0 7 103 | 0000000000 65535 f 104 | 0000000010 00000 n 105 | 0000000034 00000 n 106 | 0000002634 00000 n 107 | 0000002657 00000 n 108 | 0000002830 00000 n 109 | 0000002904 00000 n 110 | trailer 111 | << /ID [ (some) (id) ] 112 | /Root 6 0 R 113 | /Size 7 114 | >> 115 | startxref 116 | 2963 117 | %%EOF -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/phone_call2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "phone_call2.pdf", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/phone_call2.imageset/phone_call2.pdf: -------------------------------------------------------------------------------- 1 | %PDF-1.7 2 | 3 | 1 0 obj 4 | << >> 5 | endobj 6 | 7 | 2 0 obj 8 | << /Length 3 0 R >> 9 | stream 10 | /DeviceRGB CS 11 | /DeviceRGB cs 12 | q 13 | 1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm 14 | 0.203922 0.780392 0.349020 scn 15 | 36.000000 18.000000 m 16 | 36.000000 8.058874 27.941126 0.000000 18.000000 0.000000 c 17 | 8.058874 0.000000 0.000000 8.058874 0.000000 18.000000 c 18 | 0.000000 27.941126 8.058874 36.000000 18.000000 36.000000 c 19 | 27.941126 36.000000 36.000000 27.941126 36.000000 18.000000 c 20 | h 21 | f 22 | n 23 | Q 24 | q 25 | 1.000000 0.000000 -0.000000 1.000000 10.000000 9.975708 cm 26 | 1.000000 1.000000 1.000000 scn 27 | 4.110033 4.142415 m 28 | 4.886732 3.365716 5.706580 2.664529 6.569579 2.038855 c 29 | 7.432578 1.413181 8.295577 0.916957 9.158576 0.550182 c 30 | 10.021575 0.183408 10.838727 0.000020 11.610032 0.000020 c 31 | 12.133225 0.000020 12.621359 0.094411 13.074433 0.283192 c 32 | 13.527508 0.471972 13.937432 0.768628 14.304207 1.173160 c 33 | 14.519957 1.410484 14.689859 1.666687 14.813916 1.941768 c 34 | 14.937972 2.211454 15.000000 2.483839 15.000000 2.758920 c 35 | 15.000000 2.963882 14.959547 3.158057 14.878641 3.341444 c 36 | 14.797734 3.530225 14.660194 3.692038 14.466020 3.826881 c 37 | 11.982201 5.590635 l 38 | 11.793420 5.720085 11.618123 5.817172 11.456310 5.881897 c 39 | 11.294498 5.946622 11.140777 5.978985 10.995146 5.978985 c 40 | 10.811758 5.978985 10.633765 5.927744 10.461165 5.825263 c 41 | 10.288566 5.728175 10.110572 5.587938 9.927184 5.404551 c 42 | 9.352751 4.838208 l 43 | 9.271845 4.751908 9.169364 4.708758 9.045307 4.708758 c 44 | 8.980582 4.708758 8.918554 4.719545 8.859223 4.741120 c 45 | 8.805285 4.762695 8.759439 4.781573 8.721683 4.797754 c 46 | 8.468177 4.932598 8.144552 5.164529 7.750809 5.493547 c 47 | 7.357066 5.827960 6.960626 6.194735 6.561489 6.593871 c 48 | 6.167746 6.993009 5.803668 7.389448 5.469256 7.783192 c 49 | 5.134844 8.176935 4.902913 8.497863 4.773463 8.745975 c 50 | 4.751888 8.789125 4.730313 8.837668 4.708738 8.891605 c 51 | 4.692557 8.950937 4.684466 9.010268 4.684466 9.069599 c 52 | 4.684466 9.188262 4.724919 9.288046 4.805825 9.368952 c 53 | 5.380259 9.959567 l 54 | 5.558252 10.142954 5.698490 10.320948 5.800971 10.493547 c 55 | 5.903452 10.666147 5.954692 10.844141 5.954692 11.027529 c 56 | 5.954692 11.173160 5.919633 11.326881 5.849514 11.488693 c 57 | 5.784790 11.650505 5.685005 11.825802 5.550162 12.014584 c 58 | 3.810680 14.466040 l 59 | 3.670442 14.660214 3.503236 14.800451 3.309062 14.886751 c 60 | 3.114887 14.978445 2.909925 15.024292 2.694175 15.024292 c 61 | 2.138619 15.024292 1.618123 14.789664 1.132686 14.320409 c 62 | 0.738943 13.942846 0.450378 13.524832 0.266990 13.066363 c 63 | 0.088997 12.613289 0.000000 12.127851 0.000000 11.610052 c 64 | 0.000000 10.833353 0.180690 10.016201 0.542071 9.158596 c 65 | 0.903452 8.306385 1.394283 7.451477 2.014563 6.593871 c 66 | 2.634844 5.736266 3.333333 4.919114 4.110033 4.142415 c 67 | h 68 | f 69 | n 70 | Q 71 | 72 | endstream 73 | endobj 74 | 75 | 3 0 obj 76 | 2610 77 | endobj 78 | 79 | 4 0 obj 80 | << /Annots [] 81 | /Type /Page 82 | /MediaBox [ 0.000000 0.000000 36.000000 36.000000 ] 83 | /Resources 1 0 R 84 | /Contents 2 0 R 85 | /Parent 5 0 R 86 | >> 87 | endobj 88 | 89 | 5 0 obj 90 | << /Kids [ 4 0 R ] 91 | /Count 1 92 | /Type /Pages 93 | >> 94 | endobj 95 | 96 | 6 0 obj 97 | << /Pages 5 0 R 98 | /Type /Catalog 99 | >> 100 | endobj 101 | 102 | xref 103 | 0 7 104 | 0000000000 65535 f 105 | 0000000010 00000 n 106 | 0000000034 00000 n 107 | 0000002700 00000 n 108 | 0000002723 00000 n 109 | 0000002896 00000 n 110 | 0000002970 00000 n 111 | trailer 112 | << /ID [ (some) (id) ] 113 | /Root 6 0 R 114 | /Size 7 115 | >> 116 | startxref 117 | 3029 118 | %%EOF -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/shop_NA.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "shop_NA.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/shop_NA.imageset/shop_NA.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exyte/PopupView/676d4609495f144b48a583af40b936eedfb49f3e/PopupExample/Assets.xcassets/shop_NA.imageset/shop_NA.png -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/shop_coffee.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "shop_coffee.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/shop_coffee.imageset/shop_coffee.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exyte/PopupView/676d4609495f144b48a583af40b936eedfb49f3e/PopupExample/Assets.xcassets/shop_coffee.imageset/shop_coffee.png -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/transaction_coffee.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "transaction_coffee.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/transaction_coffee.imageset/transaction_coffee.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exyte/PopupView/676d4609495f144b48a583af40b936eedfb49f3e/PopupExample/Assets.xcassets/transaction_coffee.imageset/transaction_coffee.png -------------------------------------------------------------------------------- /PopupExample/Assets.xcassets/winner.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "winner.pdf", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /PopupExample/Font/NunitoSans_7pt-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exyte/PopupView/676d4609495f144b48a583af40b936eedfb49f3e/PopupExample/Font/NunitoSans_7pt-Bold.ttf -------------------------------------------------------------------------------- /PopupExample/Font/NunitoSans_7pt-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exyte/PopupView/676d4609495f144b48a583af40b936eedfb49f3e/PopupExample/Font/NunitoSans_7pt-Light.ttf -------------------------------------------------------------------------------- /PopupExample/Font/NunitoSans_7pt-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exyte/PopupView/676d4609495f144b48a583af40b936eedfb49f3e/PopupExample/Font/NunitoSans_7pt-Medium.ttf -------------------------------------------------------------------------------- /PopupExample/Font/NunitoSans_7pt-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exyte/PopupView/676d4609495f144b48a583af40b936eedfb49f3e/PopupExample/Font/NunitoSans_7pt-Regular.ttf -------------------------------------------------------------------------------- /PopupExample/PopupExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /PopupExample/PopupExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /PopupExample/PopupExample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "pins" : [ 3 | { 4 | "identity" : "swiftui-introspect", 5 | "kind" : "remoteSourceControl", 6 | "location" : "https://github.com/siteline/swiftui-introspect", 7 | "state" : { 8 | "revision" : "807f73ce09a9b9723f12385e592b4e0aaebd3336", 9 | "version" : "1.3.0" 10 | } 11 | } 12 | ], 13 | "version" : 2 14 | } 15 | -------------------------------------------------------------------------------- /PopupExample/PopupExample.xcodeproj/xcshareddata/xcschemes/PopupExample.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 45 | 46 | 56 | 58 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /PopupExample/PopupExample/Components/Images.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageViews.swift 3 | // Example 4 | // 5 | // Created by Alex.M on 19.05.2022. 6 | // 7 | 8 | import SwiftUI 9 | 10 | private struct AlertImageView : View where Content : View { 11 | let hex: String 12 | let alignment: Alignment 13 | 14 | @ViewBuilder let content: (Color) -> Content 15 | 16 | var body: some View { 17 | let color = Color(hex: hex) 18 | return ZStack(alignment: alignment) { 19 | Rectangle() 20 | .fill(color) 21 | .opacity(0.24) 22 | .frame(width: 40, height: 40) 23 | content(color) 24 | } 25 | .cornerRadius(10) 26 | } 27 | } 28 | 29 | struct SmallFloatsImage: View { 30 | let alignment: Alignment 31 | 32 | var body: some View { 33 | AlertImageView(hex: "9265F8", alignment: alignment) { color in 34 | Rectangle() 35 | .fill(color) 36 | .frame(width: 24, height: 4) 37 | .cornerRadius(12) 38 | .padding(.all, 8) 39 | } 40 | } 41 | } 42 | 43 | struct BigFloatsImage: View { 44 | let alignment: Alignment 45 | 46 | var body: some View { 47 | AlertImageView(hex: "9265F8", alignment: alignment) { color in 48 | Rectangle() 49 | .fill(color) 50 | .frame(width: 12, height: 4) 51 | .cornerRadius(12) 52 | .padding(.all, 8) 53 | } 54 | } 55 | } 56 | 57 | struct ToastImage: View { 58 | let position: Position 59 | 60 | var body: some View { 61 | AlertImageView(hex: "87B9FF", alignment: position.toAligment()) { color in 62 | RoundedRectangle(cornerRadius: 4) 63 | .fill(color) 64 | .frame(width: 40, height: 24) 65 | .padding( 66 | EdgeInsets( 67 | top: position == .top ? -16 : 0, 68 | leading: 0, 69 | bottom: position == .bottom ? -16 : 0, 70 | trailing: 0 71 | ) 72 | ) 73 | } 74 | } 75 | 76 | enum Position { 77 | case top, bottom 78 | 79 | func toAligment() -> Alignment { 80 | switch self { 81 | case .top: 82 | return .top 83 | case .bottom: 84 | return .bottom 85 | } 86 | } 87 | } 88 | } 89 | 90 | struct PopupImage: View { 91 | let style: Style 92 | 93 | var body: some View { 94 | AlertImageView(hex: "CCE7A2", alignment: style.toAligment()) { color in 95 | switch style { 96 | case .default: 97 | RoundedRectangle(cornerRadius: 4) 98 | .fill(color) 99 | .frame(width: 24, height: 20) 100 | 101 | case .bottomFirst: 102 | RoundedRectangle(cornerRadius: 4) 103 | .fill(color) 104 | .frame(width: 24, height: 20) 105 | .padding(.bottom, 4) 106 | 107 | case .bottomSecond: 108 | RoundedRectangle(cornerRadius: 4) 109 | .fill(color) 110 | .frame(width: 40, height: 20) 111 | .padding(0) 112 | } 113 | } 114 | } 115 | 116 | enum Style { 117 | case `default` 118 | case bottomFirst 119 | case bottomSecond 120 | 121 | func toAligment() -> Alignment { 122 | switch self { 123 | case .default: 124 | return .center 125 | case .bottomFirst, .bottomSecond: 126 | return .bottom 127 | } 128 | } 129 | } 130 | } 131 | 132 | struct ActionSheetImage: View { 133 | var body: some View { 134 | AlertImageView(hex: "FFB93D", alignment: .bottom) { color in 135 | RoundedRectangle(cornerRadius: 4) 136 | .fill(color) 137 | .frame(width: 40, height: 20) 138 | .padding(0) 139 | } 140 | } 141 | } 142 | 143 | struct InputSheetImage: View { 144 | var body: some View { 145 | AlertImageView(hex: "B03A31", alignment: .bottom) { color in 146 | RoundedRectangle(cornerRadius: 4) 147 | .fill(color) 148 | .frame(width: 24, height: 20) 149 | .padding(.bottom, 4) 150 | } 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /PopupExample/PopupExample/Components/PopupButton.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PopupButton.swift 3 | // Example 4 | // 5 | // Created by Alex.M on 19.05.2022. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct PopupButton : View where Content : View { 11 | @Binding var isShowing: Bool 12 | 13 | var hideAll: () -> () 14 | 15 | @ViewBuilder let content: () -> Content 16 | 17 | var body: some View { 18 | Button { 19 | hideAll() 20 | isShowing.toggle() 21 | } label: { 22 | content() 23 | } 24 | .customButtonStyle(foreground: .black, background: .clear) 25 | } 26 | } 27 | 28 | struct ItemPopupButton : View where Content : View { 29 | @Binding var item: SomeItem? 30 | 31 | let text = "In two weeks, you did 12 workouts and burned 2671 calories. That's 566 calories more than last month. Continue at the same pace and the result will please you." 32 | 33 | var hideAll: () -> () 34 | 35 | @ViewBuilder let content: () -> Content 36 | 37 | var body: some View { 38 | Button { 39 | hideAll() 40 | item = SomeItem(value: text) 41 | } label: { 42 | content() 43 | } 44 | .customButtonStyle(foreground: .black, background: .clear) 45 | } 46 | } 47 | 48 | class SomeItem: Equatable { 49 | 50 | let value: String 51 | 52 | init(value: String) { 53 | self.value = value 54 | } 55 | 56 | static func == (lhs: SomeItem, rhs: SomeItem) -> Bool { 57 | lhs.value == rhs.value 58 | } 59 | } 60 | 61 | -------------------------------------------------------------------------------- /PopupExample/PopupExample/ContentView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentView.swift 3 | // Example 4 | // 5 | // Created by Alisa Mylnikova on 23/04/2020. 6 | // Copyright © 2020 Exyte. All rights reserved. 7 | // 8 | 9 | import SwiftUI 10 | import PopupView 11 | 12 | struct ContentView : View { 13 | @State var floatsSmall = FloatsStateSmall() 14 | @State var floatsBig = FloatsStateBig() 15 | @State var toasts = ToastsState() 16 | @State var popups = PopupsState() 17 | @State var actionSheets = ActionSheetsState() 18 | @State var inputSheets = InputSheetsState() 19 | 20 | var body: some View { 21 | createPopupsList() 22 | 23 | // MARK: - Designed floats big screen 24 | 25 | .popup(isPresented: $floatsBig.showingTopLeading) { 26 | FloatTopLeading() 27 | } customize: { 28 | $0 29 | .type(.floater()) 30 | .position(.topLeading) 31 | .animation(.spring()) 32 | } 33 | 34 | .popup(isPresented: $floatsBig.showingTop) { 35 | FloatTop() 36 | } customize: { 37 | $0 38 | .type(.floater()) 39 | .position(.top) 40 | .animation(.spring()) 41 | } 42 | 43 | .popup(isPresented: $floatsBig.showingTopTrailing) { 44 | FloatTopTrailing() 45 | } customize: { 46 | $0 47 | .type(.floater()) 48 | .position(.topTrailing) 49 | .animation(.spring()) 50 | } 51 | 52 | .popup(isPresented: $floatsBig.showingLeading) { 53 | FloatLeading() 54 | } customize: { 55 | $0 56 | .type(.floater()) 57 | .position(.leading) 58 | .animation(.spring()) 59 | } 60 | 61 | .popup(isPresented: $floatsBig.showingTrailing) { 62 | FloatTrailing() 63 | } customize: { 64 | $0 65 | .type(.floater()) 66 | .position(.trailing) 67 | .animation(.spring()) 68 | } 69 | 70 | .popup(isPresented: $floatsBig.showingBottomLeading) { 71 | FloatBottomLeading() 72 | } customize: { 73 | $0 74 | .type(.floater()) 75 | .position(.bottomLeading) 76 | .appearFrom(.bottomSlide) 77 | .animation(.spring()) 78 | } 79 | 80 | .popup(isPresented: $floatsBig.showingBottom) { 81 | FloatBottom() 82 | } customize: { 83 | $0 84 | .type(.floater()) 85 | .position(.bottom) 86 | .animation(.spring()) 87 | } 88 | 89 | .popup(isPresented: $floatsBig.showingBottomTrailing) { 90 | FloatBottomTrailing() 91 | } customize: { 92 | $0 93 | .type(.floater()) 94 | .position(.bottomTrailing) 95 | .animation(.spring()) 96 | } 97 | 98 | // MARK: - Designed floats small screen 99 | 100 | .popup(isPresented: $floatsSmall.showingTopFirst) { 101 | FloatTopFirst() 102 | } customize: { 103 | $0 104 | .type(.floater()) 105 | .position(.top) 106 | .displayMode(.window) 107 | .animation(.spring()) 108 | .closeOnTapOutside(true) 109 | .autohideIn(3) 110 | .dismissCallback { 111 | print("did", $0) 112 | } 113 | .willDismissCallback { 114 | print("will", $0) 115 | } 116 | } 117 | 118 | .popup(isPresented: $floatsSmall.showingTopSecond) { 119 | FloatTopSecond() 120 | } customize: { 121 | $0 122 | .type(.floater()) 123 | .position(.top) 124 | .animation(.spring()) 125 | } 126 | 127 | .popup(isPresented: $floatsSmall.showingBottomFirst) { 128 | FloatBottomFirst() 129 | } customize: { 130 | $0 131 | .type(.floater()) 132 | .position(.bottom) 133 | .animation(.spring()) 134 | } 135 | 136 | .popup(isPresented: $floatsSmall.showingBottomSecond) { 137 | FloatBottomSecond() 138 | } customize: { 139 | $0 140 | .type(.floater()) 141 | .position(.bottom) 142 | .animation(.spring()) 143 | } 144 | 145 | // MARK: - Designed toasts 146 | 147 | .popup(isPresented: $toasts.showingTopFirst) { 148 | ToastTopFirst() 149 | } customize: { 150 | $0 151 | .type(.toast) 152 | .position(.top) 153 | } 154 | 155 | .popup(isPresented: $toasts.showingTopSecond) { 156 | ToastTopSecond() 157 | } customize: { 158 | $0 159 | .type(.toast) 160 | .position(.top) 161 | } 162 | 163 | .popup(isPresented: $toasts.showingBottomFirst) { 164 | ToastBottomFirst(isShowing: $toasts.showingBottomFirst) 165 | } customize: { 166 | $0 167 | .type(.toast) 168 | .position(.bottom) 169 | .closeOnTap(false) 170 | .backgroundColor(.black.opacity(0.4)) 171 | } 172 | 173 | .popup(isPresented: $toasts.showingBottomSecond) { 174 | ToastBottomSecond() 175 | } customize: { 176 | $0 177 | .type(.toast) 178 | .position(.bottom) 179 | .autohideIn(10) 180 | } 181 | 182 | // MARK: - Designed popups 183 | 184 | .popup(item: $popups.middleItem) { item in 185 | PopupMiddle(item: item) 186 | } customize: { 187 | $0 188 | .appearFrom(.centerScale) 189 | .closeOnTap(false) 190 | .backgroundColor(.black.opacity(0.4)) 191 | } 192 | 193 | .popup(isPresented: $popups.showingBottomFirst) { 194 | PopupBottomFirst(isPresented: $popups.showingBottomFirst) 195 | } customize: { 196 | $0 197 | .type(.floater()) 198 | .disappearTo(.centerScale) 199 | .position(.bottom) 200 | .closeOnTap(false) 201 | .backgroundColor(.black.opacity(0.4)) 202 | } 203 | 204 | .popup(isPresented: $popups.showingBottomSecond) { 205 | PopupBottomSecond() 206 | } customize: { 207 | $0 208 | .type(.floater(verticalPadding: 0, useSafeAreaInset: false)) 209 | .position(.bottom) 210 | .closeOnTap(true) 211 | .closeOnTapOutside(true) 212 | .backgroundColor(.black.opacity(0.4)) 213 | } 214 | 215 | #if os(iOS) 216 | // MARK: - Designed action sheets 217 | .popup(isPresented: $actionSheets.showingFirst) { 218 | ActionSheetFirst() 219 | } customize: { 220 | $0 221 | .type(.toast) 222 | .position(.bottom) 223 | .closeOnTap(false) 224 | .backgroundColor(.black.opacity(0.4)) 225 | } 226 | .popup(isPresented: $actionSheets.showingSecond) { 227 | ActionSheetSecond() 228 | } customize: { 229 | $0 230 | .type(.scroll(headerView: AnyView(scrollViewHeader()))) 231 | .position(.bottom) 232 | .closeOnTap(false) 233 | .closeOnTapOutside(true) 234 | .backgroundColor(.black.opacity(0.4)) 235 | } 236 | 237 | #endif 238 | 239 | .popup(isPresented: $inputSheets.showingFirst) { 240 | InputSheetBottom(isShowing: $inputSheets.showingFirst) 241 | } customize: { 242 | $0 243 | .position(.bottom) 244 | .closeOnTap(false) 245 | .backgroundColor(.black.opacity(0.4)) 246 | .useKeyboardSafeArea(true) 247 | } 248 | } 249 | 250 | func createPopupsList() -> PopupsList { 251 | PopupsList( 252 | floatsBig: $floatsBig, 253 | floatsSmall: $floatsSmall, 254 | toasts: $toasts, 255 | popups: $popups, 256 | actionSheets: $actionSheets, 257 | inputSheets: $inputSheets, 258 | hideAll: { 259 | floatsBig = FloatsStateBig() 260 | floatsSmall = FloatsStateSmall() 261 | toasts = ToastsState() 262 | popups = PopupsState() 263 | actionSheets = ActionSheetsState() 264 | inputSheets = InputSheetsState() 265 | } 266 | ) 267 | } 268 | 269 | #if os(iOS) 270 | func scrollViewHeader() -> some View { 271 | ZStack { 272 | Color(.white).cornerRadius(40, corners: [.topLeft, .topRight]) 273 | 274 | Color.black 275 | .opacity(0.2) 276 | .frame(width: 30, height: 6) 277 | .clipShape(Capsule()) 278 | .padding(.vertical, 20) 279 | } 280 | } 281 | #endif 282 | } 283 | 284 | struct ContentView_Previews: PreviewProvider { 285 | static var previews: some View { 286 | ContentView() 287 | } 288 | } 289 | -------------------------------------------------------------------------------- /PopupExample/PopupExample/Examples/ActionSheets.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ActionSheets.swift 3 | // Example 4 | // 5 | // Created by Alex.M on 20.05.2022. 6 | // 7 | 8 | import SwiftUI 9 | 10 | #if os(iOS) 11 | struct ActionSheetView: View { 12 | 13 | let content: Content 14 | let topPadding: CGFloat 15 | let fixedHeight: Bool 16 | let bgColor: Color 17 | 18 | init(topPadding: CGFloat = 100, fixedHeight: Bool = false, bgColor: Color = .white, @ViewBuilder content: () -> Content) { 19 | self.content = content() 20 | self.topPadding = topPadding 21 | self.fixedHeight = fixedHeight 22 | self.bgColor = bgColor 23 | } 24 | 25 | var body: some View { 26 | ZStack { 27 | bgColor.cornerRadius(40, corners: [.topLeft, .topRight]) 28 | VStack { 29 | Color.black 30 | .opacity(0.2) 31 | .frame(width: 30, height: 6) 32 | .clipShape(Capsule()) 33 | .padding(.top, 15) 34 | .padding(.bottom, 10) 35 | 36 | content 37 | .padding(.bottom, 30) 38 | .applyIf(fixedHeight) { 39 | $0.frame(height: UIScreen.main.bounds.height - topPadding) 40 | } 41 | .applyIf(!fixedHeight) { 42 | $0.frame(maxHeight: UIScreen.main.bounds.height - topPadding) 43 | } 44 | } 45 | } 46 | .fixedSize(horizontal: false, vertical: true) 47 | } 48 | } 49 | 50 | private struct ActivityView: View { 51 | let emoji: String 52 | let name: String 53 | let isSelected: Bool 54 | 55 | var body: some View { 56 | HStack(spacing: 12) { 57 | Text(emoji) 58 | .font(.system(size: 24)) 59 | 60 | Text(name.uppercased()) 61 | .font(.system(size: 13, weight: isSelected ? .regular : .light)) 62 | 63 | Spacer() 64 | 65 | if isSelected { 66 | Image(systemName: "checkmark") 67 | .foregroundColor(Color(hex: "9265F8")) 68 | } 69 | } 70 | .opacity(isSelected ? 1.0 : 0.8) 71 | } 72 | } 73 | 74 | struct ActionSheetFirst: View { 75 | var body: some View { 76 | ActionSheetView(bgColor: .white) { 77 | ScrollView { 78 | VStack(alignment: .leading, spacing: 24) { 79 | ActivityView(emoji: "🤼‍♂️", name: "Sparring", isSelected: true) 80 | ActivityView(emoji: "🧘", name: "Yoga", isSelected: false) 81 | ActivityView(emoji: "🚴", name: "cycling", isSelected: false) 82 | ActivityView(emoji: "🏊", name: "Swimming", isSelected: false) 83 | ActivityView(emoji: "🏄", name: "Surfing", isSelected: false) 84 | ActivityView(emoji: "🤸", name: "Fitness", isSelected: false) 85 | ActivityView(emoji: "⛹️", name: "Basketball", isSelected: true) 86 | ActivityView(emoji: "🏋️", name: "Lifting Weights", isSelected: false) 87 | ActivityView(emoji: "⚽️", name: "Football", isSelected: false) 88 | } 89 | .padding(.horizontal, 20) 90 | } 91 | } 92 | } 93 | } 94 | 95 | struct ActionSheetSecond: View { 96 | 97 | var body: some View { 98 | VStack(alignment: .leading, spacing: 16) { 99 | Text("Privacy Policy") 100 | .font(.system(size: 24)) 101 | 102 | Text(Constants.privacyPolicy) 103 | .font(.system(size: 14)) 104 | .opacity(0.6) 105 | } 106 | .padding(.horizontal, 24) 107 | .padding(.vertical, 16) 108 | .background(.white) 109 | } 110 | } 111 | 112 | struct ActionSheets_Previews: PreviewProvider { 113 | static var previews: some View { 114 | ZStack { 115 | Rectangle() 116 | .ignoresSafeArea() 117 | ActionSheetFirst() 118 | } 119 | 120 | ZStack { 121 | Rectangle() 122 | .ignoresSafeArea() 123 | ActionSheetSecond() 124 | } 125 | } 126 | } 127 | 128 | #endif 129 | -------------------------------------------------------------------------------- /PopupExample/PopupExample/Examples/FloatsBig.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FloatsBig.swift 3 | // PopupExample 4 | // 5 | // Created by Alisa Mylnikova on 09.06.2023. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct FloatTopLeading: View { 11 | 12 | var body: some View { 13 | ZStack { 14 | RoundedRectangle(cornerRadius: 12) 15 | .fill(Color("dark")) 16 | 17 | HStack(alignment: .top, spacing: 16) { 18 | Picture("avatar6", 48) 19 | 20 | VStack(alignment: .leading, spacing: 2) { 21 | WhiteBoldText("You are on Do Not Distrub") 22 | WhiteText("Do you want to update your status?", 13) 23 | .opacity(0.6) 24 | 25 | HStack(spacing: 18) { 26 | WhiteBoldText("Turn on") 27 | .padding(16, 5) 28 | .background(RoundedRectangle(cornerRadius: 8) 29 | .foregroundColor(Color("darkBlue"))) 30 | WhiteText("Stay off", 16) 31 | .opacity(0.6) 32 | } 33 | .padding(.top, 14) 34 | } 35 | 36 | Spacer() 37 | 38 | Picture("cross", 10) 39 | } 40 | .padding(16) 41 | } 42 | .fixedSize() 43 | } 44 | } 45 | 46 | struct FloatTop: View { 47 | 48 | var body: some View { 49 | ZStack { 50 | RoundedRectangle(cornerRadius: 12) 51 | .fill(Color("semidark")) 52 | 53 | HStack(spacing: 16) { 54 | Picture("avatar5", 48) 55 | 56 | VStack(alignment: .leading, spacing: 2) { 57 | WhiteMediumText("Kate Middleton", 17) 58 | WhiteText("incoming call", 13) 59 | .opacity(0.6) 60 | } 61 | .padding(.trailing, 60) 62 | 63 | HStack(spacing: 14) { 64 | Picture("phone_call", 36) 65 | Picture("phone_call2", 36) 66 | } 67 | } 68 | .padding(14) 69 | } 70 | .fixedSize() 71 | } 72 | } 73 | 74 | struct FloatTopTrailing: View { 75 | 76 | var body: some View { 77 | ZStack { 78 | RoundedRectangle(cornerRadius: 10) 79 | .fill(Color("light")) 80 | 81 | HStack(spacing: 7) { 82 | Circle() 83 | .foregroundColor(Color("darkBlue")) 84 | .frame(width: 10, height: 10) 85 | 86 | Picture("avatar4", 45) 87 | 88 | VStack(alignment: .leading, spacing: 2) { 89 | HStack { 90 | BlackBoldText("@edsheeran", 16) 91 | BlackText ("followed you", 16) 92 | .opacity(0.4) 93 | .padding(.trailing, 15) 94 | DarkText("8:02 am", 13) 95 | .opacity(0.6) 96 | } 97 | .padding(.trailing, 8) 98 | .fixedSize() 99 | DarkText("I know it's a bad idea. But how can I help\nmyself?", 13) 100 | .opacity(0.6) 101 | } 102 | } 103 | .padding(8) 104 | } 105 | .fixedSize() 106 | } 107 | } 108 | 109 | struct FloatLeading: View { 110 | 111 | var body: some View { 112 | ZStack { 113 | RoundedRectangle(cornerRadius: 12) 114 | .fill(Color("purple")) 115 | .overlay(alignment: .topTrailing) { 116 | Picture("cross", 10) 117 | .padding(13) 118 | } 119 | 120 | VStack(alignment: .leading, spacing: 8) { 121 | Picture("gift2", 33) 122 | .padding(.bottom, 20) 123 | 124 | WhiteBoldText("We give you a gift!") 125 | WhiteText("30% discount until the end of the month on all products of the company.", 13) 126 | .opacity(0.8) 127 | } 128 | .padding(16) 129 | } 130 | .frame(width: 195) 131 | .fixedSize() 132 | } 133 | } 134 | 135 | struct FloatTrailing: View { 136 | 137 | var body: some View { 138 | ZStack { 139 | RoundedRectangle(cornerRadius: 12) 140 | .fill(Color("semilight")) 141 | 142 | HStack { 143 | Picture("check_circle", 27) 144 | BlackText("New file created with success", 15) 145 | } 146 | .padding(20, 15) 147 | } 148 | .fixedSize() 149 | } 150 | } 151 | 152 | struct FloatBottomLeading: View { 153 | 154 | var body: some View { 155 | HStack(spacing: 8) { 156 | Image(systemName: "wifi.slash") 157 | .foregroundColor(.white) 158 | .frame(width: 21, height: 21) 159 | WhiteText("Check your network connection and try again", 15) 160 | } 161 | .padding(32, 22) 162 | .background(Color("yellow").cornerRadius(12)) 163 | } 164 | } 165 | 166 | struct FloatBottom: View { 167 | 168 | var body: some View { 169 | ZStack { 170 | Capsule() 171 | .fill(.black) 172 | 173 | HStack(spacing: 25) { 174 | Picture("file", 20) 175 | VStack(spacing: 2) { 176 | WhiteBoldText("Ooops! Download failed") 177 | WhiteText("The download was unable to complete. Please try again later.", 16) 178 | .opacity(0.6) 179 | } 180 | Text("Retry") 181 | .font(.custom(boldFont, size: 21)) 182 | .foregroundColor(Color("lightBlue")) 183 | } 184 | .padding(26, 20) 185 | } 186 | .fixedSize() 187 | } 188 | } 189 | 190 | struct FloatBottomTrailing: View { 191 | 192 | var body: some View { 193 | ZStack { 194 | RoundedRectangle(cornerRadius: 10) 195 | .fill(Color.white) 196 | 197 | HStack(spacing: 0) { 198 | BlackText("Your message has been sent to Alex Brant", 15) 199 | .padding(20) 200 | Picture("avatar2", 32) 201 | .padding(.trailing, 7) 202 | } 203 | } 204 | .shadowedStyle() 205 | .fixedSize() 206 | } 207 | } 208 | 209 | struct Picture: View { 210 | let name: String 211 | let size: CGFloat 212 | 213 | init(_ name: String, _ size: CGFloat) { 214 | self.name = name 215 | self.size = size 216 | } 217 | 218 | var body: some View { 219 | Image(name) 220 | .aspectRatio(1.0, contentMode: .fit) 221 | .frame(width: size) 222 | } 223 | } 224 | 225 | let boldFont = "NunitoSans7pt-Bold" 226 | let mediumFont = "NunitoSans7pt-Medium" 227 | let lightFont = "NunitoSans7pt-Light" 228 | let regularFont = "NunitoSans7pt-Regular" 229 | 230 | struct WhiteBoldText: View { 231 | let text: String 232 | let size: CGFloat 233 | 234 | init(_ text: String, _ size: CGFloat = 16) { 235 | self.text = text 236 | self.size = size 237 | } 238 | 239 | var body: some View { 240 | Text(text) 241 | .font(.custom(boldFont, size: size)) 242 | .foregroundColor(.white) 243 | .bold() 244 | } 245 | } 246 | 247 | struct WhiteMediumText: View { 248 | let text: String 249 | let size: CGFloat 250 | 251 | init(_ text: String, _ size: CGFloat) { 252 | self.text = text 253 | self.size = size 254 | } 255 | 256 | var body: some View { 257 | Text(text) 258 | .font(.custom(mediumFont, size: size)) 259 | .foregroundColor(.white) 260 | } 261 | } 262 | 263 | struct WhiteText: View { 264 | let text: String 265 | let size: CGFloat 266 | 267 | init(_ text: String, _ size: CGFloat) { 268 | self.text = text 269 | self.size = size 270 | } 271 | 272 | var body: some View { 273 | Text(text) 274 | .font(.custom(regularFont, size: size)) 275 | .foregroundColor(.white) 276 | } 277 | } 278 | 279 | struct BlackBoldText: View { 280 | let text: String 281 | let size: CGFloat 282 | 283 | init(_ text: String, _ size: CGFloat) { 284 | self.text = text 285 | self.size = size 286 | } 287 | 288 | var body: some View { 289 | Text(text) 290 | .font(.custom(boldFont, size: size)) 291 | .foregroundColor(.black) 292 | .bold() 293 | } 294 | } 295 | 296 | struct BlackMediumText: View { 297 | let text: String 298 | let size: CGFloat 299 | 300 | init(_ text: String, _ size: CGFloat) { 301 | self.text = text 302 | self.size = size 303 | } 304 | 305 | var body: some View { 306 | Text(text) 307 | .font(.custom(mediumFont, size: size)) 308 | .foregroundColor(.white) 309 | } 310 | } 311 | 312 | struct BlackText: View { 313 | let text: String 314 | let size: CGFloat 315 | 316 | init(_ text: String, _ size: CGFloat) { 317 | self.text = text 318 | self.size = size 319 | } 320 | 321 | var body: some View { 322 | Text(text) 323 | .font(.custom(regularFont, size: size)) 324 | .foregroundColor(.black) 325 | } 326 | } 327 | 328 | struct DarkText: View { 329 | let text: String 330 | let size: CGFloat 331 | 332 | init(_ text: String, _ size: CGFloat) { 333 | self.text = text 334 | self.size = size 335 | } 336 | 337 | var body: some View { 338 | Text(text) 339 | .font(.custom(regularFont, size: size)) 340 | .foregroundColor(Color("darkText")) 341 | } 342 | } 343 | 344 | extension View { 345 | func padding(_ horizontal: CGFloat, _ vertical: CGFloat) -> some View { 346 | self.padding(.horizontal, horizontal) 347 | .padding(.vertical, vertical) 348 | } 349 | } 350 | -------------------------------------------------------------------------------- /PopupExample/PopupExample/Examples/FloatsSmall.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Floats.swift 3 | // Example 4 | // 5 | // Created by Alex.M on 19.05.2022. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct FloatTopFirst: View { 11 | 12 | @Environment(\.popupDismiss) var dismiss 13 | 14 | var body: some View { 15 | ZStack { 16 | RoundedRectangle(cornerRadius: 12) 17 | .fill(Color(hex: "E0E3E8")) 18 | 19 | HStack(spacing: 0) { 20 | Image("avatar1") 21 | .aspectRatio(1.0, contentMode: .fit) 22 | .cornerRadius(16) 23 | .padding(16.0) 24 | 25 | VStack(alignment: .leading, spacing: 8) { 26 | Group { 27 | Text("Adam Jameson") 28 | .bold() 29 | .foregroundColor(.black) + 30 | Text(" invites you to join his training") 31 | .foregroundColor(.black.opacity(0.6)) 32 | } 33 | 34 | Button { 35 | debugPrint("Accepted!") 36 | dismiss?() 37 | } label: { 38 | Text("Accept".uppercased()) 39 | .font(.system(size: 14, weight: .black)) 40 | } 41 | .customButtonStyle(foreground: Color(hex: "9265F8"), background: .clear) 42 | } 43 | 44 | Spacer() 45 | } 46 | } 47 | .frame(height: 98) 48 | .padding(.horizontal, 16) 49 | } 50 | } 51 | 52 | struct FloatTopSecond: View { 53 | var body: some View { 54 | HStack(spacing: 0) { 55 | VStack(alignment: .leading, spacing: 2) { 56 | Text("We give you a gift!") 57 | .foregroundColor(.white) 58 | .font(.system(size: 18)) 59 | 60 | Text("30% discount until the end of the month on all products of the company.") 61 | .foregroundColor(.white) 62 | .font(.system(size: 16)) 63 | .opacity(0.8) 64 | } 65 | 66 | Spacer() 67 | 68 | Image("gift") 69 | .aspectRatio(1.0, contentMode: .fit) 70 | } 71 | .padding(16) 72 | .background(Color(hex: "9265F8").cornerRadius(12)) 73 | .shadow(color: Color(hex: "9265F8").opacity(0.5), radius: 40, x: 0, y: 12) 74 | .padding(.horizontal, 16) 75 | } 76 | } 77 | 78 | struct FloatBottomFirst: View { 79 | var body: some View { 80 | HStack(spacing: 0) { 81 | Text("Your message has been sent to Alex Brant") 82 | .font(.system(size: 15)) 83 | .foregroundColor(.black) 84 | Spacer() 85 | Image("avatar2") 86 | .frame(width: 32, height: 32) 87 | .cornerRadius(16) 88 | } 89 | .padding(16) 90 | .background(Color.white.cornerRadius(12)) 91 | .shadowedStyle() 92 | .padding(.horizontal, 16) 93 | } 94 | } 95 | 96 | struct FloatBottomSecond: View { 97 | var body: some View { 98 | HStack(spacing: 8) { 99 | Image(systemName: "wifi.slash") 100 | .foregroundColor(.white) 101 | .frame(width: 24, height: 24) 102 | 103 | Text("Check your network connection and try again") 104 | .foregroundColor(.white) 105 | .font(.system(size: 16)) 106 | } 107 | .padding(16) 108 | .background(Color(hex: "FFB93D").cornerRadius(12)) 109 | .padding(.horizontal, 16) 110 | } 111 | } 112 | 113 | struct Floats_Previews: PreviewProvider { 114 | static var previews: some View { 115 | ZStack { 116 | Rectangle() 117 | .ignoresSafeArea() 118 | VStack { 119 | FloatTopFirst() 120 | FloatTopSecond() 121 | FloatBottomFirst() 122 | FloatBottomSecond() 123 | } 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /PopupExample/PopupExample/Examples/InputSheets.swift: -------------------------------------------------------------------------------- 1 | // 2 | // InputSheets.swift 3 | // PopupExample 4 | // 5 | // Created by Alex.M on 28.09.2023. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct InputSheetBottom: View { 11 | @Binding var isShowing: Bool 12 | 13 | @State var nickname: String = "" 14 | 15 | var body: some View { 16 | VStack(spacing: 0) { 17 | Text("Nickname") 18 | .foregroundColor(.black) 19 | .font(.system(size: 20, weight: .bold)) 20 | .kerning(0.38) 21 | 22 | Text("Usernames can only use letters, numbers, ., - and _ ") 23 | .font(.system(size: 14)) 24 | .foregroundColor(Color(red: 1, green: 0.23, blue: 0.19)) 25 | .frame(maxWidth: .infinity, alignment: .leading) 26 | .padding(12) 27 | .background { 28 | RoundedRectangle(cornerRadius: 10) 29 | .fill(Color(red: 0.98, green: 0.92, blue: 0.92)) 30 | } 31 | .padding(.top, 16) 32 | 33 | TextField("Nickname", text: $nickname) 34 | .padding() 35 | .frame(height: 44) 36 | .background { 37 | RoundedRectangle(cornerRadius: 12) 38 | .stroke(Color(red: 1, green: 0.23, blue: 0.19), lineWidth: 0.5) 39 | } 40 | .padding(.top, 6) 41 | 42 | Button { 43 | isShowing = false 44 | } label: { 45 | Text("Save changes") 46 | .buttonStyle(.plain) 47 | .font(.system(size: 17)) 48 | .frame(maxWidth: .infinity) 49 | .frame(height: 44) 50 | .background { 51 | RoundedRectangle(cornerRadius: 12) 52 | .fill(Color(red: 0.29, green: 0.38, blue: 1)) 53 | } 54 | } 55 | .buttonStyle(.plain) 56 | .foregroundColor(.white) 57 | .padding(.top, 12) 58 | } 59 | .padding(16) 60 | .background(Color.white.cornerRadius(18)) 61 | .shadowedStyle() 62 | .padding(.horizontal, 8) 63 | .padding(.bottom, 30) 64 | } 65 | } 66 | 67 | #Preview { 68 | InputSheetBottom(isShowing: .constant(true)) 69 | } 70 | -------------------------------------------------------------------------------- /PopupExample/PopupExample/Examples/Popups.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Popups.swift 3 | // Example 4 | // 5 | // Created by Alex.M on 20.05.2022. 6 | // 7 | 8 | import SwiftUI 9 | import PopupView 10 | 11 | struct PopupMiddle: View { 12 | 13 | @Environment(\.popupDismiss) var dismiss 14 | let item: SomeItem 15 | 16 | var body: some View { 17 | VStack(spacing: 12) { 18 | Image("winner") 19 | .resizable() 20 | .scaledToFit() 21 | .frame(maxWidth: 226, maxHeight: 226) 22 | 23 | Text("Congratulations!") 24 | .foregroundColor(.black) 25 | .font(.system(size: 24)) 26 | .padding(.top, 12) 27 | 28 | Text(item.value) 29 | .foregroundColor(.black) 30 | .font(.system(size: 16)) 31 | .opacity(0.6) 32 | .multilineTextAlignment(.center) 33 | .padding(.bottom, 20) 34 | 35 | Button { 36 | dismiss?() 37 | } label: { 38 | Text("Thanks") 39 | .font(.system(size: 18, weight: .bold)) 40 | .frame(maxWidth: .infinity) 41 | .padding(.vertical, 18) 42 | .padding(.horizontal, 24) 43 | .foregroundColor(.white) 44 | .background(Color(hex: "9265F8")) 45 | .cornerRadius(12) 46 | } 47 | .buttonStyle(.plain) 48 | } 49 | .padding(EdgeInsets(top: 37, leading: 24, bottom: 40, trailing: 24)) 50 | .background(Color.white.cornerRadius(20)) 51 | .shadowedStyle() 52 | .padding(.horizontal, 40) 53 | } 54 | } 55 | 56 | struct PopupBottomFirst: View { 57 | 58 | @Binding var isPresented: Bool 59 | 60 | var body: some View { 61 | VStack(spacing: 12) { 62 | Text("Policy changes") 63 | .foregroundColor(.black) 64 | .font(.system(size: 24)) 65 | 66 | Text("Policy change occurs through interactions between wide external changes or shocks to the political system and the success of the ideas in the coalitions, which may cause actors in the advocacy coalition to shift coalitions.") 67 | .foregroundColor(.black) 68 | .font(.system(size: 16)) 69 | .opacity(0.6) 70 | .multilineTextAlignment(.center) 71 | .padding(.bottom, 12) 72 | 73 | Button { 74 | isPresented = false 75 | } label: { 76 | Text("Ok, I accept") 77 | .font(.system(size: 18, weight: .bold)) 78 | .frame(maxWidth: .infinity) 79 | .padding(.vertical, 18) 80 | .foregroundColor(.white) 81 | .background(Color(hex: "FFB93D")) 82 | .cornerRadius(12) 83 | } 84 | .buttonStyle(.plain) 85 | } 86 | .padding(EdgeInsets(top: 37, leading: 24, bottom: 40, trailing: 24)) 87 | .background(Color.white.cornerRadius(20)) 88 | .shadowedStyle() 89 | .padding(.horizontal, 16) 90 | } 91 | } 92 | 93 | struct PopupBottomSecond: View { 94 | 95 | var body: some View { 96 | VStack(spacing: 12) { 97 | Image("chest") 98 | .resizable() 99 | .scaledToFit() 100 | .frame(maxWidth: 156, maxHeight: 156) 101 | 102 | Text("Personal offer") 103 | .foregroundColor(.black) 104 | .font(.system(size: 24)) 105 | .padding(.top, 4) 106 | 107 | Text("Say hello to flexible funding – you're pre-screened for an exclusive personal loan offer through TD Bank. Enter your Personal Offer Code to get started.") 108 | .foregroundColor(.black) 109 | .font(.system(size: 16)) 110 | .opacity(0.6) 111 | .multilineTextAlignment(.center) 112 | .padding(.bottom, 12) 113 | 114 | Text("Read More") 115 | .font(.system(size: 18, weight: .bold)) 116 | .frame(maxWidth: .infinity) 117 | .padding(.vertical, 18) 118 | .background( 119 | LinearGradient( 120 | gradient: Gradient(colors: [Color(hex: "3DDCFF"), Color(hex: "CD3DFF")]), 121 | startPoint: .top, 122 | endPoint: .bottom)) 123 | .cornerRadius(12) 124 | .foregroundColor(.white) 125 | .padding(.horizontal, 64) 126 | } 127 | .padding(EdgeInsets(top: 37, leading: 24, bottom: 40, trailing: 24)) 128 | .background(Color.white.cornerRadius(20)) 129 | .shadowedStyle() 130 | } 131 | } 132 | 133 | 134 | struct Popups_Previews: PreviewProvider { 135 | static var previews: some View { 136 | ZStack { 137 | Rectangle() 138 | .ignoresSafeArea() 139 | PopupMiddle(item: SomeItem(value: "item")) 140 | } 141 | 142 | ZStack { 143 | Rectangle() 144 | .ignoresSafeArea() 145 | PopupBottomFirst(isPresented: .constant(false)) 146 | } 147 | 148 | ZStack { 149 | Rectangle() 150 | .ignoresSafeArea() 151 | PopupBottomSecond() 152 | } 153 | } 154 | } 155 | 156 | -------------------------------------------------------------------------------- /PopupExample/PopupExample/Examples/Toasts.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Toasts.swift 3 | // Example 4 | // 5 | // Created by Alex.M on 19.05.2022. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ToastTopFirst: View { 11 | var body: some View { 12 | Text("Unable to add to bag as this item is currently not available.") 13 | .foregroundColor(.white) 14 | .padding(EdgeInsets(top: 60, leading: 32, bottom: 16, trailing: 32)) 15 | .frame(maxWidth: .infinity) 16 | .background(Color(hex: "FE504E")) 17 | } 18 | } 19 | 20 | struct ToastTopSecond: View { 21 | var body: some View { 22 | HStack { 23 | Image("avatar3") 24 | .frame(width: 48, height: 48) 25 | .cornerRadius(24) 26 | 27 | VStack(alignment: .leading) { 28 | HStack { 29 | Text("Camila Morrone") 30 | .font(.system(size: 15)) 31 | 32 | Spacer() 33 | 34 | Text("now") 35 | .font(.system(size: 13)) 36 | .opacity(0.6) 37 | } 38 | 39 | Text("Let's go have a cup of coffee! ☕️") 40 | .font(.system(size: 15, weight: .light)) 41 | } 42 | } 43 | .foregroundColor(.white) 44 | .padding(EdgeInsets(top: 56, leading: 16, bottom: 16, trailing: 16)) 45 | .frame(maxWidth: .infinity) 46 | .background(Color(hex: "87B9FF")) 47 | } 48 | } 49 | 50 | struct ToastBottomFirst: View { 51 | @Binding var isShowing: Bool 52 | 53 | var body: some View { 54 | HStack(alignment: .top, spacing: 16) { 55 | Image("fitness") 56 | .frame(width: 48, height: 48) 57 | .cornerRadius(8) 58 | 59 | VStack(alignment: .leading, spacing: 8) { 60 | Text("Log In to Fitness First") 61 | .font(.system(size: 16, weight: .bold)) 62 | Text("To continue training, you need to Log in or Sign up") 63 | .font(.system(size: 16)) 64 | .opacity(0.8) 65 | } 66 | 67 | Spacer() 68 | 69 | VStack(alignment: .leading, spacing: 4) { 70 | Button { 71 | self.isShowing = false 72 | } label: { 73 | Text("Log in") 74 | .frame(width: 112, height: 40) 75 | } 76 | .customButtonStyle(foreground: .white, background: Color(hex: "87B9FF")) 77 | .cornerRadius(8) 78 | 79 | Button { 80 | self.isShowing = false 81 | } label: { 82 | Text("Sign up") 83 | .frame(width: 112, height: 40) 84 | } 85 | .customButtonStyle() 86 | } 87 | } 88 | .foregroundColor(.black) 89 | .padding(EdgeInsets(top: 24, leading: 16, bottom: 42, trailing: 16)) 90 | .frame(maxWidth: .infinity) 91 | .background(Color.white) 92 | } 93 | } 94 | 95 | struct ToastBottomSecond: View { 96 | var body: some View { 97 | HStack(alignment: .top, spacing: 16) { 98 | Image("checkmark") 99 | .frame(width: 48, height: 48) 100 | .cornerRadius(24) 101 | 102 | VStack(alignment: .leading, spacing: 4) { 103 | Text("Subscription completed!") 104 | .font(.system(size: 16, weight: .bold)) 105 | Text("The next charge to your credit card will be made on May 25, 2022.") 106 | .font(.system(size: 16, weight: .light)) 107 | .opacity(0.8) 108 | } 109 | 110 | Spacer() 111 | } 112 | .foregroundColor(.black) 113 | .padding(EdgeInsets(top: 24, leading: 16, bottom: 42, trailing: 16)) 114 | .frame(maxWidth: .infinity) 115 | .background(Color.white) 116 | .shadow(color: .black.opacity(0.1), radius: 40, x: 0, y: -4) 117 | } 118 | } 119 | 120 | struct Toasts_Previews: PreviewProvider { 121 | static var previews: some View { 122 | ZStack { 123 | Rectangle() 124 | .ignoresSafeArea() 125 | VStack { 126 | ToastTopFirst() 127 | ToastTopSecond() 128 | ToastBottomFirst(isShowing: Binding.init(get: { true }, set: { _ in })) 129 | ToastBottomSecond() 130 | } 131 | } 132 | .previewDevice("iPhone 13 Pro Max") 133 | } 134 | } 135 | 136 | 137 | 138 | -------------------------------------------------------------------------------- /PopupExample/PopupExample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | UIAppFonts 6 | 7 | NunitoSans_7pt-Medium.ttf 8 | NunitoSans_7pt-Light.ttf 9 | NunitoSans_7pt-Regular.ttf 10 | NunitoSans_7pt-Bold.ttf 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /PopupExample/PopupExample/PopupExample.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.files.user-selected.read-only 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /PopupExample/PopupExample/PopupExampleApp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PopupExampleApp.swift 3 | // PopupExample 4 | // 5 | // Created by Alisa Mylnikova on 04.05.2023. 6 | // 7 | 8 | import SwiftUI 9 | import PopupView 10 | 11 | @main 12 | struct PopupExampleApp: App { 13 | var body: some Scene { 14 | WindowGroup { 15 | ContentView() 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /PopupExample/PopupExample/PopupsList.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PopupsList.swift 3 | // Example 4 | // 5 | // Created by Alex.M on 19.05.2022. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct FloatsStateBig { 11 | var showingTopLeading = false 12 | var showingTop = false 13 | var showingTopTrailing = false 14 | 15 | var showingLeading = false 16 | // center is a regular popup 17 | var showingTrailing = false 18 | 19 | var showingBottomLeading = false 20 | var showingBottom = false 21 | var showingBottomTrailing = false 22 | } 23 | 24 | struct FloatsStateSmall { 25 | var showingTopFirst = false 26 | var showingTopSecond = false 27 | var showingBottomFirst = false 28 | var showingBottomSecond = false 29 | } 30 | 31 | struct ToastsState { 32 | var showingTopFirst = false 33 | var showingTopSecond = false 34 | var showingBottomFirst = false 35 | var showingBottomSecond = false 36 | } 37 | 38 | struct PopupsState { 39 | var middleItem: SomeItem? 40 | var showingBottomFirst = false 41 | var showingBottomSecond = false 42 | } 43 | 44 | struct ActionSheetsState { 45 | var showingFirst = false 46 | var showingSecond = false 47 | } 48 | 49 | struct InputSheetsState { 50 | var showingFirst = false 51 | } 52 | 53 | private struct SectionHeader: View { 54 | let name: String 55 | let count: Int 56 | 57 | var body: some View { 58 | HStack { 59 | Text(name) 60 | .font(.system(size: 24, weight: .bold)) 61 | .foregroundColor(.black) 62 | 63 | Spacer() 64 | 65 | HStack(spacing: 4) { 66 | Text("\(count)") 67 | .font(.system(size: 18)) 68 | .foregroundColor(.black) 69 | .opacity(0.8) 70 | 71 | Text("types") 72 | .font(.system(size: 18)) 73 | .foregroundColor(.black) 74 | .opacity(0.5) 75 | } 76 | } 77 | .padding(.horizontal, 20) 78 | } 79 | } 80 | 81 | private struct PopupTypeView : View where Content : View { 82 | let title: String 83 | var detail: String = "" 84 | @ViewBuilder let icon: () -> Content 85 | 86 | var body: some View { 87 | ZStack(alignment: .leading) { 88 | RoundedRectangle(cornerRadius: 12) 89 | .fill(.white) 90 | 91 | HStack { 92 | icon() 93 | VStack(alignment: .leading, spacing: 0) { 94 | Text(title) 95 | .font(.system(size: 18)) 96 | .foregroundColor(.black) 97 | Text(detail) 98 | .font(.system(size: 13)) 99 | .foregroundColor(.black) 100 | .opacity(0.4) 101 | } 102 | } 103 | .padding() 104 | } 105 | .padding(.horizontal, 20) 106 | } 107 | } 108 | 109 | struct PopupsList: View { 110 | @Binding var floatsBig: FloatsStateBig 111 | @Binding var floatsSmall: FloatsStateSmall 112 | @Binding var toasts: ToastsState 113 | @Binding var popups: PopupsState 114 | @Binding var actionSheets: ActionSheetsState 115 | @Binding var inputSheets: InputSheetsState 116 | 117 | let hideAll: () -> () 118 | 119 | var body: some View { 120 | ZStack { 121 | Rectangle() 122 | .fill(Color(hex: "F7F7F9")) 123 | .ignoresSafeArea() 124 | 125 | ScrollView { 126 | VStack(spacing: 12) { 127 | safeSpaceForMac() 128 | 129 | Group { 130 | SectionHeader(name: "Floats", count: 4) 131 | .padding(.bottom, 12) 132 | 133 | floatsSection() 134 | } 135 | 136 | Group { 137 | SectionHeader(name: "Toasts", count: 4) 138 | .padding(EdgeInsets(top: 20, leading: 0, bottom: 12, trailing: 0)) 139 | 140 | toastsSection() 141 | } 142 | 143 | Group { 144 | SectionHeader(name: "Popups", count: 3) 145 | .padding(EdgeInsets(top: 20, leading: 0, bottom: 12, trailing: 0)) 146 | 147 | popupsSection() 148 | } 149 | 150 | #if os(iOS) 151 | Group { 152 | SectionHeader(name: "Action sheets", count: 2) 153 | .padding(EdgeInsets(top: 20, leading: 0, bottom: 12, trailing: 0)) 154 | 155 | actionSheetsSection() 156 | } 157 | #endif 158 | 159 | Group { 160 | SectionHeader(name: "Inputs", count: 1) 161 | .padding(.bottom, 12) 162 | 163 | inputsSection() 164 | } 165 | 166 | safeSpaceForMac() 167 | } 168 | } 169 | .padding(.top, 1) 170 | } 171 | } 172 | 173 | func safeSpaceForMac() -> some View { 174 | #if os(macOS) 175 | Color.clear.padding(.bottom, 40) 176 | #else 177 | EmptyView() 178 | #endif 179 | } 180 | 181 | @MainActor @ViewBuilder 182 | func floatsSection() -> some View { 183 | #if os(iOS) 184 | if UIDevice.current.userInterfaceIdiom == .phone { 185 | floatsSectionSmall() 186 | } else { 187 | floatsSectionBig() 188 | } 189 | #else 190 | floatsSectionBig() 191 | #endif 192 | } 193 | 194 | @ViewBuilder 195 | func floatsSectionSmall() -> some View { 196 | Button("Show multiple popups") { 197 | popups.showingBottomFirst = true 198 | toasts.showingBottomSecond = true 199 | floatsSmall.showingBottomFirst = true 200 | floatsSmall.showingTopSecond = true 201 | toasts.showingTopSecond = true 202 | } 203 | PopupButton(isShowing: $floatsSmall.showingTopFirst, hideAll: hideAll) { 204 | PopupTypeView( 205 | title: "Top version 1", 206 | detail: "Top float with a picture and one button" 207 | ) { 208 | SmallFloatsImage(alignment: .top) 209 | } 210 | } 211 | PopupButton(isShowing: $floatsSmall.showingTopSecond, hideAll: hideAll) { 212 | PopupTypeView( 213 | title: "Top version 2", 214 | detail: "Top float with a picture" 215 | ) { 216 | SmallFloatsImage(alignment: .top) 217 | } 218 | } 219 | PopupButton(isShowing: $floatsSmall.showingBottomFirst, hideAll: hideAll) { 220 | PopupTypeView( 221 | title: "Bottom version 1", 222 | detail: "Bottom float with a picture" 223 | ) { 224 | SmallFloatsImage(alignment: .bottom) 225 | } 226 | } 227 | PopupButton(isShowing: $floatsSmall.showingBottomSecond, hideAll: hideAll) { 228 | PopupTypeView( 229 | title: "Bottom version 2", 230 | detail: "Bottom float with a picture" 231 | ) { 232 | SmallFloatsImage(alignment: .bottom) 233 | } 234 | } 235 | } 236 | 237 | @ViewBuilder 238 | func floatsSectionBig() -> some View { 239 | PopupButton(isShowing: $floatsBig.showingTopLeading, hideAll: hideAll) { 240 | PopupTypeView(title: "Top Leading") { 241 | BigFloatsImage(alignment: .topLeading) 242 | } 243 | } 244 | PopupButton(isShowing: $floatsBig.showingTop, hideAll: hideAll) { 245 | PopupTypeView(title: "Top Center") { 246 | BigFloatsImage(alignment: .top) 247 | } 248 | } 249 | PopupButton(isShowing: $floatsBig.showingTopTrailing, hideAll: hideAll) { 250 | PopupTypeView(title: "Top Trailing") { 251 | BigFloatsImage(alignment: .topTrailing) 252 | } 253 | } 254 | 255 | PopupButton(isShowing: $floatsBig.showingLeading, hideAll: hideAll) { 256 | PopupTypeView(title: "Center Leading") { 257 | BigFloatsImage(alignment: .leading) 258 | } 259 | } 260 | PopupButton(isShowing: $floatsBig.showingTrailing, hideAll: hideAll) { 261 | PopupTypeView(title: "Center Trailing") { 262 | BigFloatsImage(alignment: .trailing) 263 | } 264 | } 265 | 266 | PopupButton(isShowing: $floatsBig.showingBottomLeading, hideAll: hideAll) { 267 | PopupTypeView(title: "Bottom Leading") { 268 | BigFloatsImage(alignment: .bottomLeading) 269 | } 270 | } 271 | PopupButton(isShowing: $floatsBig.showingBottom, hideAll: hideAll) { 272 | PopupTypeView(title: "Bottom Center") { 273 | BigFloatsImage(alignment: .bottom) 274 | } 275 | } 276 | PopupButton(isShowing: $floatsBig.showingBottomTrailing, hideAll: hideAll) { 277 | PopupTypeView(title: "Bottom Trailing") { 278 | BigFloatsImage(alignment: .bottomTrailing) 279 | } 280 | } 281 | } 282 | 283 | @ViewBuilder 284 | func toastsSection() -> some View { 285 | PopupButton(isShowing: $toasts.showingTopFirst, hideAll: hideAll) { 286 | PopupTypeView( 287 | title: "Top version 1", 288 | detail: "Top toast only text" 289 | ) { 290 | ToastImage(position: .top) 291 | } 292 | } 293 | PopupButton(isShowing: $toasts.showingTopSecond, hideAll: hideAll) { 294 | PopupTypeView( 295 | title: "Top version 2", 296 | detail: "Top float with picture" 297 | ) { 298 | ToastImage(position: .top) 299 | } 300 | } 301 | PopupButton(isShowing: $toasts.showingBottomFirst, hideAll: hideAll) { 302 | PopupTypeView( 303 | title: "Bottom version 1", 304 | detail: "Bottom float with a picture and two buttons" 305 | ) { 306 | ToastImage(position: .bottom) 307 | } 308 | } 309 | PopupButton(isShowing: $toasts.showingBottomSecond, hideAll: hideAll) { 310 | PopupTypeView( 311 | title: "Bottom version 2", 312 | detail: "Bottom float with a picture" 313 | ) { 314 | ToastImage(position: .bottom) 315 | } 316 | } 317 | } 318 | 319 | @ViewBuilder 320 | func popupsSection() -> some View { 321 | ItemPopupButton(item: $popups.middleItem, hideAll: hideAll) { 322 | PopupTypeView( 323 | title: "Middle", 324 | detail: "Popup in the middle of the screen with a picture" 325 | ) { 326 | PopupImage(style: .default) 327 | } 328 | } 329 | PopupButton(isShowing: $popups.showingBottomFirst, hideAll: hideAll) { 330 | PopupTypeView( 331 | title: "Bottom version 1", 332 | detail: "Popup bottom" 333 | ) { 334 | PopupImage(style: .bottomFirst) 335 | } 336 | } 337 | PopupButton(isShowing: $popups.showingBottomSecond, hideAll: hideAll) { 338 | PopupTypeView( 339 | title: "Bottom version 2", 340 | detail: "Popup bottom" 341 | ) { 342 | PopupImage(style: .bottomSecond) 343 | } 344 | } 345 | } 346 | 347 | @ViewBuilder 348 | func actionSheetsSection() -> some View { 349 | PopupButton(isShowing: $actionSheets.showingFirst, hideAll: hideAll) { 350 | PopupTypeView( 351 | title: "Version 1", 352 | detail: "Action sheets" 353 | ) { 354 | ActionSheetImage() 355 | } 356 | } 357 | PopupButton(isShowing: $actionSheets.showingSecond, hideAll: hideAll) { 358 | PopupTypeView( 359 | title: "Version 2", 360 | detail: "Action sheets" 361 | ) { 362 | ActionSheetImage() 363 | } 364 | } 365 | } 366 | 367 | @ViewBuilder 368 | func inputsSection() -> some View { 369 | PopupButton(isShowing: $inputSheets.showingFirst, hideAll: hideAll) { 370 | PopupTypeView( 371 | title: "Bottom Input", 372 | detail: "Popup in the bottom of the screen with an input text field" 373 | ) { 374 | InputSheetImage() 375 | } 376 | } 377 | } 378 | } 379 | 380 | struct PopupsList_Previews: PreviewProvider { 381 | static var previews: some View { 382 | PopupButton( 383 | isShowing: Binding.init(get: { true }, 384 | set: { _ in }), 385 | hideAll: {}) 386 | { 387 | PopupTypeView( 388 | title: "Top version 1", 389 | detail: "Top float with a picture and one button" 390 | ) { 391 | BigFloatsImage(alignment: .top) 392 | } 393 | } 394 | } 395 | } 396 | -------------------------------------------------------------------------------- /PopupExample/PopupExample/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /PopupExample/PopupExample/Utils.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Utils.swift 3 | // Example 4 | // 5 | // Created by Alisa Mylnikova on 10/06/2021. 6 | // 7 | 8 | import SwiftUI 9 | 10 | extension Color { 11 | init(hex: String) { 12 | let scanner = Scanner(string: hex) 13 | var rgbValue: UInt64 = 0 14 | scanner.scanHexInt64(&rgbValue) 15 | 16 | let r = (rgbValue & 0xff0000) >> 16 17 | let g = (rgbValue & 0xff00) >> 8 18 | let b = rgbValue & 0xff 19 | 20 | self.init(red: Double(r) / 0xff, green: Double(g) / 0xff, blue: Double(b) / 0xff) 21 | } 22 | } 23 | 24 | extension View { 25 | 26 | @ViewBuilder 27 | func applyIf(_ condition: Bool, apply: (Self) -> T) -> some View { 28 | if condition { 29 | apply(self) 30 | } else { 31 | self 32 | } 33 | } 34 | 35 | func shadowedStyle() -> some View { 36 | self 37 | .shadow(color: .black.opacity(0.08), radius: 2, x: 0, y: 0) 38 | .shadow(color: .black.opacity(0.16), radius: 24, x: 0, y: 0) 39 | } 40 | 41 | func customButtonStyle( 42 | foreground: Color = .black, 43 | background: Color = .white 44 | ) -> some View { 45 | self.buttonStyle( 46 | ExampleButtonStyle( 47 | foreground: foreground, 48 | background: background 49 | ) 50 | ) 51 | } 52 | 53 | #if os(iOS) 54 | func cornerRadius(_ radius: CGFloat, corners: UIRectCorner) -> some View { 55 | clipShape(RoundedCorner(radius: radius, corners: corners)) 56 | } 57 | #endif 58 | } 59 | 60 | private struct ExampleButtonStyle: ButtonStyle { 61 | let foreground: Color 62 | let background: Color 63 | 64 | func makeBody(configuration: Self.Configuration) -> some View { 65 | configuration.label 66 | .opacity(configuration.isPressed ? 0.45 : 1) 67 | .foregroundColor(configuration.isPressed ? foreground.opacity(0.55) : foreground) 68 | .background(configuration.isPressed ? background.opacity(0.55) : background) 69 | } 70 | } 71 | 72 | #if os(iOS) 73 | struct RoundedCorner: Shape { 74 | var radius: CGFloat = .infinity 75 | var corners: UIRectCorner = .allCorners 76 | 77 | func path(in rect: CGRect) -> Path { 78 | let path = UIBezierPath(roundedRect: rect, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius)) 79 | return Path(path.cgPath) 80 | } 81 | } 82 | #endif 83 | 84 | class Constants { 85 | static let privacyPolicy = """ 86 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam consectetur orci eget rutrum dignissim. Vivamus aliquam a massa a scelerisque. Integer eleifend lectus non blandit ultricies. Maecenas volutpat neque ut elit facilisis sodales. Mauris et iaculis tellus. Etiam nec mi consequat, ornare quam in, ornare magna. Donec quis egestas nunc. Morbi vel orci leo. Suspendisse eget lectus a erat dignissim interdum et quis neque. Fusce dapibus rhoncus nulla. Cras sed ipsum congue, tempus mi nec, vestibulum lorem. 87 | 88 | Mauris rutrum urna ex, eget bibendum lectus vehicula nec. Mauris quis porttitor sapien, id vestibulum nibh. Proin mi lectus, pretium sed nulla bibendum, fringilla dignissim lacus. Vestibulum eget ante quis urna facilisis tristique. Curabitur mollis cursus mauris, vitae sollicitudin lacus fermentum nec. Etiam accumsan venenatis feugiat. Curabitur vitae posuere quam, imperdiet mattis elit. Nulla sollicitudin non neque sed aliquet. Donec lobortis iaculis interdum. 89 | 90 | Nam eu feugiat arcu. Suspendisse porta eu sapien et eleifend. Fusce viverra laoreet tellus, eget convallis odio. Vivamus eget mollis dui. Sed euismod sed justo in fermentum. Nam at augue convallis, vulputate ligula eu, convallis risus. Proin egestas pretium nibh, in blandit ipsum varius quis. Aenean dolor mauris, luctus vel consequat id, tristique sit amet sem. Donec at pulvinar sem. Mauris diam lacus, placerat eget dolor ac, hendrerit elementum velit. 91 | 92 | Integer sagittis ultricies commodo. Nullam eu diam at justo ornare viverra. Praesent ante metus, rhoncus ac condimentum id, malesuada viverra arcu. Nunc porta, odio at elementum viverra, tortor sem placerat lacus, eget scelerisque turpis odio at nisl. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Nulla varius luctus ex, eu sagittis leo tempor nec. Etiam viverra molestie iaculis. Fusce in cursus ipsum, et elementum metus. Nullam sed sodales ligula. Aliquam erat volutpat. Proin mattis nisi et lectus rutrum, quis aliquet metus aliquet. Nulla est nisi, condimentum sed pretium ac, scelerisque semper eros. Nullam varius diam at augue vehicula elementum eget a leo. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Etiam nisi enim, euismod ac tellus at, hendrerit dignissim turpis. Proin sit amet sapien posuere, facilisis velit quis, placerat purus. 93 | 94 | Maecenas eget felis in lacus pharetra tristique. Nunc vehicula porttitor dolor, non viverra magna blandit sit amet. Phasellus et pellentesque ante, at sollicitudin leo. Etiam at quam nec ex rhoncus sagittis. Nullam tempor lectus id felis efficitur tempus eget eget lectus. Mauris vitae odio nisi. Fusce pellentesque mattis enim, vitae tincidunt nisl tempus sed. Sed et lacus vitae lectus pretium congue nec molestie odio. Phasellus nec libero ac enim consequat dapibus. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Morbi suscipit, urna vel elementum consequat, eros urna tempor nulla, vel mattis arcu ex quis nisl. Phasellus consequat porta lectus, eu tristique ipsum laoreet sit amet. Nam scelerisque ipsum sem, vitae sodales risus gravida in. 95 | 96 | Maecenas felis velit, sodales ut diam vitae, sagittis aliquet neque. Duis tristique nisl at tristique hendrerit. Suspendisse sed egestas orci. Phasellus tempor cursus tellus, eget rhoncus justo mattis id. In a dapibus enim. Nulla eu neque tincidunt tellus finibus mattis. Mauris congue tellus vitae tortor laoreet accumsan. 97 | 98 | Aenean iaculis porta consectetur. Vivamus tristique erat consectetur mi congue sollicitudin. Donec pellentesque, arcu pellentesque rhoncus vestibulum, massa diam vehicula nulla, non lacinia nunc lacus ut felis. Nam euismod finibus quam nec placerat. In imperdiet egestas sapien, sed elementum purus. Nullam interdum nisl fermentum ultrices elementum. Quisque eu mi sapien. Morbi vestibulum urna vel lacinia ultrices. Ut urna tortor, luctus in lorem eget, euismod volutpat magna. Etiam a accumsan massa. Fusce finibus blandit diam ac tincidunt. Nullam vitae dolor augue. 99 | 100 | Maecenas maximus feugiat tellus sed vulputate. Proin ut ante vitae justo pulvinar laoreet. Donec fringilla justo consectetur mi consequat porttitor. Sed at mollis metus. Quisque at magna quis est malesuada aliquam sit amet at augue. Mauris hendrerit nunc ligula, in faucibus erat commodo quis. Nulla lacus dolor, cursus quis ligula eu, lacinia sollicitudin felis. Praesent odio tellus, pellentesque vitae leo ac, faucibus facilisis augue. Pellentesque bibendum nisl eget vehicula convallis. Maecenas velit urna, hendrerit quis nulla vitae, aliquam posuere erat. Integer accumsan sed arcu nec tempus. Etiam pharetra suscipit sapien id venenatis. Donec ultricies quis nisi vitae consectetur. 101 | """ 102 | } 103 | -------------------------------------------------------------------------------- /PopupExample/PopupWatchExample Watch App/ContentView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentView.swift 3 | // Example 4 | // 5 | // Created by Alisa Mylnikova on 23/04/2020. 6 | // Copyright © 2020 Exyte. All rights reserved. 7 | // 8 | 9 | import SwiftUI 10 | import PopupView 11 | 12 | struct ExampleButton : View { 13 | 14 | @Binding var showing: Bool 15 | var title: String 16 | var hideAll: ()->() 17 | 18 | var body: some View { 19 | Button { 20 | self.hideAll() 21 | self.showing.toggle() 22 | } label: { 23 | Text(title) 24 | .foregroundColor(.black) 25 | } 26 | .frame(maxHeight: 20) 27 | } 28 | } 29 | 30 | struct ContentView : View { 31 | 32 | let bgColor = Color(hex: "e0fbfc") 33 | let popupColor = Color(hex: "3d5a80") 34 | let topToastColor = Color(hex: "293241") 35 | let bottomToastColor = Color(hex: "98c1d9") 36 | let topFloatColor = Color(hex: "293241") 37 | let bottomFloatColor = Color(hex: "ee6c4d") 38 | 39 | @State var showingTopToast = false 40 | @State var showingBottomToast = false 41 | @State var showingTopFloater = false 42 | 43 | private var screenSize: CGSize { 44 | WKInterfaceDevice.current().screenBounds.size 45 | } 46 | 47 | var body: some View { 48 | 49 | let hideAll = { 50 | self.showingTopToast = false 51 | self.showingBottomToast = false 52 | self.showingTopFloater = false 53 | } 54 | 55 | return ZStack { 56 | bgColor 57 | .edgesIgnoringSafeArea(.all) 58 | VStack(spacing: 8) { 59 | ExampleButton(showing: $showingTopToast, title: "Top toast", hideAll: hideAll) 60 | ExampleButton(showing: $showingBottomToast, title: "Bottom toast", hideAll: hideAll) 61 | ExampleButton(showing: $showingTopFloater, title: "Top floater", hideAll: hideAll) 62 | } 63 | } 64 | 65 | .popup(isPresented: $showingTopToast) { 66 | createTopToast() 67 | } customize: { 68 | $0 69 | .type(.toast) 70 | .position(.top) 71 | } 72 | 73 | .popup(isPresented: $showingBottomToast) { 74 | createBottomToast() 75 | } customize: { 76 | $0 77 | .type(.toast) 78 | .position(.bottom) 79 | } 80 | 81 | .popup(isPresented: $showingTopFloater) { 82 | createTopFloater() 83 | } customize: { 84 | $0 85 | .type(.floater()) 86 | .position(.top) 87 | .animation(.spring()) 88 | .autohideIn(2) 89 | } 90 | 91 | } 92 | 93 | func createTopToast() -> some View { 94 | HStack { 95 | Image("shop_NA") 96 | .resizable() 97 | .aspectRatio(contentMode: ContentMode.fill) 98 | .frame(width: 50, height: 50) 99 | .cornerRadius(25) 100 | 101 | VStack(alignment: .leading, spacing: 2) { 102 | HStack { 103 | Text("Nik") 104 | .foregroundColor(.white) 105 | .fontWeight(.bold) 106 | Spacer() 107 | Text("11:30") 108 | .font(.system(size: 12)) 109 | .foregroundColor(Color(red: 0.9, green: 0.9, blue: 0.9)) 110 | } 111 | 112 | Text("How about a dinner in an hour? We could discuss that one urgent issue we should be discussing.") 113 | .lineLimit(2) 114 | .font(.system(size: 14)) 115 | .foregroundColor(.white) 116 | } 117 | } 118 | .padding(15) 119 | .frame(width: screenSize.width) 120 | .background(self.topToastColor) 121 | } 122 | 123 | func createBottomToast() -> some View { 124 | HStack { 125 | Image("grapes") 126 | .resizable() 127 | .aspectRatio(contentMode: ContentMode.fill) 128 | .frame(width: 40, height: 40) 129 | 130 | VStack(alignment: .leading, spacing: 2) { 131 | Text("Grapes!") 132 | .foregroundColor(.black) 133 | .fontWeight(.bold) 134 | 135 | Text("Step right up!") 136 | .lineLimit(2) 137 | .font(.system(size: 14)) 138 | .foregroundColor(.black) 139 | } 140 | } 141 | .padding(15) 142 | .frame(width: screenSize.width) 143 | .background(self.bottomToastColor) 144 | } 145 | 146 | func createTopFloater() -> some View { 147 | HStack(spacing: 10) { 148 | Image("transaction_coffee") 149 | .resizable() 150 | .aspectRatio(contentMode: ContentMode.fill) 151 | .frame(width: 20, height: 20) 152 | VStack(spacing: 8) { 153 | Text("Temperature") 154 | .font(.system(size: 12)) 155 | .foregroundColor(.white) 156 | 157 | HStack(spacing: 0) { 158 | Color(red: 1, green: 112/255, blue: 59/255) 159 | .frame(width: 20, height: 5) 160 | Color(red: 1, green: 1, blue: 1) 161 | .frame(width: 60, height: 5) 162 | } 163 | .cornerRadius(2.5) 164 | } 165 | } 166 | .frame(width: 150, height: 60) 167 | .background(self.topFloatColor) 168 | .cornerRadius(30.0) 169 | } 170 | 171 | } 172 | 173 | extension Color { 174 | init(hex: String) { 175 | let scanner = Scanner(string: hex) 176 | var rgbValue: UInt64 = 0 177 | scanner.scanHexInt64(&rgbValue) 178 | 179 | let r = (rgbValue & 0xff0000) >> 16 180 | let g = (rgbValue & 0xff00) >> 8 181 | let b = rgbValue & 0xff 182 | 183 | self.init(red: Double(r) / 0xff, green: Double(g) / 0xff, blue: Double(b) / 0xff) 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /PopupExample/PopupWatchExample Watch App/PopupWatchExampleApp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PopupWatchExampleApp.swift 3 | // PopupWatchExample Watch App 4 | // 5 | // Created by Alisa Mylnikova on 15.05.2023. 6 | // 7 | 8 | import SwiftUI 9 | 10 | @main 11 | struct PopupWatchExample_Watch_AppApp: App { 12 | var body: some Scene { 13 | WindowGroup { 14 | ContentView() 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /PopupExample/PopupWatchExample Watch App/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Sources/PopupView/FullscreenPopup.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FullscreenPopup.swift 3 | // Pods 4 | // 5 | // Created by Alisa Mylnikova on 29.12.2022. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | 11 | @MainActor 12 | public struct FullscreenPopup: ViewModifier { 13 | 14 | // MARK: - Presentation 15 | 16 | @State var id = UUID() 17 | 18 | @Binding var isPresented: Bool 19 | @Binding var item: Item? 20 | 21 | var isBoolMode: Bool 22 | 23 | var popupPresented: Bool { 24 | item != nil || isPresented 25 | } 26 | 27 | // MARK: - Parameters 28 | 29 | /// If nil - never hides on its own 30 | var autohideIn: Double? 31 | 32 | /// Only allow dismiss by any means after this time passes 33 | var dismissibleIn: Double? 34 | 35 | /// Becomes true when `dismissibleIn` times finishes 36 | /// Makes no sense if `dismissibleIn` is nil 37 | var dismissEnabled: Binding 38 | 39 | /// Should close on tap outside - default is `false` 40 | var closeOnTapOutside: Bool 41 | 42 | /// Background color for outside area - default is `Color.clear` 43 | var backgroundColor: Color 44 | 45 | /// Custom background view for outside area 46 | var backgroundView: AnyView? 47 | 48 | /// If opaque - taps do not pass through popup's background color 49 | var displayMode: Popup.DisplayMode 50 | 51 | /// called when when dismiss animation starts 52 | var userWillDismissCallback: (DismissSource) -> () 53 | 54 | /// called when when dismiss animation ends 55 | var userDismissCallback: (DismissSource) -> () 56 | 57 | var params: Popup.PopupParameters 58 | 59 | var view: (() -> PopupContent)! 60 | var itemView: ((Item) -> PopupContent)! 61 | 62 | // MARK: - Presentation animation 63 | 64 | /// Trigger popup showing/hiding animations and... 65 | @State private var shouldShowContent = false 66 | 67 | /// ... once hiding animation is finished remove popup from the memory using this flag 68 | @State private var showContent = false 69 | 70 | /// keep track of closing state to avoid unnecessary showing bug 71 | @State private var closingIsInProcess = false 72 | 73 | /// show transparentNonAnimatingFullScreenCover 74 | @State private var showSheet = false 75 | 76 | /// opacity of background color 77 | @State private var animatableOpacity: CGFloat = 0 78 | 79 | /// A temporary variable to hold a copy of the `itemView` when the item is nil (to complete `itemView`'s dismiss animation) 80 | @State private var tempItemView: PopupContent? 81 | 82 | // MARK: - Autohide 83 | 84 | /// Class reference for capturing a weak reference later in dispatch work holder. 85 | private var isPresentedRef: ClassReference>? 86 | private var itemRef: ClassReference>? 87 | 88 | /// holder for autohiding dispatch work (to be able to cancel it when needed) 89 | @State private var autohidingWorkHolder = DispatchWorkHolder() 90 | 91 | /// holder for `dismissibleIn` dispatch work (to be able to cancel it when needed) 92 | @State private var dismissibleInWorkHolder = DispatchWorkHolder() 93 | 94 | // MARK: - Autohide With Dragging 95 | /// If user "grabbed" the popup to drag it around, put off the autohiding until he lifts his finger up 96 | 97 | /// is user currently holding th popup with his finger 98 | @State private var isDragging = false 99 | 100 | /// if autohide time was set up, shows that timer has come to an end already 101 | @State private var timeToHide = false 102 | 103 | // MARK: - dismissibleIn 104 | 105 | private var dismissEnabledRef: ClassReference>? 106 | 107 | // MARK: - Internal 108 | 109 | /// Set dismiss source to pass to dismiss callback 110 | @State private var dismissSource: DismissSource? 111 | 112 | /// Synchronize isPresented changes and animations 113 | private let eventsQueue = DispatchQueue(label: "eventsQueue", qos: .utility) 114 | @State private var eventsSemaphore = DispatchSemaphore(value: 1) 115 | 116 | init(isPresented: Binding = .constant(false), 117 | item: Binding = .constant(nil), 118 | isBoolMode: Bool, 119 | params: Popup.PopupParameters, 120 | view: (() -> PopupContent)?, 121 | itemView: ((Item) -> PopupContent)?) { 122 | self._isPresented = isPresented 123 | self._item = item 124 | self.isBoolMode = isBoolMode 125 | 126 | self.params = params 127 | self.autohideIn = params.autohideIn 128 | self.dismissibleIn = params.dismissibleIn 129 | self.dismissEnabled = params.dismissEnabled 130 | self.closeOnTapOutside = params.closeOnTapOutside 131 | self.backgroundColor = params.backgroundColor 132 | self.backgroundView = params.backgroundView 133 | self.displayMode = params.displayMode 134 | self.userDismissCallback = params.dismissCallback 135 | self.userWillDismissCallback = params.willDismissCallback 136 | 137 | if let view = view { 138 | self.view = view 139 | } 140 | if let itemView = itemView { 141 | self.itemView = itemView 142 | } 143 | 144 | self.isPresentedRef = ClassReference(self.$isPresented) 145 | self.itemRef = ClassReference(self.$item) 146 | self.dismissEnabledRef = ClassReference(self.dismissEnabled) 147 | } 148 | 149 | public func body(content: Content) -> some View { 150 | if isBoolMode { 151 | main(content: content) 152 | .onChange(of: isPresented) { newValue in 153 | eventsQueue.async { [eventsSemaphore] in 154 | eventsSemaphore.wait() 155 | DispatchQueue.main.async { 156 | closingIsInProcess = !newValue 157 | appearAction(popupPresented: newValue) 158 | } 159 | } 160 | } 161 | .onAppear { 162 | if isPresented { 163 | appearAction(popupPresented: true) 164 | } 165 | } 166 | } else { 167 | main(content: content) 168 | .onChange(of: item) { newValue in 169 | DispatchQueue.main.asyncAfter(deadline: .now() + 0.03) { 170 | self.closingIsInProcess = newValue == nil 171 | if let newValue { 172 | /// copying `itemView` 173 | self.tempItemView = itemView(newValue) 174 | } 175 | appearAction(popupPresented: newValue != nil) 176 | } 177 | } 178 | .onAppear { 179 | if let item { 180 | self.tempItemView = itemView(item) 181 | appearAction(popupPresented: true) 182 | } 183 | } 184 | } 185 | } 186 | 187 | @ViewBuilder 188 | public func main(content: Content) -> some View { 189 | #if os(iOS) 190 | switch displayMode { 191 | case .overlay: 192 | ZStack { 193 | content 194 | constructPopup() 195 | } 196 | 197 | case .sheet: 198 | content.transparentNonAnimatingFullScreenCover(isPresented: $showSheet, dismissSource: dismissSource, userDismissCallback: userDismissCallback) { 199 | constructPopup() 200 | } 201 | 202 | case .window: 203 | content 204 | .onChange(of: showSheet) { newValue in 205 | if newValue { 206 | WindowManager.showInNewWindow(id: id, dismissClosure: { 207 | dismissSource = .binding 208 | isPresented = false 209 | item = nil 210 | }) { 211 | constructPopup() 212 | } 213 | } else { 214 | WindowManager.closeWindow(id: id) 215 | } 216 | } 217 | } 218 | #else 219 | ZStack { 220 | content 221 | constructPopup() 222 | } 223 | #endif 224 | } 225 | 226 | @ViewBuilder 227 | func constructPopup() -> some View { 228 | if showContent { 229 | PopupBackgroundView( 230 | id: $id, 231 | isPresented: $isPresented, 232 | item: $item, 233 | animatableOpacity: $animatableOpacity, 234 | dismissSource: $dismissSource, 235 | backgroundColor: backgroundColor, 236 | backgroundView: backgroundView, 237 | closeOnTapOutside: closeOnTapOutside, 238 | dismissEnabled: dismissEnabled 239 | ) 240 | .modifier(getModifier()) 241 | } 242 | } 243 | 244 | var viewForItem: (() -> PopupContent)? { 245 | if let item = item { 246 | return { itemView(item) } 247 | } else if let tempItemView { 248 | return { tempItemView } 249 | } 250 | return nil 251 | } 252 | 253 | private func getModifier() -> Popup { 254 | Popup( 255 | params: params, 256 | view: viewForItem != nil ? viewForItem! : view, 257 | shouldShowContent: $shouldShowContent, 258 | showContent: showContent, 259 | isDragging: $isDragging, 260 | timeToHide: $timeToHide, 261 | positionIsCalculatedCallback: { 262 | // once the closing has been started, don't allow position recalculation to trigger popup showing again 263 | if !closingIsInProcess { 264 | DispatchQueue.main.async { 265 | shouldShowContent = true // this will cause currentOffset change thus triggering the sliding showing animation 266 | withAnimation(.linear(duration: 0.2)) { 267 | animatableOpacity = 1 // this will cause cross dissolving animation for background color/view 268 | } 269 | } 270 | setupAutohide() 271 | setupdismissibleIn() 272 | } 273 | }, 274 | dismissCallback: { source in 275 | dismissSource = source 276 | isPresented = false 277 | item = nil 278 | } 279 | ) 280 | } 281 | 282 | func appearAction(popupPresented: Bool) { 283 | if popupPresented { 284 | dismissSource = nil 285 | showSheet = true // show transparent fullscreen sheet 286 | showContent = true // immediately load popup body 287 | // shouldShowContent is set after popup's frame is calculated, see positionIsCalculatedCallback 288 | } else { 289 | closingIsInProcess = true 290 | userWillDismissCallback(dismissSource ?? .binding) 291 | autohidingWorkHolder.work?.cancel() 292 | dismissibleInWorkHolder.work?.cancel() 293 | shouldShowContent = false // this will cause currentOffset change thus triggering the sliding hiding animation 294 | animatableOpacity = 0 295 | // do the rest once the animation is finished (see onAnimationCompleted()) 296 | } 297 | 298 | // animation completion block isn't being called reliably when there are other animations happening at the same time (drag, autohide, etc.) so here we imitate onAnimationCompleted 299 | performWithDelay(0.3) { 300 | onAnimationCompleted() 301 | } 302 | } 303 | 304 | func onAnimationCompleted() { 305 | if shouldShowContent { // return if this was called on showing animation, only proceed if called on hiding 306 | eventsSemaphore.signal() 307 | return 308 | } 309 | showContent = false // unload popup body after hiding animation is done 310 | tempItemView = nil 311 | if dismissibleIn != nil { 312 | dismissEnabled.wrappedValue = false 313 | } 314 | performWithDelay(0.01) { 315 | showSheet = false 316 | } 317 | if displayMode != .sheet { // for .sheet this callback is called in fullScreenCover's onDisappear 318 | userDismissCallback(dismissSource ?? .binding) 319 | } 320 | 321 | eventsSemaphore.signal() 322 | } 323 | 324 | func setupAutohide() { 325 | // if needed, dispatch autohide and cancel previous one 326 | if let autohideIn = autohideIn { 327 | autohidingWorkHolder.work?.cancel() 328 | 329 | // Weak reference to avoid the work item capturing the struct, 330 | // which would create a retain cycle with the work holder itself. 331 | 332 | autohidingWorkHolder.work = DispatchWorkItem(block: { [weak isPresentedRef, weak itemRef] in 333 | if isDragging { 334 | timeToHide = true // raise this flag to hide the popup once the drag is over 335 | return 336 | } 337 | dismissSource = .autohide 338 | isPresentedRef?.value.wrappedValue = false 339 | itemRef?.value.wrappedValue = nil 340 | autohidingWorkHolder.work = nil 341 | }) 342 | if popupPresented, let work = autohidingWorkHolder.work { 343 | DispatchQueue.main.asyncAfter(deadline: .now() + autohideIn, execute: work) 344 | } 345 | } 346 | } 347 | 348 | func setupdismissibleIn() { 349 | if let dismissibleIn = dismissibleIn { 350 | dismissibleInWorkHolder.work?.cancel() 351 | 352 | // Weak reference to avoid the work item capturing the struct, 353 | // which would create a retain cycle with the work holder itself. 354 | 355 | dismissibleInWorkHolder.work = DispatchWorkItem(block: { [weak dismissEnabledRef] in 356 | dismissEnabledRef?.value.wrappedValue = true 357 | dismissibleInWorkHolder.work = nil 358 | }) 359 | if popupPresented, let work = dismissibleInWorkHolder.work { 360 | DispatchQueue.main.asyncAfter(deadline: .now() + dismissibleIn, execute: work) 361 | } 362 | } 363 | } 364 | 365 | func performWithDelay(_ delay: Double, block: @escaping ()->()) { 366 | DispatchQueue.main.asyncAfter(deadline: .now() + delay) { 367 | block() 368 | } 369 | } 370 | } 371 | -------------------------------------------------------------------------------- /Sources/PopupView/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | 22 | 23 | -------------------------------------------------------------------------------- /Sources/PopupView/Modifiers.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Constructors.swift 3 | // Pods 4 | // 5 | // Created by Alisa Mylnikova on 11.10.2022. 6 | // 7 | 8 | import SwiftUI 9 | 10 | public typealias SendableClosure = @Sendable @MainActor () -> Void 11 | 12 | struct PopupDismissKey: EnvironmentKey { 13 | static let defaultValue: SendableClosure? = nil 14 | } 15 | 16 | public extension EnvironmentValues { 17 | var popupDismiss: SendableClosure? { 18 | get { self[PopupDismissKey.self] } 19 | set { self[PopupDismissKey.self] = newValue } 20 | } 21 | } 22 | 23 | @MainActor 24 | extension View { 25 | public func popup( 26 | isPresented: Binding, 27 | @ViewBuilder view: @escaping () -> PopupContent, 28 | customize: @escaping (Popup.PopupParameters) -> Popup.PopupParameters 29 | ) -> some View { 30 | self.modifier( 31 | FullscreenPopup( 32 | isPresented: isPresented, 33 | isBoolMode: true, 34 | params: customize(Popup.PopupParameters()), 35 | view: view, 36 | itemView: nil) 37 | ) 38 | .environment(\.popupDismiss) { 39 | isPresented.wrappedValue = false 40 | } 41 | } 42 | 43 | public func popup( 44 | item: Binding, 45 | @ViewBuilder itemView: @escaping (Item) -> PopupContent, 46 | customize: @escaping (Popup.PopupParameters) -> Popup.PopupParameters 47 | ) -> some View { 48 | self.modifier( 49 | FullscreenPopup( 50 | item: item, 51 | isBoolMode: false, 52 | params: customize(Popup.PopupParameters()), 53 | view: nil, 54 | itemView: itemView) 55 | ) 56 | .environment(\.popupDismiss) { 57 | item.wrappedValue = nil 58 | } 59 | } 60 | 61 | public func popup( 62 | isPresented: Binding, 63 | @ViewBuilder view: @escaping () -> PopupContent) -> some View { 64 | self.modifier( 65 | FullscreenPopup( 66 | isPresented: isPresented, 67 | isBoolMode: true, 68 | params: Popup.PopupParameters(), 69 | view: view, 70 | itemView: nil) 71 | ) 72 | .environment(\.popupDismiss) { 73 | isPresented.wrappedValue = false 74 | } 75 | } 76 | 77 | public func popup( 78 | item: Binding, 79 | @ViewBuilder itemView: @escaping (Item) -> PopupContent) -> some View { 80 | self.modifier( 81 | FullscreenPopup( 82 | item: item, 83 | isBoolMode: false, 84 | params: Popup.PopupParameters(), 85 | view: nil, 86 | itemView: itemView) 87 | ) 88 | .environment(\.popupDismiss) { 89 | item.wrappedValue = nil 90 | } 91 | } 92 | } 93 | 94 | #if os(iOS) 95 | 96 | @MainActor 97 | extension View { 98 | func onOrientationChange(isLandscape: Binding, onOrientationChange: @escaping () -> Void) -> some View { 99 | self.modifier(OrientationChangeModifier(isLandscape: isLandscape, onOrientationChange: onOrientationChange)) 100 | } 101 | } 102 | 103 | @MainActor 104 | struct OrientationChangeModifier: ViewModifier { 105 | @Binding var isLandscape: Bool 106 | let onOrientationChange: () -> Void 107 | 108 | func body(content: Content) -> some View { 109 | content 110 | #if os(iOS) 111 | .onReceive(NotificationCenter.default 112 | .publisher(for: UIDevice.orientationDidChangeNotification) 113 | .receive(on: DispatchQueue.main) 114 | ) { _ in 115 | updateOrientation() 116 | } 117 | #endif 118 | // .onAppear { 119 | //#if os(iOS) 120 | // NotificationCenter.default.addObserver(forName: UIDevice.orientationDidChangeNotification, object: nil, queue: .main) { _ in 121 | // DispatchQueue.main.async { 122 | // updateOrientation() 123 | // } 124 | // } 125 | // updateOrientation() 126 | //#endif 127 | // } 128 | // .onDisappear { 129 | // #if os(iOS) 130 | // NotificationCenter.default.removeObserver(self, name: UIDevice.orientationDidChangeNotification, object: nil) 131 | // #endif 132 | // } 133 | .onChange(of: isLandscape) { _ in 134 | onOrientationChange() 135 | } 136 | } 137 | 138 | #if os(iOS) 139 | private func updateOrientation() { 140 | let newIsLandscape = UIDevice.current.orientation.isLandscape 141 | if newIsLandscape != isLandscape { 142 | isLandscape = newIsLandscape 143 | onOrientationChange() 144 | } 145 | } 146 | #endif 147 | } 148 | 149 | #endif 150 | -------------------------------------------------------------------------------- /Sources/PopupView/PopupBackgroundView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // with.swift 3 | // PopupView 4 | // 5 | // Created by Alisa Mylnikova on 28.03.2025. 6 | // 7 | 8 | import SwiftUI 9 | 10 | /// this is a separe struct with @Bindings because of how UIWindow doesn't receive updates in usual SwiftUI manner 11 | struct PopupBackgroundView: View { 12 | 13 | @Binding var id: UUID 14 | 15 | @Binding var isPresented: Bool 16 | @Binding var item: Item? 17 | 18 | @Binding var animatableOpacity: CGFloat 19 | @Binding var dismissSource: DismissSource? 20 | 21 | var backgroundColor: Color 22 | var backgroundView: AnyView? 23 | var closeOnTapOutside: Bool 24 | var dismissEnabled: Binding 25 | 26 | var body: some View { 27 | Group { 28 | if let backgroundView = backgroundView { 29 | backgroundView 30 | } else { 31 | backgroundColor 32 | } 33 | } 34 | .opacity(animatableOpacity) 35 | .applyIf(closeOnTapOutside) { view in 36 | view.contentShape(Rectangle()) 37 | } 38 | .addTapIfNotTV(if: closeOnTapOutside) { 39 | if dismissEnabled.wrappedValue { 40 | dismissSource = .tapOutside 41 | isPresented = false 42 | item = nil 43 | } 44 | } 45 | .edgesIgnoringSafeArea(.all) 46 | .animation(.linear(duration: 0.2), value: animatableOpacity) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Sources/PopupView/PopupScrollViewDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PopupScrollViewDelegate.swift 3 | // 4 | // 5 | // Created by vadim.vitkovskiy on 15.02.2024. 6 | // 7 | 8 | #if os(iOS) 9 | import UIKit 10 | 11 | extension UIScrollView { 12 | func maxContentOffsetHeight() -> CGFloat { 13 | let contentHeight = contentSize.height 14 | let visibleHeight = bounds.height 15 | let maxOffsetHeight = max(0, contentHeight - visibleHeight) 16 | return maxOffsetHeight 17 | } 18 | } 19 | 20 | final class PopupScrollViewDelegate: NSObject, ObservableObject, UIScrollViewDelegate { 21 | 22 | var scrollView: UIScrollView? 23 | 24 | var gestureIsCreated = false 25 | 26 | var didReachTop: (Double) -> Void = {_ in } 27 | var scrollEnded: (Double) -> Void = {_ in } 28 | 29 | @objc 30 | func handlePan(_ gesture: UIPanGestureRecognizer) { 31 | let translation = gesture.translation(in: scrollView) 32 | let contentOffset = scrollView?.contentOffset.y ?? 0 33 | let maxContentOffset = scrollView?.maxContentOffsetHeight() ?? 0 34 | 35 | if contentOffset - translation.y > 0 { 36 | scrollView?.contentOffset.y = min(contentOffset - translation.y, maxContentOffset) 37 | gesture.setTranslation(.zero, in: scrollView) 38 | } else { 39 | scrollView?.contentOffset.y = 0 40 | didReachTop(contentOffset - translation.y) 41 | } 42 | 43 | if gesture.state == .ended && contentOffset - translation.y < 0 { 44 | scrollEnded(contentOffset - translation.y) 45 | } 46 | } 47 | 48 | func addGestureIfNeeded() { 49 | guard let gestures = scrollView?.gestureRecognizers else { return } 50 | 51 | if !gestureIsCreated { 52 | let panGesture = gestures[1] as? UIPanGestureRecognizer 53 | panGesture?.addTarget(self, action: #selector(handlePan)) 54 | scrollView?.bounces = false 55 | gestureIsCreated = true 56 | } 57 | } 58 | } 59 | #endif 60 | -------------------------------------------------------------------------------- /Sources/PopupView/PopupView.h: -------------------------------------------------------------------------------- 1 | // 2 | // PopupView.h 3 | // PopupView 4 | // 5 | // Created by Alisa Mylnikova on 23/04/2020. 6 | // Copyright © 2020 Exyte. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for PopupView. 12 | FOUNDATION_EXPORT double PopupViewVersionNumber; 13 | 14 | //! Project version string for PopupView. 15 | FOUNDATION_EXPORT const unsigned char PopupViewVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /Sources/PopupView/PublicAPI.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PublicAPI.swift 3 | // PopupView 4 | // 5 | // Created by Alisa Mylnikova on 28.03.2025. 6 | // 7 | 8 | import SwiftUI 9 | 10 | public enum DismissSource { 11 | case binding // set isPresented to false ot item to nil 12 | case tapInside 13 | case tapOutside 14 | case drag 15 | case autohide 16 | } 17 | 18 | extension Popup { 19 | 20 | public enum PopupType { 21 | case `default` 22 | case toast 23 | case floater(verticalPadding: CGFloat = 10, horizontalPadding: CGFloat = 10, useSafeAreaInset: Bool = true) 24 | #if os(iOS) 25 | case scroll(headerView: AnyView) 26 | #endif 27 | 28 | var defaultPosition: Position { 29 | if case .default = self { 30 | return .center 31 | } 32 | return .bottom 33 | } 34 | 35 | var verticalPadding: CGFloat { 36 | if case let .floater(verticalPadding, _, _) = self { 37 | return verticalPadding 38 | } 39 | return 0 40 | } 41 | 42 | var horizontalPadding: CGFloat { 43 | if case let .floater(_, horizontalPadding, _) = self { 44 | return horizontalPadding 45 | } 46 | return 0 47 | } 48 | 49 | var useSafeAreaInset: Bool { 50 | if case let .floater(_, _, use) = self { 51 | return use 52 | } 53 | return false 54 | } 55 | } 56 | 57 | public enum DisplayMode { 58 | case overlay // place the popup above the content in a ZStack 59 | case sheet // using .fullscreenSheet 60 | case window // using UIWindow 61 | } 62 | 63 | public enum Position { 64 | case topLeading 65 | case top 66 | case topTrailing 67 | 68 | case leading 69 | case center // usual popup 70 | case trailing 71 | 72 | case bottomLeading 73 | case bottom 74 | case bottomTrailing 75 | 76 | var isTop: Bool { 77 | [.topLeading, .top, .topTrailing].contains(self) 78 | } 79 | 80 | var isVerticalCenter: Bool { 81 | [.leading, .center, .trailing].contains(self) 82 | } 83 | 84 | var isBottom: Bool { 85 | [.bottomLeading, .bottom, .bottomTrailing].contains(self) 86 | } 87 | 88 | var isLeading: Bool { 89 | [.topLeading, .leading, .bottomLeading].contains(self) 90 | } 91 | 92 | var isHorizontalCenter: Bool { 93 | [.top, .center, .bottom].contains(self) 94 | } 95 | 96 | var isTrailing: Bool { 97 | [.topTrailing, .trailing, .bottomTrailing].contains(self) 98 | } 99 | } 100 | 101 | public enum AppearAnimation { 102 | case topSlide 103 | case bottomSlide 104 | case leftSlide 105 | case rightSlide 106 | case centerScale 107 | case none 108 | } 109 | 110 | public struct PopupParameters { 111 | var type: PopupType = .default 112 | var displayMode: DisplayMode = .window 113 | 114 | var position: Position? 115 | 116 | var appearFrom: AppearAnimation? 117 | var disappearTo: AppearAnimation? 118 | 119 | var animation: Animation = .easeOut(duration: 0.3) 120 | 121 | /// If nil - never hides on its own 122 | var autohideIn: Double? 123 | 124 | /// Only allow dismiss by any means after this time passes 125 | var dismissibleIn: Double? 126 | 127 | /// Becomes true when `dismissibleIn` times finishes 128 | /// Makes no sense if `dismissibleIn` is nil 129 | var dismissEnabled: Binding = .constant(true) 130 | 131 | /// Should allow dismiss by dragging - default is `true` 132 | var dragToDismiss: Bool = true 133 | 134 | /// Minimum distance to drag to dismiss 135 | var dragToDismissDistance: CGFloat? 136 | 137 | /// Should close on tap - default is `true` 138 | var closeOnTap: Bool = true 139 | 140 | /// Should close on tap outside - default is `false` 141 | var closeOnTapOutside: Bool = false 142 | 143 | /// Background color for outside area 144 | var backgroundColor: Color = .clear 145 | 146 | /// Custom background view for outside area 147 | var backgroundView: AnyView? 148 | 149 | /// move up for keyboardHeight when it is displayed 150 | var useKeyboardSafeArea: Bool = false 151 | 152 | /// called when when dismiss animation starts 153 | var willDismissCallback: (DismissSource) -> () = {_ in} 154 | 155 | /// called when when dismiss animation ends 156 | var dismissCallback: (DismissSource) -> () = {_ in} 157 | 158 | public func type(_ type: PopupType) -> PopupParameters { 159 | var params = self 160 | params.type = type 161 | return params 162 | } 163 | 164 | public func displayMode(_ displayMode: DisplayMode) -> PopupParameters { 165 | var params = self 166 | params.displayMode = displayMode 167 | return params 168 | } 169 | 170 | public func position(_ position: Position) -> PopupParameters { 171 | var params = self 172 | params.position = position 173 | return params 174 | } 175 | 176 | public func appearFrom(_ appearFrom: AppearAnimation) -> PopupParameters { 177 | var params = self 178 | params.appearFrom = appearFrom 179 | return params 180 | } 181 | 182 | public func disappearTo(_ disappearTo: AppearAnimation) -> PopupParameters { 183 | var params = self 184 | params.disappearTo = disappearTo 185 | return params 186 | } 187 | 188 | public func animation(_ animation: Animation) -> PopupParameters { 189 | var params = self 190 | params.animation = animation 191 | return params 192 | } 193 | 194 | public func autohideIn(_ autohideIn: Double?) -> PopupParameters { 195 | var params = self 196 | params.autohideIn = autohideIn 197 | return params 198 | } 199 | 200 | public func dismissibleIn(_ dismissibleIn: Double?, _ dismissEnabled: Binding?) -> PopupParameters { 201 | var params = self 202 | params.dismissibleIn = dismissibleIn 203 | if let dismissEnabled = dismissEnabled { 204 | params.dismissEnabled = dismissEnabled 205 | } 206 | return params 207 | } 208 | 209 | /// Should allow dismiss by dragging - default is `true` 210 | public func dragToDismiss(_ dragToDismiss: Bool) -> PopupParameters { 211 | var params = self 212 | params.dragToDismiss = dragToDismiss 213 | return params 214 | } 215 | 216 | /// Minimum distance to drag to dismiss 217 | public func dragToDismissDistance(_ dragToDismissDistance: CGFloat) -> PopupParameters { 218 | var params = self 219 | params.dragToDismissDistance = dragToDismissDistance 220 | return params 221 | } 222 | 223 | /// Should close on tap - default is `true` 224 | public func closeOnTap(_ closeOnTap: Bool) -> PopupParameters { 225 | var params = self 226 | params.closeOnTap = closeOnTap 227 | return params 228 | } 229 | 230 | /// Should close on tap outside - default is `false` 231 | public func closeOnTapOutside(_ closeOnTapOutside: Bool) -> PopupParameters { 232 | var params = self 233 | params.closeOnTapOutside = closeOnTapOutside 234 | return params 235 | } 236 | 237 | public func backgroundColor(_ backgroundColor: Color) -> PopupParameters { 238 | var params = self 239 | params.backgroundColor = backgroundColor 240 | return params 241 | } 242 | 243 | public func backgroundView(_ backgroundView: ()->(BackgroundView)) -> PopupParameters { 244 | var params = self 245 | params.backgroundView = AnyView(backgroundView()) 246 | return params 247 | } 248 | 249 | @available(*, deprecated, message: "use displayMode instead") 250 | public func isOpaque(_ isOpaque: Bool) -> PopupParameters { 251 | var params = self 252 | params.displayMode = isOpaque ? .sheet : .overlay 253 | return params 254 | } 255 | 256 | public func useKeyboardSafeArea(_ useKeyboardSafeArea: Bool) -> PopupParameters { 257 | var params = self 258 | params.useKeyboardSafeArea = useKeyboardSafeArea 259 | return params 260 | } 261 | 262 | // MARK: - dismiss callbacks 263 | 264 | public func willDismissCallback(_ dismissCallback: @escaping (DismissSource) -> ()) -> PopupParameters { 265 | var params = self 266 | params.willDismissCallback = dismissCallback 267 | return params 268 | } 269 | 270 | public func willDismissCallback(_ dismissCallback: @escaping () -> ()) -> PopupParameters { 271 | var params = self 272 | params.willDismissCallback = { _ in 273 | dismissCallback() 274 | } 275 | return params 276 | } 277 | 278 | @available(*, deprecated, renamed: "dismissCallback") 279 | public func dismissSourceCallback(_ dismissCallback: @escaping (DismissSource) -> ()) -> PopupParameters { 280 | var params = self 281 | params.dismissCallback = dismissCallback 282 | return params 283 | } 284 | 285 | public func dismissCallback(_ dismissCallback: @escaping (DismissSource) -> ()) -> PopupParameters { 286 | var params = self 287 | params.dismissCallback = dismissCallback 288 | return params 289 | } 290 | 291 | public func dismissCallback(_ dismissCallback: @escaping () -> ()) -> PopupParameters { 292 | var params = self 293 | params.dismissCallback = { _ in 294 | dismissCallback() 295 | } 296 | return params 297 | } 298 | } 299 | } 300 | -------------------------------------------------------------------------------- /Sources/PopupView/Utils.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Utils.swift 3 | // PopupView 4 | // 5 | // Created by Alisa Mylnikova on 01.06.2022. 6 | // Copyright © 2022 Exyte. All rights reserved. 7 | // 8 | 9 | import SwiftUI 10 | import Combine 11 | import Foundation 12 | 13 | struct MemoryAddress: CustomStringConvertible { 14 | 15 | let intValue: Int 16 | 17 | var description: String { 18 | let length = 2 + 2 * MemoryLayout.size 19 | return String(format: "%0\(length)p", intValue) 20 | } 21 | 22 | // for structures 23 | init(of structPointer: UnsafePointer) { 24 | intValue = Int(bitPattern: structPointer) 25 | } 26 | } 27 | 28 | extension MemoryAddress where T: AnyObject { 29 | 30 | // for classes 31 | init(of classInstance: T) { 32 | intValue = unsafeBitCast(classInstance, to: Int.self) 33 | // or Int(bitPattern: Unmanaged.passUnretained(classInstance).toOpaque()) 34 | } 35 | } 36 | final class DispatchWorkHolder { 37 | var work: DispatchWorkItem? 38 | } 39 | 40 | final class ClassReference { 41 | var value: T 42 | 43 | init(_ value: T) { 44 | self.value = value 45 | } 46 | } 47 | 48 | extension View { 49 | 50 | @ViewBuilder 51 | func valueChanged(value: T, onChange: @escaping (T) -> Void) -> some View { 52 | if #available(iOS 14.0, tvOS 14.0, macOS 11.0, watchOS 7.0, *) { 53 | self.onChange(of: value, perform: onChange) 54 | } else { 55 | self.onReceive(Just(value)) { value in 56 | onChange(value) 57 | } 58 | } 59 | } 60 | } 61 | 62 | extension View { 63 | @ViewBuilder 64 | func applyIf(_ condition: Bool, apply: (Self) -> T) -> some View { 65 | if condition { 66 | apply(self) 67 | } else { 68 | self 69 | } 70 | } 71 | 72 | @ViewBuilder 73 | func addTapIfNotTV(if condition: Bool, onTap: @escaping ()->()) -> some View { 74 | #if os(tvOS) 75 | self 76 | #else 77 | if condition { 78 | self.simultaneousGesture( 79 | TapGesture().onEnded { 80 | onTap() 81 | } 82 | ) 83 | } else { 84 | self 85 | } 86 | #endif 87 | } 88 | } 89 | 90 | // MARK: - FrameGetter 91 | 92 | struct FrameGetter: ViewModifier { 93 | 94 | @Binding var frame: CGRect 95 | 96 | func body(content: Content) -> some View { 97 | content 98 | .background( 99 | GeometryReader { proxy -> AnyView in 100 | DispatchQueue.main.async { 101 | let rect = proxy.frame(in: .global) 102 | // This avoids an infinite layout loop 103 | if rect.integral != self.frame.integral { 104 | self.frame = rect 105 | } 106 | } 107 | return AnyView(EmptyView()) 108 | } 109 | ) 110 | } 111 | } 112 | 113 | internal extension View { 114 | func frameGetter(_ frame: Binding) -> some View { 115 | modifier(FrameGetter(frame: frame)) 116 | } 117 | } 118 | 119 | struct SafeAreaGetter: ViewModifier { 120 | 121 | @Binding var safeArea: EdgeInsets 122 | 123 | func body(content: Content) -> some View { 124 | content 125 | .background( 126 | GeometryReader { proxy -> AnyView in 127 | DispatchQueue.main.async { 128 | let area = proxy.safeAreaInsets 129 | // This avoids an infinite layout loop 130 | if area != self.safeArea { 131 | self.safeArea = area 132 | } 133 | } 134 | return AnyView(EmptyView()) 135 | } 136 | ) 137 | } 138 | } 139 | 140 | extension View { 141 | public func safeAreaGetter(_ safeArea: Binding) -> some View { 142 | modifier(SafeAreaGetter(safeArea: safeArea)) 143 | } 144 | } 145 | 146 | // MARK: - TransparentNonAnimatingFullScreenCover 147 | 148 | #if os(iOS) 149 | 150 | extension View { 151 | 152 | func transparentNonAnimatingFullScreenCover( 153 | isPresented: Binding, 154 | dismissSource: DismissSource?, 155 | userDismissCallback: @escaping (DismissSource) -> (), 156 | content: @escaping () -> Content) -> some View { 157 | modifier(TransparentNonAnimatableFullScreenModifier(isPresented: isPresented, dismissSource: dismissSource, userDismissCallback: userDismissCallback, fullScreenContent: content)) 158 | } 159 | } 160 | 161 | private struct TransparentNonAnimatableFullScreenModifier: ViewModifier { 162 | 163 | @Binding var isPresented: Bool 164 | var dismissSource: DismissSource? 165 | var userDismissCallback: (DismissSource) -> () 166 | let fullScreenContent: () -> (FullScreenContent) 167 | 168 | func body(content: Content) -> some View { 169 | content 170 | .onChange(of: isPresented) { isPresented in 171 | UIView.setAnimationsEnabled(false) 172 | } 173 | .fullScreenCover(isPresented: $isPresented) { 174 | ZStack { 175 | fullScreenContent() 176 | } 177 | .background(FullScreenCoverBackgroundRemovalView()) 178 | .onAppear { 179 | if !UIView.areAnimationsEnabled { 180 | UIView.setAnimationsEnabled(true) 181 | } 182 | } 183 | .onDisappear { 184 | userDismissCallback(dismissSource ?? .binding) 185 | if !UIView.areAnimationsEnabled { 186 | UIView.setAnimationsEnabled(true) 187 | } 188 | } 189 | } 190 | } 191 | } 192 | 193 | private struct FullScreenCoverBackgroundRemovalView: UIViewRepresentable { 194 | 195 | private class BackgroundRemovalView: UIView { 196 | override func didMoveToWindow() { 197 | super.didMoveToWindow() 198 | superview?.superview?.backgroundColor = .clear 199 | } 200 | } 201 | 202 | func makeUIView(context: Context) -> UIView { 203 | return BackgroundRemovalView() 204 | } 205 | 206 | func updateUIView(_ uiView: UIView, context: Context) {} 207 | } 208 | 209 | #endif 210 | 211 | // MARK: - WindowManager 212 | 213 | #if os(iOS) 214 | 215 | // A generic wrapper that tracks changes in the view's state 216 | @MainActor 217 | class HostingViewState: ObservableObject { 218 | @Published var content: Content 219 | let id1: UUID 220 | 221 | private var cancellable: AnyCancellable? 222 | 223 | init(content: Content, id: UUID) { 224 | self.content = content 225 | self.id1 = id 226 | // Subscribe to changes in the content and trigger updates 227 | self.cancellable = self.observeStateChanges() 228 | } 229 | 230 | private func observeStateChanges() -> AnyCancellable { 231 | // Observe state changes using Combine 232 | // You can use `Just` here for simplicity, or expand it for more complex needs. 233 | // In real-world cases, this would listen to any changes in the state object passed. 234 | 235 | return Just(content) 236 | .sink { [weak self] newContent in 237 | guard let self else { return } 238 | WindowManager.shared.windows[self.id1]?.rootViewController = UIHostingController(rootView: newContent) 239 | // self?.content = newContent // Trigger the content update when state changes 240 | } 241 | } 242 | } 243 | 244 | @MainActor 245 | public final class WindowManager { 246 | static let shared = WindowManager() 247 | var windows: [UUID: UIWindow] = [:] 248 | 249 | // Show a new window with hosted SwiftUI content 250 | public static func showInNewWindow(id: UUID, dismissClosure: @escaping ()->(), content: @escaping () -> Content) { 251 | guard let scene = UIApplication.shared.connectedScenes.first as? UIWindowScene else { 252 | print("No valid scene available") 253 | return 254 | } 255 | 256 | let window = UIPassthroughWindow(windowScene: scene) 257 | window.backgroundColor = .clear 258 | 259 | let controller = UIPassthroughVC(rootView: content() 260 | .environment(\.popupDismiss) { 261 | dismissClosure() 262 | }) 263 | controller.view.backgroundColor = .clear 264 | window.rootViewController = controller 265 | window.windowLevel = .alert + 1 266 | window.makeKeyAndVisible() 267 | 268 | // Store window reference 269 | shared.windows[id] = window 270 | } 271 | 272 | static func closeWindow(id: UUID) { 273 | shared.windows[id]?.isHidden = true 274 | shared.windows.removeValue(forKey: id) 275 | } 276 | } 277 | 278 | class UIPassthroughWindow: UIWindow { 279 | 280 | override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { 281 | if let vc = self.rootViewController { 282 | vc.view.layoutSubviews() // otherwise the frame is as if the popup is still outside the screen 283 | if let _ = isTouchInsideSubview(point: point, vc: vc.view) { 284 | // pass tap to this UIPassthroughVC 285 | return vc.view 286 | } 287 | } 288 | return nil // pass to next window 289 | } 290 | 291 | private func isTouchInsideSubview(point: CGPoint, vc: UIView) -> UIView? { 292 | for subview in vc.subviews { 293 | if subview.frame.contains(point) { 294 | return subview 295 | } 296 | } 297 | return nil 298 | } 299 | } 300 | 301 | class UIPassthroughVC: UIHostingController { 302 | 303 | override func touchesBegan(_ touches: Set, with event: UIEvent?) { 304 | // Check if any touch is inside one of the subviews, if so, ignore it 305 | if !isTouchInsideSubview(touches) { 306 | // If touch is not inside any subview, pass the touch to the next responder 307 | super.touchesBegan(touches, with: event) 308 | } 309 | } 310 | 311 | override func touchesMoved(_ touches: Set, with event: UIEvent?) { 312 | if !isTouchInsideSubview(touches) { 313 | super.touchesMoved(touches, with: event) 314 | } 315 | } 316 | 317 | override func touchesEnded(_ touches: Set, with event: UIEvent?) { 318 | if !isTouchInsideSubview(touches) { 319 | super.touchesEnded(touches, with: event) 320 | } 321 | } 322 | 323 | override func touchesCancelled(_ touches: Set, with event: UIEvent?) { 324 | if !isTouchInsideSubview(touches) { 325 | super.touchesCancelled(touches, with: event) 326 | } 327 | } 328 | 329 | // Helper function to determine if any touch is inside a subview 330 | private func isTouchInsideSubview(_ touches: Set) -> Bool { 331 | guard let touch = touches.first else { 332 | return false 333 | } 334 | 335 | let touchLocation = touch.location(in: self.view) 336 | 337 | // Iterate over all subviews to check if the touch is inside any of them 338 | for subview in self.view.subviews { 339 | if subview.frame.contains(touchLocation) { 340 | return true 341 | } 342 | } 343 | return false 344 | } 345 | } 346 | #endif 347 | 348 | // MARK: - KeyboardHeightHelper 349 | 350 | #if os(iOS) 351 | 352 | @MainActor 353 | class KeyboardHeightHelper: ObservableObject { 354 | 355 | @Published var keyboardHeight: CGFloat = 0 356 | @Published var keyboardDisplayed: Bool = false 357 | 358 | init() { 359 | NotificationCenter.default.addObserver(self, selector: #selector(onKeyboardWillShowNotification), name: UIResponder.keyboardWillShowNotification, object: nil) 360 | NotificationCenter.default.addObserver(self, selector: #selector(onKeyboardWillHideNotification), name: UIResponder.keyboardWillHideNotification, object: nil) 361 | } 362 | 363 | deinit { 364 | NotificationCenter.default.removeObserver(self) 365 | } 366 | 367 | @objc private func onKeyboardWillShowNotification(_ notification: Notification) { 368 | guard let userInfo = notification.userInfo, 369 | let keyboardRect = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect else { return } 370 | 371 | DispatchQueue.main.async { 372 | self.keyboardHeight = keyboardRect.height 373 | self.keyboardDisplayed = true 374 | } 375 | } 376 | 377 | @objc private func onKeyboardWillHideNotification(_ notification: Notification) { 378 | DispatchQueue.main.async { 379 | self.keyboardHeight = 0 380 | self.keyboardDisplayed = false 381 | } 382 | } 383 | } 384 | 385 | #else 386 | 387 | class KeyboardHeightHelper: ObservableObject { 388 | 389 | @Published var keyboardHeight: CGFloat = 0 390 | @Published var keyboardDisplayed: Bool = false 391 | } 392 | 393 | #endif 394 | 395 | 396 | // MARK: - Hide keyboard 397 | 398 | extension CGPoint { 399 | 400 | @MainActor 401 | static var pointFarAwayFromScreen: CGPoint { 402 | CGPoint(x: 2*CGSize.screenSize.width, y: 2*CGSize.screenSize.height) 403 | } 404 | } 405 | 406 | extension CGSize { 407 | 408 | @MainActor 409 | static var screenSize: CGSize { 410 | #if os(iOS) || os(tvOS) 411 | return UIScreen.main.bounds.size 412 | #elseif os(watchOS) 413 | return WKInterfaceDevice.current().screenBounds.size 414 | #elseif os(macOS) 415 | return NSScreen.main?.frame.size ?? .zero 416 | #elseif os(visionOS) 417 | return .zero 418 | #endif 419 | } 420 | } 421 | --------------------------------------------------------------------------------