├── .gitignore
├── AnchoredPopupExample
├── AnchoredPopupExample.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ └── xcshareddata
│ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ └── WorkspaceSettings.xcsettings
│ └── xcshareddata
│ │ └── xcschemes
│ │ └── AnchoredPopupExample.xcscheme
└── AnchoredPopupExample
│ ├── AnchoredPopupExample.entitlements
│ ├── AnchoredPopupExampleApp.swift
│ ├── Assets.xcassets
│ ├── AccentColor.colorset
│ │ └── Contents.json
│ ├── Colors
│ │ ├── Contents.json
│ │ ├── popupAzureishWhite.colorset
│ │ │ └── Contents.json
│ │ ├── popupCornflowerBlue.colorset
│ │ │ └── Contents.json
│ │ ├── popupDarkViolet.colorset
│ │ │ └── Contents.json
│ │ ├── popupGray1.colorset
│ │ │ └── Contents.json
│ │ ├── popupGray2.colorset
│ │ │ └── Contents.json
│ │ ├── popupGray3.colorset
│ │ │ └── Contents.json
│ │ ├── popupGray5.colorset
│ │ │ └── Contents.json
│ │ ├── popupLavender.colorset
│ │ │ └── Contents.json
│ │ ├── popupLightBlue.colorset
│ │ │ └── Contents.json
│ │ ├── popupLightGreen.colorset
│ │ │ └── Contents.json
│ │ ├── popupLightPeriwinkle.colorset
│ │ │ └── Contents.json
│ │ ├── popupMenthol.colorset
│ │ │ └── Contents.json
│ │ ├── popupSeparator.colorset
│ │ │ └── Contents.json
│ │ ├── popupViolet.colorset
│ │ │ └── Contents.json
│ │ ├── popupYellow.colorset
│ │ │ └── Contents.json
│ │ └── popupYellow2.colorset
│ │ │ └── Contents.json
│ ├── Contents.json
│ ├── arrowRight.imageset
│ │ ├── Contents.json
│ │ └── Vector.pdf
│ ├── avatar.imageset
│ │ ├── Contents.json
│ │ └── Rectangle 62.pdf
│ ├── congratulations.imageset
│ │ ├── Contents.json
│ │ └── Winner 1.pdf
│ ├── cross.imageset
│ │ ├── Contents.json
│ │ └── Vector.pdf
│ ├── gift.imageset
│ │ ├── Contents.json
│ │ └── image 5.pdf
│ ├── giftBG.imageset
│ │ ├── Contents.json
│ │ └── Ellipse 26.pdf
│ ├── girl1.imageset
│ │ ├── Contents.json
│ │ └── oksana-taran-xB4ExGcUai0-unsplash-removebg-preview 1.pdf
│ ├── girl2.imageset
│ │ ├── Contents.json
│ │ └── clem-onojeghuo-n6gnCa77Urc-unsplash-removebg-preview 1.pdf
│ ├── mainMenuCamera.imageset
│ │ ├── Contents.json
│ │ └── Videocamera.pdf
│ ├── mainMenuDumbbell.imageset
│ │ ├── Contents.json
│ │ └── Dumbbell.pdf
│ ├── mainMenuLibrary.imageset
│ │ ├── Contents.json
│ │ └── Video Library.pdf
│ ├── mainPlay.imageset
│ │ ├── Contents.json
│ │ └── Polygon 1.pdf
│ ├── mainPlaySmall.imageset
│ │ ├── Contents.json
│ │ └── Play.pdf
│ ├── mainTrophy.imageset
│ │ ├── Contents.json
│ │ └── Vector.pdf
│ ├── navBack.imageset
│ │ ├── Contents.json
│ │ └── Left Accessory Button.pdf
│ ├── profile.imageset
│ │ ├── Contents.json
│ │ └── image.pdf
│ ├── profileFavorites.imageset
│ │ ├── Contents.json
│ │ └── Heart.pdf
│ ├── profileTicket.imageset
│ │ ├── Contents.json
│ │ └── Vector.pdf
│ ├── profileTicketBG.imageset
│ │ ├── Contents.json
│ │ └── Group.pdf
│ ├── profileWorkout.imageset
│ │ ├── Contents.json
│ │ └── Dumbbell.pdf
│ ├── question.imageset
│ │ ├── Contents.json
│ │ └── Question Circle.pdf
│ └── xmark.imageset
│ │ ├── Contents.json
│ │ └── Vector.pdf
│ ├── Fonts
│ ├── ChakraPetch-Light.ttf
│ ├── ChakraPetch-Medium.ttf
│ ├── ChakraPetch-Regular.ttf
│ ├── IBMPlexMono-Bold.ttf
│ ├── IBMPlexMono-Medium.ttf
│ └── IBMPlexMono-Regular.ttf
│ ├── Info.plist
│ ├── Preview Content
│ └── Preview Assets.xcassets
│ │ └── Contents.json
│ ├── Screens
│ ├── MainView.swift
│ ├── Popups.swift
│ └── ProfileView.swift
│ └── Utils
│ ├── FontUtils.swift
│ └── Utils.swift
├── LICENSE
├── Package.swift
├── README.md
└── Sources
└── AnchoredPopup
├── AnchoredAnimationManager.swift
├── AnchoredPopup.swift
├── BlurBackdropView.swift
├── PublicAPI.swift
├── Utils.swift
└── WindowManager.swift
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | /.build
3 | /Packages
4 | xcuserdata/
5 | DerivedData/
6 | .swiftpm/configuration/registries.json
7 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
8 | .netrc
9 |
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 63;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 5B1858D62D51FD3A00625A0F /* AnchoredPopup in Frameworks */ = {isa = PBXBuildFile; productRef = 5B1858D52D51FD3A00625A0F /* AnchoredPopup */; };
11 | 5B4929972D673CC9002D045E /* ChakraPetch-Light.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 5B4929962D673CC9002D045E /* ChakraPetch-Light.ttf */; };
12 | 5B4D04F82D92F9A8005262A1 /* AnchoredPopup in Frameworks */ = {isa = PBXBuildFile; productRef = 5B4D04F72D92F9A8005262A1 /* AnchoredPopup */; };
13 | 5B4DBAD32D92F74A0067A006 /* AnchoredPopup in Frameworks */ = {isa = PBXBuildFile; productRef = 5B4DBAD22D92F74A0067A006 /* AnchoredPopup */; };
14 | 5B55B9D52D9A6E71004A31C0 /* AnchoredPopup in Frameworks */ = {isa = PBXBuildFile; productRef = 5B55B9D42D9A6E71004A31C0 /* AnchoredPopup */; };
15 | 5B87F2BB2D3FAE7600A1D1FA /* AnchoredPopup in Frameworks */ = {isa = PBXBuildFile; productRef = 5B87F2BA2D3FAE7600A1D1FA /* AnchoredPopup */; };
16 | 5B87F2BE2D3FAEE400A1D1FA /* AnchoredPopup in Frameworks */ = {isa = PBXBuildFile; productRef = 5B87F2BD2D3FAEE400A1D1FA /* AnchoredPopup */; };
17 | 5B87F2C12D3FB04A00A1D1FA /* AnchoredPopup in Frameworks */ = {isa = PBXBuildFile; productRef = 5B87F2C02D3FB04A00A1D1FA /* AnchoredPopup */; };
18 | 5B87F2C42D3FB0D100A1D1FA /* AnchoredPopup in Frameworks */ = {isa = PBXBuildFile; productRef = 5B87F2C32D3FB0D100A1D1FA /* AnchoredPopup */; };
19 | 5BACA4352D64806C0093E0CF /* AnchoredPopup in Frameworks */ = {isa = PBXBuildFile; productRef = 5BACA4342D64806C0093E0CF /* AnchoredPopup */; };
20 | 5BACA4492D64808A0093E0CF /* IBMPlexMono-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 5BACA43A2D64808A0093E0CF /* IBMPlexMono-Regular.ttf */; };
21 | 5BACA44A2D64808A0093E0CF /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5BACA43C2D64808A0093E0CF /* Preview Assets.xcassets */; };
22 | 5BACA44C2D64808A0093E0CF /* IBMPlexMono-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 5BACA4382D64808A0093E0CF /* IBMPlexMono-Bold.ttf */; };
23 | 5BACA44D2D64808A0093E0CF /* ChakraPetch-Medium.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 5BACA4362D64808A0093E0CF /* ChakraPetch-Medium.ttf */; };
24 | 5BACA44E2D64808A0093E0CF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5BACA4462D64808A0093E0CF /* Assets.xcassets */; };
25 | 5BACA44F2D64808A0093E0CF /* ChakraPetch-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 5BACA4372D64808A0093E0CF /* ChakraPetch-Regular.ttf */; };
26 | 5BACA4502D64808A0093E0CF /* IBMPlexMono-Medium.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 5BACA4392D64808A0093E0CF /* IBMPlexMono-Medium.ttf */; };
27 | 5BACA4512D64808A0093E0CF /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BACA4432D64808A0093E0CF /* Utils.swift */; };
28 | 5BACA4522D64808A0093E0CF /* AnchoredPopupExampleApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BACA4452D64808A0093E0CF /* AnchoredPopupExampleApp.swift */; };
29 | 5BACA4532D64808A0093E0CF /* Popups.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BACA43F2D64808A0093E0CF /* Popups.swift */; };
30 | 5BACA4542D64808A0093E0CF /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BACA43E2D64808A0093E0CF /* MainView.swift */; };
31 | 5BACA4552D64808A0093E0CF /* FontUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BACA4422D64808A0093E0CF /* FontUtils.swift */; };
32 | 5BACA4562D64808A0093E0CF /* ProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BACA4402D64808A0093E0CF /* ProfileView.swift */; };
33 | 5BB5D7002D647EC900D26B4D /* AnchoredPopup in Frameworks */ = {isa = PBXBuildFile; productRef = 5BB5D6FF2D647EC900D26B4D /* AnchoredPopup */; };
34 | /* End PBXBuildFile section */
35 |
36 | /* Begin PBXFileReference section */
37 | 5B4929962D673CC9002D045E /* ChakraPetch-Light.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "ChakraPetch-Light.ttf"; sourceTree = ""; };
38 | 5B87F2A22D3FA2B300A1D1FA /* AnchoredPopupExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AnchoredPopupExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
39 | 5BACA4362D64808A0093E0CF /* ChakraPetch-Medium.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "ChakraPetch-Medium.ttf"; sourceTree = ""; };
40 | 5BACA4372D64808A0093E0CF /* ChakraPetch-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "ChakraPetch-Regular.ttf"; sourceTree = ""; };
41 | 5BACA4382D64808A0093E0CF /* IBMPlexMono-Bold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "IBMPlexMono-Bold.ttf"; sourceTree = ""; };
42 | 5BACA4392D64808A0093E0CF /* IBMPlexMono-Medium.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "IBMPlexMono-Medium.ttf"; sourceTree = ""; };
43 | 5BACA43A2D64808A0093E0CF /* IBMPlexMono-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "IBMPlexMono-Regular.ttf"; sourceTree = ""; };
44 | 5BACA43C2D64808A0093E0CF /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; };
45 | 5BACA43E2D64808A0093E0CF /* MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = ""; };
46 | 5BACA43F2D64808A0093E0CF /* Popups.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Popups.swift; sourceTree = ""; };
47 | 5BACA4402D64808A0093E0CF /* ProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileView.swift; sourceTree = ""; };
48 | 5BACA4422D64808A0093E0CF /* FontUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FontUtils.swift; sourceTree = ""; };
49 | 5BACA4432D64808A0093E0CF /* Utils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utils.swift; sourceTree = ""; };
50 | 5BACA4452D64808A0093E0CF /* AnchoredPopupExampleApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnchoredPopupExampleApp.swift; sourceTree = ""; };
51 | 5BACA4462D64808A0093E0CF /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
52 | 5BACA4472D64808A0093E0CF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
53 | /* End PBXFileReference section */
54 |
55 | /* Begin PBXFrameworksBuildPhase section */
56 | 5B87F29F2D3FA2B300A1D1FA /* Frameworks */ = {
57 | isa = PBXFrameworksBuildPhase;
58 | buildActionMask = 2147483647;
59 | files = (
60 | 5B87F2BB2D3FAE7600A1D1FA /* AnchoredPopup in Frameworks */,
61 | 5B87F2C42D3FB0D100A1D1FA /* AnchoredPopup in Frameworks */,
62 | 5B1858D62D51FD3A00625A0F /* AnchoredPopup in Frameworks */,
63 | 5B87F2BE2D3FAEE400A1D1FA /* AnchoredPopup in Frameworks */,
64 | 5BACA4352D64806C0093E0CF /* AnchoredPopup in Frameworks */,
65 | 5B4DBAD32D92F74A0067A006 /* AnchoredPopup in Frameworks */,
66 | 5B87F2C12D3FB04A00A1D1FA /* AnchoredPopup in Frameworks */,
67 | 5B55B9D52D9A6E71004A31C0 /* AnchoredPopup in Frameworks */,
68 | 5BB5D7002D647EC900D26B4D /* AnchoredPopup in Frameworks */,
69 | 5B4D04F82D92F9A8005262A1 /* AnchoredPopup in Frameworks */,
70 | );
71 | runOnlyForDeploymentPostprocessing = 0;
72 | };
73 | /* End PBXFrameworksBuildPhase section */
74 |
75 | /* Begin PBXGroup section */
76 | 5B87F2992D3FA2B300A1D1FA = {
77 | isa = PBXGroup;
78 | children = (
79 | 5BACA4482D64808A0093E0CF /* AnchoredPopupExample */,
80 | 5B87F2A32D3FA2B300A1D1FA /* Products */,
81 | );
82 | sourceTree = "";
83 | };
84 | 5B87F2A32D3FA2B300A1D1FA /* Products */ = {
85 | isa = PBXGroup;
86 | children = (
87 | 5B87F2A22D3FA2B300A1D1FA /* AnchoredPopupExample.app */,
88 | );
89 | name = Products;
90 | sourceTree = "";
91 | };
92 | 5BACA43B2D64808A0093E0CF /* Fonts */ = {
93 | isa = PBXGroup;
94 | children = (
95 | 5B4929962D673CC9002D045E /* ChakraPetch-Light.ttf */,
96 | 5BACA4362D64808A0093E0CF /* ChakraPetch-Medium.ttf */,
97 | 5BACA4372D64808A0093E0CF /* ChakraPetch-Regular.ttf */,
98 | 5BACA4382D64808A0093E0CF /* IBMPlexMono-Bold.ttf */,
99 | 5BACA4392D64808A0093E0CF /* IBMPlexMono-Medium.ttf */,
100 | 5BACA43A2D64808A0093E0CF /* IBMPlexMono-Regular.ttf */,
101 | );
102 | path = Fonts;
103 | sourceTree = "";
104 | };
105 | 5BACA43D2D64808A0093E0CF /* Preview Content */ = {
106 | isa = PBXGroup;
107 | children = (
108 | 5BACA43C2D64808A0093E0CF /* Preview Assets.xcassets */,
109 | );
110 | path = "Preview Content";
111 | sourceTree = "";
112 | };
113 | 5BACA4412D64808A0093E0CF /* Screens */ = {
114 | isa = PBXGroup;
115 | children = (
116 | 5BACA43E2D64808A0093E0CF /* MainView.swift */,
117 | 5BACA43F2D64808A0093E0CF /* Popups.swift */,
118 | 5BACA4402D64808A0093E0CF /* ProfileView.swift */,
119 | );
120 | path = Screens;
121 | sourceTree = "";
122 | };
123 | 5BACA4442D64808A0093E0CF /* Utils */ = {
124 | isa = PBXGroup;
125 | children = (
126 | 5BACA4422D64808A0093E0CF /* FontUtils.swift */,
127 | 5BACA4432D64808A0093E0CF /* Utils.swift */,
128 | );
129 | path = Utils;
130 | sourceTree = "";
131 | };
132 | 5BACA4482D64808A0093E0CF /* AnchoredPopupExample */ = {
133 | isa = PBXGroup;
134 | children = (
135 | 5BACA43B2D64808A0093E0CF /* Fonts */,
136 | 5BACA43D2D64808A0093E0CF /* Preview Content */,
137 | 5BACA4412D64808A0093E0CF /* Screens */,
138 | 5BACA4442D64808A0093E0CF /* Utils */,
139 | 5BACA4452D64808A0093E0CF /* AnchoredPopupExampleApp.swift */,
140 | 5BACA4462D64808A0093E0CF /* Assets.xcassets */,
141 | 5BACA4472D64808A0093E0CF /* Info.plist */,
142 | );
143 | path = AnchoredPopupExample;
144 | sourceTree = "";
145 | };
146 | /* End PBXGroup section */
147 |
148 | /* Begin PBXNativeTarget section */
149 | 5B87F2A12D3FA2B300A1D1FA /* AnchoredPopupExample */ = {
150 | isa = PBXNativeTarget;
151 | buildConfigurationList = 5B87F2B02D3FA2B400A1D1FA /* Build configuration list for PBXNativeTarget "AnchoredPopupExample" */;
152 | buildPhases = (
153 | 5B87F29E2D3FA2B300A1D1FA /* Sources */,
154 | 5B87F29F2D3FA2B300A1D1FA /* Frameworks */,
155 | 5B87F2A02D3FA2B300A1D1FA /* Resources */,
156 | );
157 | buildRules = (
158 | );
159 | dependencies = (
160 | );
161 | name = AnchoredPopupExample;
162 | packageProductDependencies = (
163 | 5B87F2BA2D3FAE7600A1D1FA /* AnchoredPopup */,
164 | 5B87F2BD2D3FAEE400A1D1FA /* AnchoredPopup */,
165 | 5B87F2C02D3FB04A00A1D1FA /* AnchoredPopup */,
166 | 5B87F2C32D3FB0D100A1D1FA /* AnchoredPopup */,
167 | 5B1858D52D51FD3A00625A0F /* AnchoredPopup */,
168 | 5BB5D6FF2D647EC900D26B4D /* AnchoredPopup */,
169 | 5BACA4342D64806C0093E0CF /* AnchoredPopup */,
170 | 5B4DBAD22D92F74A0067A006 /* AnchoredPopup */,
171 | 5B4D04F72D92F9A8005262A1 /* AnchoredPopup */,
172 | 5B55B9D42D9A6E71004A31C0 /* AnchoredPopup */,
173 | );
174 | productName = AnchoredPopupExample;
175 | productReference = 5B87F2A22D3FA2B300A1D1FA /* AnchoredPopupExample.app */;
176 | productType = "com.apple.product-type.application";
177 | };
178 | /* End PBXNativeTarget section */
179 |
180 | /* Begin PBXProject section */
181 | 5B87F29A2D3FA2B300A1D1FA /* Project object */ = {
182 | isa = PBXProject;
183 | attributes = {
184 | BuildIndependentTargetsInParallel = 1;
185 | LastSwiftUpdateCheck = 1600;
186 | LastUpgradeCheck = 1600;
187 | TargetAttributes = {
188 | 5B87F2A12D3FA2B300A1D1FA = {
189 | CreatedOnToolsVersion = 16.0;
190 | };
191 | };
192 | };
193 | buildConfigurationList = 5B87F29D2D3FA2B300A1D1FA /* Build configuration list for PBXProject "AnchoredPopupExample" */;
194 | compatibilityVersion = "Xcode 15.0";
195 | developmentRegion = en;
196 | hasScannedForEncodings = 0;
197 | knownRegions = (
198 | en,
199 | Base,
200 | );
201 | mainGroup = 5B87F2992D3FA2B300A1D1FA;
202 | minimizedProjectReferenceProxies = 1;
203 | packageReferences = (
204 | 5B55B9D32D9A6E71004A31C0 /* XCLocalSwiftPackageReference "../../AnchoredPopup" */,
205 | );
206 | productRefGroup = 5B87F2A32D3FA2B300A1D1FA /* Products */;
207 | projectDirPath = "";
208 | projectRoot = "";
209 | targets = (
210 | 5B87F2A12D3FA2B300A1D1FA /* AnchoredPopupExample */,
211 | );
212 | };
213 | /* End PBXProject section */
214 |
215 | /* Begin PBXResourcesBuildPhase section */
216 | 5B87F2A02D3FA2B300A1D1FA /* Resources */ = {
217 | isa = PBXResourcesBuildPhase;
218 | buildActionMask = 2147483647;
219 | files = (
220 | 5BACA4492D64808A0093E0CF /* IBMPlexMono-Regular.ttf in Resources */,
221 | 5BACA44A2D64808A0093E0CF /* Preview Assets.xcassets in Resources */,
222 | 5BACA44C2D64808A0093E0CF /* IBMPlexMono-Bold.ttf in Resources */,
223 | 5BACA44D2D64808A0093E0CF /* ChakraPetch-Medium.ttf in Resources */,
224 | 5B4929972D673CC9002D045E /* ChakraPetch-Light.ttf in Resources */,
225 | 5BACA44E2D64808A0093E0CF /* Assets.xcassets in Resources */,
226 | 5BACA44F2D64808A0093E0CF /* ChakraPetch-Regular.ttf in Resources */,
227 | 5BACA4502D64808A0093E0CF /* IBMPlexMono-Medium.ttf in Resources */,
228 | );
229 | runOnlyForDeploymentPostprocessing = 0;
230 | };
231 | /* End PBXResourcesBuildPhase section */
232 |
233 | /* Begin PBXSourcesBuildPhase section */
234 | 5B87F29E2D3FA2B300A1D1FA /* Sources */ = {
235 | isa = PBXSourcesBuildPhase;
236 | buildActionMask = 2147483647;
237 | files = (
238 | 5BACA4512D64808A0093E0CF /* Utils.swift in Sources */,
239 | 5BACA4522D64808A0093E0CF /* AnchoredPopupExampleApp.swift in Sources */,
240 | 5BACA4532D64808A0093E0CF /* Popups.swift in Sources */,
241 | 5BACA4542D64808A0093E0CF /* MainView.swift in Sources */,
242 | 5BACA4552D64808A0093E0CF /* FontUtils.swift in Sources */,
243 | 5BACA4562D64808A0093E0CF /* ProfileView.swift in Sources */,
244 | );
245 | runOnlyForDeploymentPostprocessing = 0;
246 | };
247 | /* End PBXSourcesBuildPhase section */
248 |
249 | /* Begin XCBuildConfiguration section */
250 | 5B87F2AE2D3FA2B400A1D1FA /* Debug */ = {
251 | isa = XCBuildConfiguration;
252 | buildSettings = {
253 | ALWAYS_SEARCH_USER_PATHS = NO;
254 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
255 | CLANG_ANALYZER_NONNULL = YES;
256 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
257 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
258 | CLANG_ENABLE_MODULES = YES;
259 | CLANG_ENABLE_OBJC_ARC = YES;
260 | CLANG_ENABLE_OBJC_WEAK = YES;
261 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
262 | CLANG_WARN_BOOL_CONVERSION = YES;
263 | CLANG_WARN_COMMA = YES;
264 | CLANG_WARN_CONSTANT_CONVERSION = YES;
265 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
266 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
267 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
268 | CLANG_WARN_EMPTY_BODY = YES;
269 | CLANG_WARN_ENUM_CONVERSION = YES;
270 | CLANG_WARN_INFINITE_RECURSION = YES;
271 | CLANG_WARN_INT_CONVERSION = YES;
272 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
273 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
274 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
275 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
276 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
277 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
278 | CLANG_WARN_STRICT_PROTOTYPES = YES;
279 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
280 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
281 | CLANG_WARN_UNREACHABLE_CODE = YES;
282 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
283 | COPY_PHASE_STRIP = NO;
284 | DEBUG_INFORMATION_FORMAT = dwarf;
285 | ENABLE_STRICT_OBJC_MSGSEND = YES;
286 | ENABLE_TESTABILITY = YES;
287 | ENABLE_USER_SCRIPT_SANDBOXING = YES;
288 | GCC_C_LANGUAGE_STANDARD = gnu17;
289 | GCC_DYNAMIC_NO_PIC = NO;
290 | GCC_NO_COMMON_BLOCKS = YES;
291 | GCC_OPTIMIZATION_LEVEL = 0;
292 | GCC_PREPROCESSOR_DEFINITIONS = (
293 | "DEBUG=1",
294 | "$(inherited)",
295 | );
296 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
297 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
298 | GCC_WARN_UNDECLARED_SELECTOR = YES;
299 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
300 | GCC_WARN_UNUSED_FUNCTION = YES;
301 | GCC_WARN_UNUSED_VARIABLE = YES;
302 | IPHONEOS_DEPLOYMENT_TARGET = 18.0;
303 | LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
304 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
305 | MTL_FAST_MATH = YES;
306 | ONLY_ACTIVE_ARCH = YES;
307 | SDKROOT = iphoneos;
308 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
309 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
310 | SWIFT_VERSION = 6.0;
311 | };
312 | name = Debug;
313 | };
314 | 5B87F2AF2D3FA2B400A1D1FA /* Release */ = {
315 | isa = XCBuildConfiguration;
316 | buildSettings = {
317 | ALWAYS_SEARCH_USER_PATHS = NO;
318 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
319 | CLANG_ANALYZER_NONNULL = YES;
320 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
321 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
322 | CLANG_ENABLE_MODULES = YES;
323 | CLANG_ENABLE_OBJC_ARC = YES;
324 | CLANG_ENABLE_OBJC_WEAK = YES;
325 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
326 | CLANG_WARN_BOOL_CONVERSION = YES;
327 | CLANG_WARN_COMMA = YES;
328 | CLANG_WARN_CONSTANT_CONVERSION = YES;
329 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
330 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
331 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
332 | CLANG_WARN_EMPTY_BODY = YES;
333 | CLANG_WARN_ENUM_CONVERSION = YES;
334 | CLANG_WARN_INFINITE_RECURSION = YES;
335 | CLANG_WARN_INT_CONVERSION = YES;
336 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
337 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
338 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
339 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
340 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
341 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
342 | CLANG_WARN_STRICT_PROTOTYPES = YES;
343 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
344 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
345 | CLANG_WARN_UNREACHABLE_CODE = YES;
346 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
347 | COPY_PHASE_STRIP = NO;
348 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
349 | ENABLE_NS_ASSERTIONS = NO;
350 | ENABLE_STRICT_OBJC_MSGSEND = YES;
351 | ENABLE_USER_SCRIPT_SANDBOXING = YES;
352 | GCC_C_LANGUAGE_STANDARD = gnu17;
353 | GCC_NO_COMMON_BLOCKS = YES;
354 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
355 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
356 | GCC_WARN_UNDECLARED_SELECTOR = YES;
357 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
358 | GCC_WARN_UNUSED_FUNCTION = YES;
359 | GCC_WARN_UNUSED_VARIABLE = YES;
360 | IPHONEOS_DEPLOYMENT_TARGET = 18.0;
361 | LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
362 | MTL_ENABLE_DEBUG_INFO = NO;
363 | MTL_FAST_MATH = YES;
364 | SDKROOT = iphoneos;
365 | SWIFT_COMPILATION_MODE = wholemodule;
366 | SWIFT_VERSION = 6.0;
367 | VALIDATE_PRODUCT = YES;
368 | };
369 | name = Release;
370 | };
371 | 5B87F2B12D3FA2B400A1D1FA /* Debug */ = {
372 | isa = XCBuildConfiguration;
373 | buildSettings = {
374 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
375 | CODE_SIGN_STYLE = Automatic;
376 | CURRENT_PROJECT_VERSION = 1;
377 | DEVELOPMENT_ASSET_PATHS = "\"AnchoredPopupExample/Preview Content\"";
378 | DEVELOPMENT_TEAM = FZXCM5CJ7P;
379 | ENABLE_PREVIEWS = YES;
380 | GENERATE_INFOPLIST_FILE = YES;
381 | INFOPLIST_FILE = AnchoredPopupExample/Info.plist;
382 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
383 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
384 | INFOPLIST_KEY_UILaunchScreen_Generation = YES;
385 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
386 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
387 | IPHONEOS_DEPLOYMENT_TARGET = 17.0;
388 | LD_RUNPATH_SEARCH_PATHS = (
389 | "$(inherited)",
390 | "@executable_path/Frameworks",
391 | );
392 | MARKETING_VERSION = 1.0;
393 | PRODUCT_BUNDLE_IDENTIFIER = com.exyte.AnchoredPopupExample;
394 | PRODUCT_NAME = "$(TARGET_NAME)";
395 | SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
396 | SUPPORTS_MACCATALYST = NO;
397 | SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
398 | SWIFT_EMIT_LOC_STRINGS = YES;
399 | SWIFT_VERSION = 6.0;
400 | TARGETED_DEVICE_FAMILY = "1,2";
401 | };
402 | name = Debug;
403 | };
404 | 5B87F2B22D3FA2B400A1D1FA /* Release */ = {
405 | isa = XCBuildConfiguration;
406 | buildSettings = {
407 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
408 | CODE_SIGN_STYLE = Automatic;
409 | CURRENT_PROJECT_VERSION = 1;
410 | DEVELOPMENT_ASSET_PATHS = "\"AnchoredPopupExample/Preview Content\"";
411 | DEVELOPMENT_TEAM = FZXCM5CJ7P;
412 | ENABLE_PREVIEWS = YES;
413 | GENERATE_INFOPLIST_FILE = YES;
414 | INFOPLIST_FILE = AnchoredPopupExample/Info.plist;
415 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
416 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
417 | INFOPLIST_KEY_UILaunchScreen_Generation = YES;
418 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
419 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
420 | IPHONEOS_DEPLOYMENT_TARGET = 17.0;
421 | LD_RUNPATH_SEARCH_PATHS = (
422 | "$(inherited)",
423 | "@executable_path/Frameworks",
424 | );
425 | MARKETING_VERSION = 1.0;
426 | PRODUCT_BUNDLE_IDENTIFIER = com.exyte.AnchoredPopupExample;
427 | PRODUCT_NAME = "$(TARGET_NAME)";
428 | SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
429 | SUPPORTS_MACCATALYST = NO;
430 | SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
431 | SWIFT_EMIT_LOC_STRINGS = YES;
432 | SWIFT_VERSION = 6.0;
433 | TARGETED_DEVICE_FAMILY = "1,2";
434 | };
435 | name = Release;
436 | };
437 | /* End XCBuildConfiguration section */
438 |
439 | /* Begin XCConfigurationList section */
440 | 5B87F29D2D3FA2B300A1D1FA /* Build configuration list for PBXProject "AnchoredPopupExample" */ = {
441 | isa = XCConfigurationList;
442 | buildConfigurations = (
443 | 5B87F2AE2D3FA2B400A1D1FA /* Debug */,
444 | 5B87F2AF2D3FA2B400A1D1FA /* Release */,
445 | );
446 | defaultConfigurationIsVisible = 0;
447 | defaultConfigurationName = Release;
448 | };
449 | 5B87F2B02D3FA2B400A1D1FA /* Build configuration list for PBXNativeTarget "AnchoredPopupExample" */ = {
450 | isa = XCConfigurationList;
451 | buildConfigurations = (
452 | 5B87F2B12D3FA2B400A1D1FA /* Debug */,
453 | 5B87F2B22D3FA2B400A1D1FA /* Release */,
454 | );
455 | defaultConfigurationIsVisible = 0;
456 | defaultConfigurationName = Release;
457 | };
458 | /* End XCConfigurationList section */
459 |
460 | /* Begin XCLocalSwiftPackageReference section */
461 | 5B55B9D32D9A6E71004A31C0 /* XCLocalSwiftPackageReference "../../AnchoredPopup" */ = {
462 | isa = XCLocalSwiftPackageReference;
463 | relativePath = ../../AnchoredPopup;
464 | };
465 | /* End XCLocalSwiftPackageReference section */
466 |
467 | /* Begin XCSwiftPackageProductDependency section */
468 | 5B1858D52D51FD3A00625A0F /* AnchoredPopup */ = {
469 | isa = XCSwiftPackageProductDependency;
470 | productName = AnchoredPopup;
471 | };
472 | 5B4D04F72D92F9A8005262A1 /* AnchoredPopup */ = {
473 | isa = XCSwiftPackageProductDependency;
474 | productName = AnchoredPopup;
475 | };
476 | 5B4DBAD22D92F74A0067A006 /* AnchoredPopup */ = {
477 | isa = XCSwiftPackageProductDependency;
478 | productName = AnchoredPopup;
479 | };
480 | 5B55B9D42D9A6E71004A31C0 /* AnchoredPopup */ = {
481 | isa = XCSwiftPackageProductDependency;
482 | productName = AnchoredPopup;
483 | };
484 | 5B87F2BA2D3FAE7600A1D1FA /* AnchoredPopup */ = {
485 | isa = XCSwiftPackageProductDependency;
486 | productName = AnchoredPopup;
487 | };
488 | 5B87F2BD2D3FAEE400A1D1FA /* AnchoredPopup */ = {
489 | isa = XCSwiftPackageProductDependency;
490 | productName = AnchoredPopup;
491 | };
492 | 5B87F2C02D3FB04A00A1D1FA /* AnchoredPopup */ = {
493 | isa = XCSwiftPackageProductDependency;
494 | productName = AnchoredPopup;
495 | };
496 | 5B87F2C32D3FB0D100A1D1FA /* AnchoredPopup */ = {
497 | isa = XCSwiftPackageProductDependency;
498 | productName = AnchoredPopup;
499 | };
500 | 5BACA4342D64806C0093E0CF /* AnchoredPopup */ = {
501 | isa = XCSwiftPackageProductDependency;
502 | productName = AnchoredPopup;
503 | };
504 | 5BB5D6FF2D647EC900D26B4D /* AnchoredPopup */ = {
505 | isa = XCSwiftPackageProductDependency;
506 | productName = AnchoredPopup;
507 | };
508 | /* End XCSwiftPackageProductDependency section */
509 | };
510 | rootObject = 5B87F29A2D3FA2B300A1D1FA /* Project object */;
511 | }
512 |
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample.xcodeproj/xcshareddata/xcschemes/AnchoredPopupExample.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
9 |
10 |
16 |
22 |
23 |
24 |
30 |
36 |
37 |
38 |
39 |
40 |
46 |
47 |
57 |
59 |
65 |
66 |
67 |
68 |
74 |
76 |
82 |
83 |
84 |
85 |
87 |
88 |
91 |
92 |
93 |
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/AnchoredPopupExample.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 |
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/AnchoredPopupExampleApp.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AnchoredPopupExampleApp.swift
3 | // AnchoredPopupExample
4 | //
5 | // Created by Alisa Mylnikova on 21.01.2025.
6 | //
7 |
8 | import SwiftUI
9 |
10 | @main
11 | struct AnchoredPopupExampleApp: App {
12 | var body: some Scene {
13 | WindowGroup {
14 | MainView()
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/Colors/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/Colors/popupAzureishWhite.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0xF2",
9 | "green" : "0xE8",
10 | "red" : "0xE0"
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 |
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/Colors/popupCornflowerBlue.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0xFF",
9 | "green" : "0xD2",
10 | "red" : "0xB1"
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 |
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/Colors/popupDarkViolet.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0xF6",
9 | "green" : "0x50",
10 | "red" : "0x82"
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 |
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/Colors/popupGray1.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0xF6",
9 | "green" : "0xF6",
10 | "red" : "0xF6"
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 |
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/Colors/popupGray2.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0x88",
9 | "green" : "0x88",
10 | "red" : "0x88"
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 |
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/Colors/popupGray3.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0x52",
9 | "green" : "0x52",
10 | "red" : "0x52"
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 |
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/Colors/popupGray5.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0x38",
9 | "green" : "0x38",
10 | "red" : "0x38"
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 |
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/Colors/popupLavender.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0xF9",
9 | "green" : "0xC7",
10 | "red" : "0xD7"
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 |
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/Colors/popupLightBlue.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0xFF",
9 | "green" : "0xF0",
10 | "red" : "0xE5"
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 |
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/Colors/popupLightGreen.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0xE0",
9 | "green" : "0xF7",
10 | "red" : "0xEE"
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 |
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/Colors/popupLightPeriwinkle.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0xDC",
9 | "green" : "0xCB",
10 | "red" : "0xBC"
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 |
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/Colors/popupMenthol.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0xA2",
9 | "green" : "0xE7",
10 | "red" : "0xCC"
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 |
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/Colors/popupSeparator.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0xB2",
9 | "green" : "0xA8",
10 | "red" : "0x9F"
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 |
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/Colors/popupViolet.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0xF8",
9 | "green" : "0x65",
10 | "red" : "0x92"
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 |
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/Colors/popupYellow.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0x39",
9 | "green" : "0x95",
10 | "red" : "0xF7"
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 |
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/Colors/popupYellow2.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0x00",
9 | "green" : "0xC2",
10 | "red" : "0xFF"
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 |
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/arrowRight.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Vector.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/arrowRight.imageset/Vector.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exyte/AnchoredPopup/43fc3deed8c9d3edf72ee9cc84d151541ba709f5/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/arrowRight.imageset/Vector.pdf
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/avatar.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Rectangle 62.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/avatar.imageset/Rectangle 62.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exyte/AnchoredPopup/43fc3deed8c9d3edf72ee9cc84d151541ba709f5/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/avatar.imageset/Rectangle 62.pdf
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/congratulations.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Winner 1.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/congratulations.imageset/Winner 1.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exyte/AnchoredPopup/43fc3deed8c9d3edf72ee9cc84d151541ba709f5/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/congratulations.imageset/Winner 1.pdf
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/cross.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Vector.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/cross.imageset/Vector.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exyte/AnchoredPopup/43fc3deed8c9d3edf72ee9cc84d151541ba709f5/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/cross.imageset/Vector.pdf
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/gift.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "image 5.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/gift.imageset/image 5.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exyte/AnchoredPopup/43fc3deed8c9d3edf72ee9cc84d151541ba709f5/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/gift.imageset/image 5.pdf
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/giftBG.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Ellipse 26.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/giftBG.imageset/Ellipse 26.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exyte/AnchoredPopup/43fc3deed8c9d3edf72ee9cc84d151541ba709f5/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/giftBG.imageset/Ellipse 26.pdf
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/girl1.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "oksana-taran-xB4ExGcUai0-unsplash-removebg-preview 1.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/girl1.imageset/oksana-taran-xB4ExGcUai0-unsplash-removebg-preview 1.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exyte/AnchoredPopup/43fc3deed8c9d3edf72ee9cc84d151541ba709f5/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/girl1.imageset/oksana-taran-xB4ExGcUai0-unsplash-removebg-preview 1.pdf
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/girl2.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "clem-onojeghuo-n6gnCa77Urc-unsplash-removebg-preview 1.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/girl2.imageset/clem-onojeghuo-n6gnCa77Urc-unsplash-removebg-preview 1.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exyte/AnchoredPopup/43fc3deed8c9d3edf72ee9cc84d151541ba709f5/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/girl2.imageset/clem-onojeghuo-n6gnCa77Urc-unsplash-removebg-preview 1.pdf
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/mainMenuCamera.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Videocamera.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/mainMenuCamera.imageset/Videocamera.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exyte/AnchoredPopup/43fc3deed8c9d3edf72ee9cc84d151541ba709f5/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/mainMenuCamera.imageset/Videocamera.pdf
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/mainMenuDumbbell.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Dumbbell.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/mainMenuDumbbell.imageset/Dumbbell.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exyte/AnchoredPopup/43fc3deed8c9d3edf72ee9cc84d151541ba709f5/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/mainMenuDumbbell.imageset/Dumbbell.pdf
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/mainMenuLibrary.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Video Library.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/mainMenuLibrary.imageset/Video Library.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exyte/AnchoredPopup/43fc3deed8c9d3edf72ee9cc84d151541ba709f5/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/mainMenuLibrary.imageset/Video Library.pdf
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/mainPlay.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Polygon 1.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/mainPlay.imageset/Polygon 1.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exyte/AnchoredPopup/43fc3deed8c9d3edf72ee9cc84d151541ba709f5/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/mainPlay.imageset/Polygon 1.pdf
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/mainPlaySmall.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Play.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/mainPlaySmall.imageset/Play.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exyte/AnchoredPopup/43fc3deed8c9d3edf72ee9cc84d151541ba709f5/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/mainPlaySmall.imageset/Play.pdf
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/mainTrophy.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Vector.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/mainTrophy.imageset/Vector.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exyte/AnchoredPopup/43fc3deed8c9d3edf72ee9cc84d151541ba709f5/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/mainTrophy.imageset/Vector.pdf
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/navBack.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Left Accessory Button.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/navBack.imageset/Left Accessory Button.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exyte/AnchoredPopup/43fc3deed8c9d3edf72ee9cc84d151541ba709f5/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/navBack.imageset/Left Accessory Button.pdf
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/profile.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "image.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/profile.imageset/image.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exyte/AnchoredPopup/43fc3deed8c9d3edf72ee9cc84d151541ba709f5/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/profile.imageset/image.pdf
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/profileFavorites.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Heart.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/profileFavorites.imageset/Heart.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exyte/AnchoredPopup/43fc3deed8c9d3edf72ee9cc84d151541ba709f5/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/profileFavorites.imageset/Heart.pdf
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/profileTicket.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Vector.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/profileTicket.imageset/Vector.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exyte/AnchoredPopup/43fc3deed8c9d3edf72ee9cc84d151541ba709f5/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/profileTicket.imageset/Vector.pdf
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/profileTicketBG.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Group.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/profileTicketBG.imageset/Group.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exyte/AnchoredPopup/43fc3deed8c9d3edf72ee9cc84d151541ba709f5/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/profileTicketBG.imageset/Group.pdf
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/profileWorkout.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Dumbbell.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/profileWorkout.imageset/Dumbbell.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exyte/AnchoredPopup/43fc3deed8c9d3edf72ee9cc84d151541ba709f5/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/profileWorkout.imageset/Dumbbell.pdf
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/question.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Question Circle.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/question.imageset/Question Circle.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exyte/AnchoredPopup/43fc3deed8c9d3edf72ee9cc84d151541ba709f5/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/question.imageset/Question Circle.pdf
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/xmark.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Vector.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/xmark.imageset/Vector.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exyte/AnchoredPopup/43fc3deed8c9d3edf72ee9cc84d151541ba709f5/AnchoredPopupExample/AnchoredPopupExample/Assets.xcassets/xmark.imageset/Vector.pdf
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Fonts/ChakraPetch-Light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exyte/AnchoredPopup/43fc3deed8c9d3edf72ee9cc84d151541ba709f5/AnchoredPopupExample/AnchoredPopupExample/Fonts/ChakraPetch-Light.ttf
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Fonts/ChakraPetch-Medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exyte/AnchoredPopup/43fc3deed8c9d3edf72ee9cc84d151541ba709f5/AnchoredPopupExample/AnchoredPopupExample/Fonts/ChakraPetch-Medium.ttf
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Fonts/ChakraPetch-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exyte/AnchoredPopup/43fc3deed8c9d3edf72ee9cc84d151541ba709f5/AnchoredPopupExample/AnchoredPopupExample/Fonts/ChakraPetch-Regular.ttf
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Fonts/IBMPlexMono-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exyte/AnchoredPopup/43fc3deed8c9d3edf72ee9cc84d151541ba709f5/AnchoredPopupExample/AnchoredPopupExample/Fonts/IBMPlexMono-Bold.ttf
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Fonts/IBMPlexMono-Medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exyte/AnchoredPopup/43fc3deed8c9d3edf72ee9cc84d151541ba709f5/AnchoredPopupExample/AnchoredPopupExample/Fonts/IBMPlexMono-Medium.ttf
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Fonts/IBMPlexMono-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/exyte/AnchoredPopup/43fc3deed8c9d3edf72ee9cc84d151541ba709f5/AnchoredPopupExample/AnchoredPopupExample/Fonts/IBMPlexMono-Regular.ttf
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | UIAppFonts
6 |
7 | IBMPlexMono-Medium.ttf
8 | IBMPlexMono-Bold.ttf
9 | IBMPlexMono-Regular.ttf
10 | ChakraPetch-Medium.ttf
11 | ChakraPetch-Light.ttf
12 | ChakraPetch-Regular.ttf
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Screens/MainView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Untitled.swift
3 | // AnchoredPopupExample
4 | //
5 | // Created by Alisa Mylnikova on 22.01.2025.
6 | //
7 |
8 | import SwiftUI
9 | import AnchoredPopup
10 |
11 | struct MainView: View {
12 |
13 | var body: some View {
14 | ZStack(alignment: .bottom) {
15 | ScrollView {
16 | content
17 | }
18 |
19 | HStack {
20 | Circle().foregroundStyle(.popupViolet)
21 | .overlay {
22 | Image(.mainPlay)
23 | }
24 | .size(60)
25 | .useAsPopupAnchor(id: "main_menu") {
26 | MainMenuView()
27 | } customize: {
28 | $0.position(.anchorRelative(.bottomLeading))
29 | .background(.none)
30 | .isBackgroundPassthrough(true)
31 | .closeOnTap(false)
32 | }
33 |
34 | Spacer()
35 |
36 | Circle().foregroundStyle(RadialGradient(colors: [.popupYellow, .popupYellow2], center: .center, startRadius: 0, endRadius: 30))
37 | .overlay {
38 | Image(.mainTrophy)
39 | }
40 | .size(60)
41 | .useAsPopupAnchor(id: "congratulations_view") {
42 | CongratulationsView()
43 | } customize: {
44 | $0.position(.screenRelative())
45 | .closeOnTap(false)
46 | }
47 | }
48 | .padding(16)
49 | }
50 | }
51 |
52 | var content: some View {
53 | VStack(spacing: 16) {
54 | HStack {
55 | VStack(alignment: .leading) {
56 | Text("Today").plexMedium(16, .popupViolet)
57 | Text("Sat, 23 April 🌟").chakraMedium(24)
58 | }
59 |
60 | Spacer()
61 |
62 | Image(.avatar)
63 | .useAsPopupAnchor(id: "profile_view") {
64 | ProfileView()
65 | } customize: {
66 | $0.closeOnTap(false)
67 | }
68 | .overlay(alignment: .topTrailing) {
69 | Circle().styled(.popupViolet, border: .white, 4)
70 | .size(16)
71 | .padding(.top, -2)
72 | .padding(.trailing, -2)
73 | }
74 | }
75 |
76 | HStack {
77 | statView(emoji: "🔥", value: "14 920", title: "calories")
78 | statView(emoji: "💪", value: "8’03”", title: "average pace")
79 | statView(emoji: "⏱️", value: "1:52:58", title: "time")
80 | }
81 | .padding(.vertical, 16)
82 | .background {
83 | RoundedRectangle(cornerRadius: 16)
84 | .foregroundStyle(.popupLavender)
85 | }
86 | .padding(.bottom, 24)
87 |
88 | HStack {
89 | Text("Select Workout").chakraMedium(24)
90 | Spacer()
91 | Text("SEE ALL").plexBold(15, .popupViolet)
92 | }
93 |
94 | ScrollView(.horizontal) {
95 | HStack {
96 | ForEach(Constants.sportEmoji) { emoji in
97 | Circle().foregroundStyle(.popupGray1)
98 | .size(80)
99 | .overlay {
100 | Text(emoji).font(.system(size: 32))
101 | }
102 | }
103 | }
104 | }
105 |
106 | girl1View()
107 | girl2View()
108 | }
109 | .padding(20)
110 | }
111 |
112 | func statView(emoji: String, value: String, title: String) -> some View {
113 | VStack {
114 | Circle().foregroundStyle(.white)
115 | .size(44).overlay {
116 | Text(emoji).font(.system(size: 26))
117 | }
118 | Text(value).chakraMedium(20)
119 | Text(title).plexMedium(12, .popupDarkViolet)
120 | }
121 | .greedyWidth()
122 | }
123 |
124 | func girl1View() -> some View {
125 | HStack {
126 | VStack(alignment: .leading) {
127 | Text("Yoga").chakraMedium(24)
128 | .padding(.bottom, 4)
129 | Text("18 exercises").plex(13, .popupGray2)
130 | .padding(.bottom, 16)
131 | HStack {
132 | Image(.mainPlaySmall)
133 | Text("60 min").plexMedium(12)
134 | }
135 | .padding(16, 6)
136 | .background {
137 | RoundedRectangle(cornerRadius: 6)
138 | .foregroundStyle(.popupCornflowerBlue)
139 | }
140 | }
141 |
142 | Spacer()
143 | Image(.girl1)
144 | }
145 | .padding(.horizontal, 32)
146 | .background {
147 | RoundedRectangle(cornerRadius: 20)
148 | .foregroundStyle(.popupLightBlue)
149 | .greedyWidth()
150 | }
151 | }
152 |
153 | func girl2View() -> some View {
154 | HStack {
155 | VStack(alignment: .leading) {
156 | Text("Fitness").chakraMedium(24)
157 | .padding(.bottom, 4)
158 | Text("24 exercises").plex(13, .popupGray2)
159 | .padding(.bottom, 16)
160 | HStack {
161 | Image(.mainPlaySmall)
162 | Text("82 min").plexMedium(12)
163 | }
164 | .padding(16, 6)
165 | .background {
166 | RoundedRectangle(cornerRadius: 6)
167 | .foregroundStyle(.popupMenthol)
168 | }
169 | }
170 |
171 | Spacer()
172 | Image(.girl2)
173 | }
174 | .padding(.horizontal, 32)
175 | .background {
176 | RoundedRectangle(cornerRadius: 20)
177 | .foregroundStyle(.popupLightGreen)
178 | .greedyWidth()
179 | }
180 | }
181 | }
182 |
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Screens/Popups.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Popups.swift
3 | // AnchoredPopupExample
4 | //
5 | // Created by Alisa Mylnikova on 22.01.2025.
6 | //
7 |
8 | import SwiftUI
9 | import AnchoredPopup
10 |
11 | struct MainMenuView: View {
12 | var body: some View {
13 | ZStack {
14 | Color.popupDarkViolet
15 | .cornerRadius(30)
16 |
17 | VStack(alignment: .leading, spacing: 0) {
18 | Label {
19 | Text("Start workout").chakra(15, .white)
20 | } icon: {
21 | Image(.mainMenuDumbbell)
22 | }
23 | .padding(.horizontal, 20)
24 | .padding(.top, 30)
25 |
26 | Color.popupViolet.frame(height: 1)
27 | .padding(16, 12)
28 |
29 | Label {
30 | Text("Record your exercises ").chakra(15, .white)
31 | } icon: {
32 | Image(.mainMenuCamera)
33 | }
34 | .padding(.horizontal, 20)
35 |
36 | Color.popupViolet.frame(height: 1)
37 | .padding(16, 12)
38 |
39 | Label {
40 | Text("Watch lesson").chakra(15, .white)
41 | } icon: {
42 | Image(.mainMenuLibrary)
43 | }
44 | .padding(.horizontal, 20)
45 | .padding(.bottom, 16)
46 |
47 | ZStack {
48 | Circle().foregroundStyle(.popupViolet)
49 | .size(60)
50 | Image(.cross)
51 | }
52 | .onTapGesture {
53 | AnchoredPopup.launchShrinkingAnimation(id: "main_menu")
54 | }
55 | }
56 | }
57 | .fixedSize()
58 | }
59 | }
60 |
61 | struct CongratulationsView: View {
62 | @Environment(\.anchoredPopupDismiss) var dismiss
63 |
64 | var body: some View {
65 | VStack(spacing: 12) {
66 | Image(.congratulations)
67 | Text("Congratulations!").chakraMedium(24)
68 | 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.")
69 | .plex(15, .popupGray3)
70 | .multilineTextAlignment(.center)
71 |
72 | Button {
73 | dismiss?()
74 | } label: {
75 | ZStack {
76 | RoundedRectangle(cornerRadius: 12)
77 | .foregroundStyle(.popupViolet)
78 | .greedyWidth()
79 | Text("THANKS").plexMedium(18, .white)
80 | .padding(18)
81 | }
82 | .fixedSize(horizontal: false, vertical: true)
83 | }
84 | .padding(.horizontal, 24)
85 | .padding(.top, 20)
86 | }
87 | .padding(24, 40)
88 | .background {
89 | Color.white.cornerRadius(20)
90 | }
91 | .overlay(alignment: .topTrailing) {
92 | Button {
93 | dismiss?()
94 | } label: {
95 | ZStack {
96 | RoundedRectangle(cornerRadius: 6)
97 | .foregroundStyle(.popupAzureishWhite)
98 | .size(32)
99 | Image(.xmark)
100 | }
101 | }
102 | .padding(16, 20)
103 | }
104 | .padding(40)
105 | }
106 | }
107 |
108 | struct QuestionsView: View {
109 |
110 | var body: some View {
111 | VStack(spacing: 0) {
112 | VStack(alignment: .leading, spacing: 16) {
113 | Text("Help center")
114 | Text("Support forum")
115 | Text("YouTube videos")
116 | Text("Submit feedback")
117 | Text("Ask the community")
118 |
119 | Color.popupSeparator.frame(height: 0.5)
120 | .padding(.horizontal, -4)
121 |
122 | Text("Change language...")
123 | }
124 | .chakraLight(15, .popupGray5)
125 | .padding(.horizontal, 20)
126 | .padding(.top, 28)
127 | .padding(.bottom, 4)
128 |
129 | HStack {
130 | Spacer()
131 | Circle().styled(.popupLightPeriwinkle)
132 | .overlay {
133 | Image(.cross)
134 | }
135 | .size(56)
136 | }
137 | }
138 | .background {
139 | RoundedRectangle(cornerRadius: 28).styled(.popupAzureishWhite)
140 | }
141 | .frame(width: 230)
142 | }
143 | }
144 |
145 | struct InviteView: View {
146 | @Environment(\.anchoredPopupDismiss) var dismiss
147 |
148 | var body: some View {
149 | VStack(spacing: 12) {
150 | ZStack {
151 | Image(.giftBG)
152 | Image(.gift)
153 | }
154 | Text("Invite friends. Get free Plus").chakraMedium(20)
155 | .multilineTextAlignment(.center)
156 | Text("Get month of free Workout Plus for every friend who joins via your invite link.")
157 | .plex(13, .popupGray3)
158 | .multilineTextAlignment(.center)
159 |
160 | Button {
161 | dismiss?()
162 | } label: {
163 | ZStack {
164 | RoundedRectangle(cornerRadius: 12)
165 | .foregroundStyle(.popupViolet)
166 | .greedyWidth()
167 | Text("SEND INVITE").plexMedium(18, .white)
168 | .padding(18)
169 | }
170 | .fixedSize(horizontal: false, vertical: true)
171 | }
172 | .padding(.top, 20)
173 | }
174 | .frame(width: 230)
175 | .padding(16, 24)
176 | .background {
177 | Color.white.cornerRadius(20)
178 | }
179 | .overlay(alignment: .topTrailing) {
180 | Button {
181 | dismiss?()
182 | } label: {
183 | ZStack {
184 | RoundedRectangle(cornerRadius: 6)
185 | .foregroundStyle(.popupAzureishWhite)
186 | .size(32)
187 | Image(.xmark)
188 | }
189 | }
190 | .padding(12)
191 | }
192 | .padding(16, 38)
193 | }
194 | }
195 |
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Screens/ProfileView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ProfileView.swift
3 | // AnchoredPopupExample
4 | //
5 | // Created by Alisa Mylnikova on 18.02.2025.
6 | //
7 |
8 | import SwiftUI
9 | import AnchoredPopup
10 |
11 | struct ProfileView: View {
12 | @Environment(\.anchoredPopupDismiss) var dismiss
13 |
14 | var body: some View {
15 | content
16 | .ignoresSafeArea()
17 | .background(Color.white)
18 | .overlay(alignment: .topLeading) {
19 | Button {
20 | dismiss?()
21 | } label: {
22 | Image(.navBack)
23 | .padding(8)
24 | }
25 | .padding(.top, 45)
26 | }
27 | .overlay(alignment: .bottom) {
28 | HStack {
29 | Image(.profileTicketBG)
30 | .overlay {
31 | Image(.profileTicket)
32 | }
33 | .size(60)
34 | .useAsPopupAnchor(id: "send_invite") {
35 | InviteView()
36 | } customize: {
37 | $0.position(.screenRelative(.bottomTrailing))
38 | .closeOnTap(false)
39 | .background(.color(.black.opacity(0.4)))
40 | }
41 |
42 | Spacer()
43 |
44 | Circle().styled(.popupLightPeriwinkle)
45 | .overlay {
46 | Image(.question)
47 | }
48 | .size(56)
49 | .useAsPopupAnchor(id: "questions_view") {
50 | QuestionsView()
51 | } customize: {
52 | $0.position(.anchorRelative(.bottomTrailing))
53 | .background(.none)
54 | .isBackgroundPassthrough(true)
55 | }
56 | }
57 | .padding(16)
58 | .padding(.bottom, 30)
59 | }
60 | }
61 |
62 | var content: some View {
63 | VStack {
64 | Image(.profile)
65 | .resizable()
66 | .scaledToFill()
67 | .frame(width: UIScreen.main.bounds.width, height: 330)
68 |
69 | Spacer()
70 |
71 | VStack {
72 | Text("Online").plexMedium(16, .popupViolet)
73 | Text("Mary Goodwin").chakraMedium(24)
74 |
75 | Spacer()
76 |
77 | HStack(spacing: 12) {
78 | profileButton(.profileWorkout, "Workout plan")
79 | profileButton(.profileFavorites, "Favourites")
80 | }
81 |
82 | Spacer()
83 |
84 | profileCell("Notifications")
85 | profileCell("Settings")
86 | profileCell("FAQ")
87 | profileCell("Support")
88 | profileCell("Log out")
89 | }
90 | .padding(.horizontal, 20)
91 | .padding(.bottom, 100)
92 | }
93 | }
94 |
95 | func profileButton(_ image: ImageResource, _ title: String) -> some View {
96 | HStack {
97 | Circle().foregroundStyle(.popupLavender)
98 | .size(42)
99 | .overlay {
100 | Image(image)
101 | }
102 | Spacer()
103 | Text(title).chakraMedium(15)
104 | Spacer()
105 | }
106 | .padding(6)
107 | .overlay {
108 | RoundedRectangle(cornerRadius: .infinity)
109 | .styled(.clear, border: .popupAzureishWhite, 1)
110 | }
111 | }
112 |
113 | @ViewBuilder
114 | func profileCell(_ title: String) -> some View {
115 | HStack {
116 | Text(title).chakra(20)
117 | Spacer()
118 | Image(.arrowRight)
119 | }
120 | Spacer()
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Utils/FontUtils.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FontUtils.swift
3 | // ExyteApp
4 | //
5 | // Created by Alisa Mylnikova on 26.10.2023.
6 | //
7 |
8 | import SwiftUI
9 |
10 | extension View {
11 | func chakra(_ size: CGFloat, _ color: Color = .black) -> some View {
12 | self.font(.custom("ChakraPetch-Regular", size: size))
13 | .foregroundStyle(color)
14 | }
15 |
16 | func chakraMedium(_ size: CGFloat, _ color: Color = .black) -> some View {
17 | self.font(.custom("ChakraPetch-Medium", size: size))
18 | .foregroundStyle(color)
19 | }
20 | func chakraLight(_ size: CGFloat, _ color: Color = .black) -> some View {
21 | self.font(.custom("ChakraPetch-Light", size: size))
22 | .foregroundStyle(color)
23 | }
24 |
25 | func plex(_ size: CGFloat, _ color: Color = .black) -> some View {
26 | self.font(.custom("IBMPlexMono-Regular", size: size))
27 | .foregroundStyle(color)
28 | }
29 |
30 | func plexMedium(_ size: CGFloat, _ color: Color = .black) -> some View {
31 | self.font(.custom("IBMPlexMono-Medium", size: size))
32 | .foregroundStyle(color)
33 | }
34 |
35 | func plexBold(_ size: CGFloat, _ color: Color = .black) -> some View {
36 | self.font(.custom("IBMPlexMono-Bold", size: size))
37 | .foregroundStyle(color)
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/AnchoredPopupExample/AnchoredPopupExample/Utils/Utils.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Utils.swift
3 | // AnchoredPopupExample
4 | //
5 | // Created by Alisa Mylnikova on 22.01.2025.
6 | //
7 |
8 | import SwiftUI
9 |
10 | #if compiler(>=6.0)
11 | extension String: @retroactive Identifiable {
12 | public var id: String { self }
13 | }
14 | #else
15 | extension String: Identifiable {
16 | public var id: String { self }
17 | }
18 | #endif
19 |
20 | extension View {
21 | func greedyWidth() -> some View {
22 | self.frame(maxWidth: .infinity)
23 | }
24 |
25 | func padding(_ horizontal: CGFloat, _ vertical: CGFloat) -> some View {
26 | self.padding(.horizontal, horizontal)
27 | .padding(.vertical, vertical)
28 | }
29 |
30 | func size(_ size: CGFloat) -> some View {
31 | self.frame(width: size, height: size)
32 | }
33 |
34 | func fullTap(action: @escaping () -> Void) -> some View {
35 | self.contentShape(Rectangle())
36 | .onTapGesture {
37 | action()
38 | }
39 | }
40 |
41 | @ViewBuilder
42 | func isHidden(_ hidden: Bool) -> some View {
43 | if hidden {
44 | self.hidden()
45 | } else {
46 | self
47 | }
48 | }
49 |
50 | @ViewBuilder
51 | func applyIf(_ condition: Bool, apply: (Self) -> T) -> some View {
52 | if condition {
53 | apply(self)
54 | } else {
55 | self
56 | }
57 | }
58 | }
59 |
60 | // Extension to easily apply the modifier to any shape
61 | extension Shape {
62 | func styled(_ foregroundColor: Color, border borderColor: Color = .clear, _ borderWidth: CGFloat = 0) -> some View {
63 | self.foregroundStyle(foregroundColor) // Apply foreground color
64 | .overlay(
65 | self
66 | .stroke(borderColor, lineWidth: borderWidth) // Apply border color and width
67 | )
68 | }
69 | }
70 |
71 | class Constants {
72 | static let sportEmoji = ["🤼♂️", "🧘", "🚴", "🏊", "🏄", "🤸", "⛹️", "🏋️", "⚽️"]
73 | }
74 |
75 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Exyte
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version: 5.9
2 |
3 | import PackageDescription
4 |
5 | let package = Package(
6 | name: "AnchoredPopup",
7 | platforms: [
8 | .iOS(.v17)
9 | ],
10 | products: [
11 | // Products define the executables and libraries a package produces, making them visible to other packages.
12 | .library(
13 | name: "AnchoredPopup",
14 | targets: ["AnchoredPopup"]),
15 | ],
16 | targets: [
17 | // Targets are the basic building blocks of a package, defining a module or a test suite.
18 | // Targets can depend on other targets in this package and products from dependencies.
19 | .target(
20 | name: "AnchoredPopup",
21 | swiftSettings: [
22 | .enableExperimentalFeature("StrictConcurrency")
23 | ]
24 | )
25 | ]
26 | )
27 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | Anchored Popup
13 |
14 | Anchored Popup grows "out" of a trigger view, anchoring to a UnitPoint of the trigger, written with SwiftUI
15 |
16 | 
17 | [](https://swiftpackageindex.com/exyte/AnchoredPopup)
18 | [](https://swiftpackageindex.com/exyte/AnchoredPopup)
19 | [](https://swiftpackageindex.com/exyte/AnchoredPopup)
20 | [](https://opensource.org/licenses/MIT)
21 |
22 | # Usage
23 |
24 | ### Minimal example
25 |
26 | ```swift
27 | import AnchoredPopup
28 |
29 | Circle()
30 | .useAsPopupAnchor(id: "main_menu") {
31 | MainMenuView()
32 | }
33 | ```
34 |
35 | Customized example:
36 | ```swift
37 | .useAsPopupAnchor(id: "main_menu") {
38 | MainMenuView()
39 | } customize: {
40 | $0.position(.anchorRelative(.bottomLeading))
41 | .background(.none)
42 | .isBackgroundPassthrough(true)
43 | .closeOnTap(false)
44 | }
45 | ```
46 |
47 | ### Required parameters - useAsPopupAnchor
48 | - `id` - A unique `String` to store everything related to this animation, you can use it to manually launch animations using this func `AnchoredPopup.launchAnchoredAnimation`
49 | - `contentBuilder` - popup body builder
50 |
51 | ### Optional parameters
52 | - `position` - a `UnitPoint` to align with `UnitPoint`-th part of anchor view. Could be an `anchorRelative` or `screenRelative` 'UnitPoint'
53 | - `animation` - appear/disappear animation
54 | - `closeOnTap` - enable/disable closing on tap on popup
55 | - `closeOnTapOutside` - enable/disable closing on tap on popup's background
56 | - `isBackgroundPassthrough` - enable/disable taps passing through the popup's background
57 | - `background` - Available options are:
58 | * `.none`
59 | * `.color(Color)`
60 | * `.blur(radius: CGFloat)` - blurred fullscreen overlay
61 | * `.view(AnyView)` - custom view builder
62 |
63 | ## State management pitfall
64 | AnchoredPopup uses `UIWindow` to display itself above anything you might have on screen, so remember - to get adequate UI updates, use `ObservableObjects` or `@Bindings` instead of `@State`. This won't work:
65 | ```swift
66 | struct MainView: View {
67 | @State var name = "Mike"
68 | var body: some View {
69 | Text("Show popup")
70 | .useAsPopupAnchor(id: "a") {
71 | ZStack {
72 | Color.red.size(100)
73 | VStack {
74 | Text(name)
75 | Button("Change text") {
76 | name = "John"
77 | }
78 | }
79 | }
80 | } customize: {
81 | $0.position(.anchorRelative(.bottomLeading))
82 | .closeOnTap(false)
83 | }
84 | }
85 | }
86 | ```
87 | This will work:
88 | ```swift
89 | struct MainView: View {
90 | @State var name = "Mike"
91 | var body: some View {
92 | Text("Show popup")
93 | .useAsPopupAnchor(id: "a") {
94 | Popup(name: $name)
95 | } customize: {
96 | $0.position(.anchorRelative(.bottomLeading))
97 | .closeOnTap(false)
98 | }
99 | }
100 | }
101 |
102 | struct Popup: View {
103 | @Binding var name: String
104 | var body: some View {
105 | ZStack {
106 | Color.red.size(100)
107 | VStack {
108 | Text(name)
109 | Button("Change text") {
110 | name = "John"
111 | }
112 | }
113 | }
114 | }
115 | }
116 | ```
117 | This will work too:
118 | ```swift
119 | struct MainView: View {
120 | var body: some View {
121 | Text("Show popup")
122 | .useAsPopupAnchor(id: "a") {
123 | Popup()
124 | } customize: {
125 | $0.position(.anchorRelative(.bottomLeading))
126 | .closeOnTap(false)
127 | }
128 | }
129 | }
130 |
131 | struct Popup: View {
132 | @State var name = "Mike"
133 | var body: some View {
134 | ZStack {
135 | Color.red.size(100)
136 | VStack {
137 | Text(name)
138 | Button("Change text") {
139 | name = "John"
140 | }
141 | }
142 | }
143 | }
144 | }
145 | ```
146 |
147 | ## Examples
148 |
149 | To try AnchoredPopup examples:
150 | - Clone the repo `https://github.com/exyte/AnchoredPopup.git`
151 | - Open `AnchoredPopupExample.xcodeproj`
152 | - Try it!
153 |
154 | ## Installation
155 |
156 | ### [Swift Package Manager](https://swift.org/package-manager/)
157 |
158 | ```swift
159 | dependencies: [
160 | .package(url: "https://github.com/exyte/AnchoredPopup.git")
161 | ]
162 | ```
163 |
164 | ## Requirements
165 |
166 | * iOS 17.0+
167 |
168 | ## Our other open source SwiftUI libraries
169 | [PopupView](https://github.com/exyte/PopupView) - Toasts and popups library
170 | [Grid](https://github.com/exyte/Grid) - The most powerful Grid container
171 | [AnimatedTabBar](https://github.com/exyte/AnimatedTabBar) - A tabbar with a number of preset animations
172 | [ScalingHeaderScrollView](https://github.com/exyte/ScalingHeaderScrollView) - A scroll view with a sticky header which shrinks as you scroll
173 | [MediaPicker](https://github.com/exyte/mediapicker) - Customizable media picker
174 | [Chat](https://github.com/exyte/chat) - Chat UI framework with fully customizable message cells, input view, and a built-in media picker
175 | [OpenAI](https://github.com/exyte/OpenAI) Wrapper lib for [OpenAI REST API](https://platform.openai.com/docs/api-reference/introduction)
176 | [AnimatedGradient](https://github.com/exyte/AnimatedGradient) - Animated linear gradient
177 | [ConcentricOnboarding](https://github.com/exyte/ConcentricOnboarding) - Animated onboarding flow
178 | [FloatingButton](https://github.com/exyte/FloatingButton) - Floating button menu
179 | [ActivityIndicatorView](https://github.com/exyte/ActivityIndicatorView) - A number of animated loading indicators
180 | [ProgressIndicatorView](https://github.com/exyte/ProgressIndicatorView) - A number of animated progress indicators
181 | [FlagAndCountryCode](https://github.com/exyte/FlagAndCountryCode) - Phone codes and flags for every country
182 | [SVGView](https://github.com/exyte/SVGView) - SVG parser
183 | [LiquidSwipe](https://github.com/exyte/LiquidSwipe) - Liquid navigation animation
184 |
--------------------------------------------------------------------------------
/Sources/AnchoredPopup/AnchoredAnimationManager.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AnchoredAnimationManager.swift
3 | //
4 | // Created by Alisa Mylnikova on 23.10.2024.
5 | //
6 |
7 | import SwiftUI
8 | import Combine
9 |
10 | /// this manager stores states for all the paired growing/shrinking animations
11 | @MainActor
12 | class AnchoredAnimationManager: ObservableObject {
13 | static let shared = AnchoredAnimationManager()
14 |
15 | enum GrowingViewState {
16 | case hidden, growing, displayed, shrinking
17 | }
18 |
19 | struct AnimationItem: Equatable {
20 | var id: String
21 | var buttonFrame: IntRect
22 | var state: GrowingViewState
23 |
24 | static func == (lhs: AnimationItem, rhs: AnimationItem) -> Bool {
25 | lhs.id == rhs.id
26 | && lhs.buttonFrame == rhs.buttonFrame
27 | && lhs.state == rhs.state
28 | }
29 | }
30 |
31 | @Published var animations: [AnimationItem] = []
32 |
33 | private var statePublishers: [String: CurrentValueSubject] = [:]
34 | private var framePublishers: [String: CurrentValueSubject] = [:]
35 | private var cancellables = Set()
36 |
37 | static subscript(id: String) -> AnimationItem? {
38 | shared.animations.first { $0.id == id }
39 | }
40 |
41 | func changeStateForAnimation(for id: String, state: GrowingViewState) {
42 | if let index = animations.firstIndex(where: { $0.id == id }) {
43 | animations[index].state = state
44 | }
45 | }
46 |
47 | func updateFrame(for id: String, frame: CGRect) {
48 | if let index = animations.firstIndex(where: { $0.id == id }) {
49 | animations[index].buttonFrame = frame.toIntRect()
50 | } else {
51 | animations.append(AnimationItem(id: id, buttonFrame: frame.toIntRect(), state: .hidden))
52 | }
53 | }
54 |
55 | func statePublisher(for id: String) -> CurrentValueSubject {
56 | if let publisher = statePublishers[id] {
57 | return publisher
58 | }
59 |
60 | // Track the last emitted value for comparison
61 | var lastValue: AnimationItem? = nil
62 |
63 | // Create a CurrentValueSubject to hold the current value
64 | let subject = CurrentValueSubject(nil)
65 |
66 | // Generate the publisher and handle state changes
67 | $animations
68 | .map { animations in
69 | animations.first { $0.id == id }
70 | }
71 | .compactMap { $0 }
72 | .filter { newItem in
73 | if let last = lastValue {
74 | // Only emit if the item has changed from the last value
75 | if last.state != newItem.state {
76 | lastValue = newItem // Update the last value
77 | return true // Emit if there's a change
78 | } else {
79 | return false // Don't emit if no change
80 | }
81 | } else {
82 | lastValue = newItem // Set initial value
83 | return true // Emit the first time
84 | }
85 | }
86 | .sink { newItem in
87 | // Emit the value to the CurrentValueSubject
88 | subject.send(newItem)
89 | }
90 | .store(in: &cancellables)
91 |
92 | statePublishers[id] = subject
93 | return subject
94 | }
95 |
96 | func framePublisher(for id: String) -> CurrentValueSubject {
97 | if let publisher = framePublishers[id] {
98 | return publisher
99 | }
100 |
101 | // Track the last emitted value for comparison
102 | var lastValue: AnimationItem? = nil
103 |
104 | // Create a CurrentValueSubject to hold the current value
105 | let subject = CurrentValueSubject(nil)
106 |
107 | // Generate the publisher and handle state changes
108 | $animations
109 | .map { animations in
110 | animations.first { $0.id == id }
111 | }
112 | .compactMap { $0 }
113 | .filter { newItem in
114 | if let last = lastValue {
115 | // Only emit if the item has changed from the last value
116 | if last.buttonFrame != newItem.buttonFrame {
117 | lastValue = newItem // Update the last value
118 | return true // Emit if there's a change
119 | } else {
120 | return false // Don't emit if no change
121 | }
122 | } else {
123 | lastValue = newItem // Set initial value
124 | return true // Emit the first time
125 | }
126 | }
127 | .sink { newItem in
128 | // Emit the value to the CurrentValueSubject
129 | subject.send(newItem)
130 | }
131 | .store(in: &cancellables)
132 |
133 | framePublishers[id] = subject
134 | return subject
135 | }
136 | }
137 |
138 | struct TriggerButton: ViewModifier where V: View {
139 | @State var id: String
140 | var params: PopupParameters
141 | @ViewBuilder var contentBuilder: () -> V
142 |
143 | @State private var cancellable: AnyCancellable?
144 |
145 | func body(content: Content) -> some View {
146 | content
147 | .overlay {
148 | GeometryReader { geo in
149 | Color.clear
150 | .preference(key: ButtonFramePreferenceKey.self, value: ButtonFrameInfo(id: id, frame: geo.frame(in: .global)))
151 | }
152 | }
153 | .onPreferenceChange(ButtonFramePreferenceKey.self) { value in
154 | DispatchQueue.main.async {
155 | if id == value.id {
156 | AnchoredAnimationManager.shared.updateFrame(for: value.id, frame: value.frame)
157 | }
158 | }
159 | }
160 | .simultaneousGesture(
161 | TapGesture().onEnded { gesture in
162 | // trigger displaying animation
163 | hideKeyboard()
164 | AnchoredAnimationManager.shared.changeStateForAnimation(for: id, state: .growing)
165 | }
166 | )
167 | .onReceive(AnchoredAnimationManager.shared.statePublisher(for: id)) { animation in
168 | if animation?.state == .growing {
169 | WindowManager.openNewWindow(id: id, isPassthrough: params.isPassthrough) {
170 | ZStack {
171 | AnimatedBackgroundView(id: $id, background: params.background)
172 | .simultaneousGesture(
173 | TapGesture().onEnded {
174 | if params.closeOnTapOutside {
175 | // trigger hiding animation
176 | AnchoredAnimationManager.shared.changeStateForAnimation(for: id, state: .shrinking)
177 | }
178 | }
179 | )
180 | AnchoredAnimationView(id: id, params: params, contentBuilder: contentBuilder)
181 | }
182 | }
183 | } else if animation?.state == .hidden {
184 | WindowManager.closeWindow(id: id)
185 | }
186 | }
187 | }
188 | }
189 |
190 | @MainActor
191 | fileprivate struct AnchoredAnimationView: View where V: View {
192 | var id: String
193 | var params: PopupParameters
194 | var contentBuilder: () -> V
195 |
196 | @State private var animatableOpacity: CGFloat = 0
197 | @State private var animatableScale: CGSize = .zero
198 | @State private var animatableOffset: CGSize = .zero
199 |
200 | @State private var triggerButtonFrame: IntRect = .zero
201 | @State private var contentSize: IntSize = .zero
202 |
203 | @State private var semaphore = DispatchSemaphore(value: 1)
204 |
205 | var body: some View {
206 | VStack {
207 | contentBuilder()
208 | .overlay(GeometryReader { geo in
209 | Color.clear.onAppear {
210 | DispatchQueue.main.async {
211 | contentSize = geo.size.toIntSize()
212 | if let animation = AnchoredAnimationManager.shared.animations.first(where: { $0.id == id }) {
213 | setupAndLaunchAnimation(animation)
214 | }
215 | }
216 | }
217 | })
218 | .scaleEffect(animatableScale)
219 | .offset(animatableOffset)
220 | .position(x: triggerButtonFrame.floatMidX, y: triggerButtonFrame.floatMidY)
221 | .opacity(animatableOpacity)
222 | .ignoresSafeArea()
223 | .simultaneousGesture(
224 | TapGesture().onEnded { gesture in
225 | if params.closeOnTap {
226 | // trigger hiding animation
227 | AnchoredAnimationManager.shared.changeStateForAnimation(for: id, state: .shrinking)
228 | }
229 | }
230 | )
231 | }
232 | .onReceive(AnchoredAnimationManager.shared.framePublisher(for: id)) { animation in
233 | if let animation, triggerButtonFrame == .zero {
234 | triggerButtonFrame = animation.buttonFrame
235 | }
236 | }
237 | .onReceive(AnchoredAnimationManager.shared.statePublisher(for: id)) { animation in
238 | if let animation {
239 | setupAndLaunchAnimation(animation)
240 | }
241 | }
242 | }
243 |
244 | private func setupAndLaunchAnimation(_ animation: AnchoredAnimationManager.AnimationItem) {
245 | if contentSize == .zero || triggerButtonFrame == .zero { return }
246 |
247 | semaphore.wait()
248 | if let animation = AnchoredAnimationManager[id] {
249 | if animation.state == .growing {
250 | setHiddenState()
251 |
252 | if #available(iOS 17.0, *) {
253 | withAnimation(params.animation) {
254 | setDisplayedState()
255 | } completion: {
256 | AnchoredAnimationManager.shared.changeStateForAnimation(for: id, state: .displayed)
257 | semaphore.signal()
258 | }
259 | } else {
260 | withAnimation(params.animation) {
261 | setDisplayedState()
262 | }
263 | DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
264 | AnchoredAnimationManager.shared.changeStateForAnimation(for: id, state: .displayed)
265 | semaphore.signal()
266 | }
267 | }
268 | } else if animation.state == .shrinking {
269 | if #available(iOS 17.0, *) {
270 | withAnimation(params.animation) {
271 | setHiddenState()
272 | } completion: {
273 | AnchoredAnimationManager.shared.changeStateForAnimation(for: id, state: .hidden)
274 | semaphore.signal()
275 | }
276 | } else {
277 | withAnimation(params.animation) {
278 | setHiddenState()
279 | }
280 | DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
281 | AnchoredAnimationManager.shared.changeStateForAnimation(for: id, state: .hidden)
282 | semaphore.signal()
283 | }
284 | }
285 | } else {
286 | semaphore.signal()
287 | }
288 | }
289 | }
290 |
291 | private func setHiddenState() {
292 | animatableOffset = .zero
293 | animatableScale = calculateHiddenScale()
294 | animatableOpacity = 0
295 | }
296 |
297 | private func setDisplayedState() {
298 | animatableOffset = calculateDisplayedOffset()
299 | animatableScale = CGSize(width: 1, height: 1)
300 | animatableOpacity = 1
301 | }
302 |
303 | /// start with popup matching trigger's position and size
304 | private func calculateHiddenScale() -> CGSize {
305 | let tw = triggerButtonFrame.floatWidth
306 | let th = triggerButtonFrame.floatHeight
307 | let pw = contentSize.floatWidth
308 | let ph = contentSize.floatHeight
309 | return CGSize(width: tw/pw, height: th/ph)
310 | }
311 |
312 | /// starting position is center of the trigger
313 | private func calculateDisplayedOffset() -> CGSize {
314 | let cw = contentSize.floatWidth
315 | let ch = contentSize.floatHeight
316 |
317 | switch params.position {
318 | case .anchorRelative(let p):
319 | let tw = triggerButtonFrame.floatWidth
320 | let th = triggerButtonFrame.floatHeight
321 |
322 | // difference between centers
323 | let w = cw/2 - tw/2
324 | let h = ch/2 - th/2
325 |
326 | // normalization: (0, 1) -> (1, -1)
327 | let px = -2 * p.x + 1
328 | let py = -2 * p.y + 1
329 |
330 | // the content view center is currently same as anchor view
331 | // +/- the difference between centers
332 | return CGSize(width: w * px, height: h * py)
333 |
334 | case .screenRelative(let p):
335 | let tx = triggerButtonFrame.floatMidX
336 | let ty = triggerButtonFrame.floatMidY
337 | let sw = UIScreen.main.bounds.width
338 | let sh = UIScreen.main.bounds.height
339 |
340 | // normalization: (0, 1) -> (1, -1)
341 | let px = -2 * p.x + 1
342 | let py = -2 * p.y + 1
343 |
344 | // the content view center is currently same as anchor view
345 | // -tx: put middle of popup into (0,0)
346 | // sw * p.x: put middle of popup into required unit point of screen
347 | // cw/2 * px: align required unit point of popup with the screen
348 | return CGSize(width: -tx + sw * p.x + cw/2 * px, height: -ty + sh * p.y + ch/2 * py)
349 | }
350 | }
351 | }
352 |
--------------------------------------------------------------------------------
/Sources/AnchoredPopup/AnchoredPopup.swift:
--------------------------------------------------------------------------------
1 | // The Swift Programming Language
2 | // https://docs.swift.org/swift-book
3 |
--------------------------------------------------------------------------------
/Sources/AnchoredPopup/BlurBackdropView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIBackdropView.swift
3 | // Beyond
4 | //
5 | // Created by Alisa Mylnikova on 30.10.2024.
6 | //
7 |
8 | import SwiftUI
9 |
10 | open class UIBackdropView: UIView {
11 | open override class var layerClass: AnyClass {
12 | NSClassFromString("CABackdropLayer") ?? CALayer.self
13 | }
14 | }
15 |
16 | public struct Backdrop: UIViewRepresentable {
17 | public init() {}
18 |
19 | public func makeUIView(context: Context) -> UIBackdropView {
20 | UIBackdropView()
21 | }
22 |
23 | public func updateUIView(_ uiView: UIBackdropView, context: Context) {}
24 | }
25 |
26 | public struct Blur: View {
27 | public var radius: CGFloat
28 | public var opaque: Bool
29 |
30 | public init(radius: CGFloat = 3.0, opaque: Bool = false) {
31 | self.radius = radius
32 | self.opaque = opaque
33 | }
34 |
35 | public var body: some View {
36 | Backdrop()
37 | .blur(radius: radius, opaque: opaque)
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/Sources/AnchoredPopup/PublicAPI.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PublicAPI.swift
3 | // AnchoredPopup
4 | //
5 | // Created by Alisa Mylnikova on 04.02.2025.
6 | //
7 |
8 | import SwiftUI
9 |
10 | // - MARK: Popup creation
11 |
12 | public extension View {
13 | func useAsPopupAnchor(id: String, @ViewBuilder contentBuilder: @escaping () -> V, customize: @escaping (PopupParameters) -> PopupParameters) -> some View {
14 | self.modifier(TriggerButton(id: id, params: customize(PopupParameters()), contentBuilder: contentBuilder))
15 | }
16 |
17 | func useAsPopupAnchor(id: String, @ViewBuilder contentBuilder: @escaping () -> V) -> some View {
18 | self.modifier(TriggerButton(id: id, params: PopupParameters(), contentBuilder: contentBuilder))
19 | }
20 | }
21 |
22 | /// convenience methods to open/close the popup manually from code
23 | public class AnchoredPopup {
24 | @MainActor public static func launchGrowingAnimation(id: String) {
25 | AnchoredAnimationManager.shared.changeStateForAnimation(for: id, state: .growing)
26 | }
27 |
28 | @MainActor public static func launchShrinkingAnimation(id: String) {
29 | AnchoredAnimationManager.shared.changeStateForAnimation(for: id, state: .shrinking)
30 | }
31 | }
32 |
33 | // - MARK: Customization parameters
34 |
35 | public enum AnchoredPopupPosition {
36 | case anchorRelative(_ point: UnitPoint) // popup view will be aligned to anchor view at corresponding proportion
37 | case screenRelative(_ point: UnitPoint = .center) // popup view will be aligned to whole screen
38 | }
39 |
40 | public enum AnchoredPopupBackground {
41 | case none
42 | case color(Color)
43 | case blur(radius: CGFloat = 6)
44 | case view(AnyView)
45 |
46 | // Convenience initializer for `view` that automatically wraps the content in `AnyView`
47 | public init(viewBuilder: @escaping () -> Content) {
48 | self = .view(AnyView(viewBuilder()))
49 | }
50 | }
51 |
52 | public struct PopupParameters {
53 | var position: AnchoredPopupPosition = .screenRelative()
54 | var animation: Animation = .easeIn(duration: 0.3)
55 |
56 | /// Should close on tap anywhere inside the popup
57 | var closeOnTap: Bool = true
58 |
59 | /// Should close on tap anywhere outside of the popup
60 | var closeOnTapOutside: Bool = false
61 |
62 | /// Should taps pass through the popup's background
63 | var isPassthrough: Bool = false
64 |
65 | var background: AnchoredPopupBackground = .blur()
66 |
67 | public func position(_ position: AnchoredPopupPosition) -> PopupParameters {
68 | var params = self
69 | params.position = position
70 | return params
71 | }
72 |
73 | /// Appear/disappear animation - default is `easeOut`
74 | public func animation(_ animation: Animation) -> PopupParameters {
75 | var params = self
76 | params.animation = animation
77 | return params
78 | }
79 |
80 | /// Should close on tap - default is `true`
81 | public func closeOnTap(_ closeOnTap: Bool) -> PopupParameters {
82 | var params = self
83 | params.closeOnTap = closeOnTap
84 | return params
85 | }
86 |
87 | /// Should close on tap outside - default is `false`
88 | public func closeOnTapOutside(_ closeOnTapOutside: Bool) -> PopupParameters {
89 | var params = self
90 | params.closeOnTapOutside = closeOnTapOutside
91 | return params
92 | }
93 |
94 | /// Should taps pass through the popup's background - default is `false`
95 | public func isBackgroundPassthrough(_ isPassthrough: Bool) -> PopupParameters {
96 | var params = self
97 | params.isPassthrough = isPassthrough
98 | return params
99 | }
100 |
101 | /// Background for popup - default is `.blur`
102 | public func background(_ background: AnchoredPopupBackground) -> PopupParameters {
103 | var params = self
104 | params.background = background
105 | return params
106 | }
107 | }
108 |
109 | // - MARK: Environmental dismiss
110 |
111 | public typealias SendableClosure = @Sendable @MainActor () -> Void
112 |
113 | struct AnchoredPopupDismissKey: EnvironmentKey {
114 | static let defaultValue: SendableClosure? = nil
115 | }
116 |
117 | public extension EnvironmentValues {
118 | var anchoredPopupDismiss: SendableClosure? {
119 | get { self[AnchoredPopupDismissKey.self] }
120 | set { self[AnchoredPopupDismissKey.self] = newValue }
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/Sources/AnchoredPopup/Utils.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Utils.swift
3 | // AnchoredPopup
4 | //
5 | // Created by Alisa Mylnikova on 21.01.2025.
6 | //
7 |
8 | import SwiftUI
9 |
10 | @MainActor func hideKeyboard() {
11 | UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
12 | }
13 |
14 | // MARK: - Frame getting
15 |
16 | struct ButtonFrameInfo: Equatable {
17 | let id: String
18 | let frame: CGRect
19 | }
20 |
21 | struct ButtonFramePreferenceKey: PreferenceKey {
22 | typealias Value = ButtonFrameInfo
23 | static let defaultValue: ButtonFrameInfo = ButtonFrameInfo(id: "", frame: .zero)
24 |
25 | static func reduce(value: inout ButtonFrameInfo, nextValue: () -> ButtonFrameInfo) {
26 | if value != nextValue() {
27 | value = nextValue()
28 | }
29 | }
30 | }
31 |
32 | // MARK: - AnimatedBackgroundView
33 |
34 | struct AnimatedBackgroundView: View {
35 | @Binding var id: String
36 | var background: AnchoredPopupBackground
37 |
38 | @State private var animatableOpacity: CGFloat = 0
39 |
40 | var body: some View {
41 | Group {
42 | switch background {
43 | case .none:
44 | EmptyView()
45 | case .color(let color):
46 | color
47 | case .blur(let radius):
48 | Blur(radius: radius)
49 | case .view(let anyView):
50 | anyView
51 | }
52 | }
53 | .ignoresSafeArea()
54 | .opacity(animatableOpacity)
55 | .onReceive(AnchoredAnimationManager.shared.statePublisher(for: id)) { animation in
56 | if let animation {
57 | setupAndLaunchAnimation(animation)
58 | }
59 | }
60 | }
61 |
62 | private func setupAndLaunchAnimation(_ animation: AnchoredAnimationManager.AnimationItem) {
63 | DispatchQueue.main.async {
64 | withAnimation(.easeInOut(duration: 0.2)) {
65 | if animation.state == .growing {
66 | setDisplayedState()
67 | } else if animation.state == .shrinking {
68 | setHiddenState()
69 | }
70 | }
71 | }
72 | }
73 |
74 | private func setHiddenState() {
75 | animatableOpacity = 0
76 | }
77 |
78 | private func setDisplayedState() {
79 | animatableOpacity = 1
80 | }
81 | }
82 |
83 | // MARK: - IntRect
84 |
85 | struct IntRect: Equatable {
86 | var midX, midY, width, height: Int
87 | var floatMidX: CGFloat { CGFloat(midX) }
88 | var floatMidY: CGFloat { CGFloat(midY) }
89 | var floatWidth: CGFloat { CGFloat(width) }
90 | var floatHeight: CGFloat { CGFloat(height) }
91 |
92 | static let zero = IntRect(midX: 0, midY: 0, width: 0, height: 0)
93 |
94 | static func == (lhs: Self, rhs: Self) -> Bool {
95 | lhs.midX == rhs.midX
96 | && lhs.midY == rhs.midY
97 | && lhs.width == rhs.width
98 | && lhs.height == rhs.height
99 | }
100 | }
101 |
102 | extension CGRect {
103 | func toIntRect() -> IntRect {
104 | IntRect(midX: Int(midX), midY: Int(midY), width: Int(width), height: Int(height))
105 | }
106 | }
107 |
108 | struct IntSize: Equatable {
109 | var width, height: Int
110 | var floatWidth: CGFloat { CGFloat(width) }
111 | var floatHeight: CGFloat { CGFloat(height) }
112 |
113 | static let zero = IntSize(width: 0, height: 0)
114 |
115 | static func == (lhs: Self, rhs: Self) -> Bool {
116 | lhs.width == rhs.width
117 | && lhs.height == rhs.height
118 | }
119 | }
120 |
121 | extension CGSize {
122 | func toIntSize() -> IntSize {
123 | IntSize(width: Int(width), height: Int(height))
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/Sources/AnchoredPopup/WindowManager.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WindowManager.swift
3 | // Beyond
4 | //
5 | // Created by Alisa Mylnikova on 19.12.2024.
6 | //
7 |
8 | import SwiftUI
9 |
10 | @MainActor
11 | final class WindowManager {
12 | static let shared = WindowManager()
13 | private var windows: [String: UIWindow] = [:]
14 |
15 | static func openNewWindow(id: String, isPassthrough: Bool, content: ()->Content) {
16 | guard let scene = UIApplication.shared.connectedScenes.first as? UIWindowScene else {
17 | print("No valid scene available")
18 | return
19 | }
20 |
21 | let window = isPassthrough ? UIPassthroughWindow(windowScene: scene) : UIWindow(windowScene: scene)
22 | window.backgroundColor = .clear
23 | let root = content()
24 | .environment(\.anchoredPopupDismiss) {
25 | AnchoredAnimationManager.shared.changeStateForAnimation(for: id, state: .shrinking)
26 | }
27 | let controller = isPassthrough ? UIPassthroughVC(rootView: root) : UIHostingController(rootView: root)
28 | controller.view.backgroundColor = .clear
29 | window.rootViewController = controller
30 | window.windowLevel = .alert + 1
31 | window.makeKeyAndVisible()
32 | shared.windows[id] = window
33 | }
34 |
35 | static func closeWindow(id: String) {
36 | shared.windows[id]?.isHidden = true
37 | shared.windows.removeValue(forKey: id)
38 | }
39 | }
40 |
41 | class UIPassthroughWindow: UIWindow {
42 |
43 | override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
44 | if let vc = self.rootViewController {
45 | vc.view.layoutSubviews() // otherwise the frame is as if the popup is still outside the screen
46 | if let _ = isTouchInsideSubview(point: point, vc: vc.view) {
47 | // pass tap to this UIPassthroughVC
48 | return vc.view
49 | }
50 | }
51 | return nil // pass to next window
52 | }
53 |
54 | private func isTouchInsideSubview(point: CGPoint, vc: UIView) -> UIView? {
55 | for subview in vc.subviews {
56 | if subview.frame.contains(point) {
57 | return subview
58 | }
59 | }
60 | return nil
61 | }
62 | }
63 |
64 | class UIPassthroughVC: UIHostingController {
65 |
66 | override func touchesBegan(_ touches: Set, with event: UIEvent?) {
67 | // Check if any touch is inside one of the subviews, if so, ignore it
68 | if !isTouchInsideSubview(touches) {
69 | // If touch is not inside any subview, pass the touch to the next responder
70 | super.touchesBegan(touches, with: event)
71 | }
72 | }
73 |
74 | override func touchesMoved(_ touches: Set, with event: UIEvent?) {
75 | if !isTouchInsideSubview(touches) {
76 | super.touchesMoved(touches, with: event)
77 | }
78 | }
79 |
80 | override func touchesEnded(_ touches: Set, with event: UIEvent?) {
81 | if !isTouchInsideSubview(touches) {
82 | super.touchesEnded(touches, with: event)
83 | }
84 | }
85 |
86 | override func touchesCancelled(_ touches: Set, with event: UIEvent?) {
87 | if !isTouchInsideSubview(touches) {
88 | super.touchesCancelled(touches, with: event)
89 | }
90 | }
91 |
92 | // Helper function to determine if any touch is inside a subview
93 | private func isTouchInsideSubview(_ touches: Set) -> Bool {
94 | guard let touch = touches.first else {
95 | return false
96 | }
97 |
98 | let touchLocation = touch.location(in: self.view)
99 |
100 | // Iterate over all subviews to check if the touch is inside any of them
101 | for subview in self.view.subviews {
102 | if subview.frame.contains(touchLocation) {
103 | return true
104 | }
105 | }
106 | return false
107 | }
108 | }
109 |
--------------------------------------------------------------------------------