├── .gitignore
├── .swift-version
├── BlobMenu.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
└── xcshareddata
│ └── xcschemes
│ └── BlobMenu.xcscheme
├── Example
├── Example.xcodeproj
│ ├── project.pbxproj
│ └── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
└── Example
│ ├── App
│ ├── AppDelegate.swift
│ ├── Environment.swift
│ ├── SceneDelegate.swift
│ └── Theme.swift
│ ├── Preview Content
│ └── Preview Assets.xcassets
│ │ └── Contents.json
│ ├── Resources
│ ├── Assets.xcassets
│ │ ├── AppIcon.appiconset
│ │ │ ├── Contents.json
│ │ │ ├── Ramotion.png
│ │ │ ├── icon-120.png
│ │ │ ├── icon-152.png
│ │ │ ├── icon-167.png
│ │ │ ├── icon-180.png
│ │ │ ├── icon-20.png
│ │ │ ├── icon-29.png
│ │ │ ├── icon-40.png
│ │ │ ├── icon-58.png
│ │ │ ├── icon-60.png
│ │ │ ├── icon-76.png
│ │ │ ├── icon-80.png
│ │ │ └── icon-87.png
│ │ ├── Contents.json
│ │ ├── Icon_Calendar_selected.imageset
│ │ │ ├── Contents.json
│ │ │ └── Icon_Calendar_selected.pdf
│ │ ├── Icon_Calendar_unselected.imageset
│ │ │ ├── Contents.json
│ │ │ └── Icon_Calendar_unselected.pdf
│ │ ├── Icon_Chat_selected.imageset
│ │ │ ├── Contents.json
│ │ │ └── Icon_Chat_selected.pdf
│ │ ├── Icon_Chat_unselected.imageset
│ │ │ ├── Contents.json
│ │ │ └── Icon_Chat_unselected.pdf
│ │ ├── Icon_Favorite_selected.imageset
│ │ │ ├── Contents.json
│ │ │ └── Icon_Favorite_selected.pdf
│ │ ├── Icon_Favorite_unselected.imageset
│ │ │ ├── Contents.json
│ │ │ └── Icon_Favorite_unselected.pdf
│ │ ├── Icon_Profile_selected.imageset
│ │ │ ├── Contents.json
│ │ │ └── Icon_Profile_selected.pdf
│ │ ├── Icon_Profile_unselected.imageset
│ │ │ ├── Contents.json
│ │ │ └── Icon_Profile_unselected.pdf
│ │ ├── Icon_swipe.imageset
│ │ │ ├── Contents.json
│ │ │ ├── Icon_Swipe_dark.pdf
│ │ │ └── Icon_Swipe_light.pdf
│ │ └── Logo.imageset
│ │ │ ├── Contents.json
│ │ │ ├── Logo_dark.pdf
│ │ │ └── Logo_light.pdf
│ ├── Base.lproj
│ │ └── LaunchScreen.storyboard
│ └── Info.plist
│ ├── Screens
│ ├── FirstView.swift
│ ├── FourthView.swift
│ ├── RootView.swift
│ ├── SecondView.swift
│ └── ThirdView.swift
│ ├── UI
│ └── ExtendedScrollView.swift
│ └── Utilities
│ ├── Screen.swift
│ └── Utilities.swift
├── LICENSE
├── Package.swift
├── Promo
├── Blob-Menu-dark.gif
├── Blob-Menu-full.gif
├── Blob-Menu-light.gif
└── Header.png
├── README.md
├── Sources
├── BlobMenu.h
├── Configuration
│ ├── BlobMenuConfiguration.swift
│ └── Theme.swift
├── Effects
│ └── Transitions.swift
├── Extensions
│ ├── CGPoint+Extensions.swift
│ ├── CGRect+Extensions.swift
│ ├── CGSize+Extensions.swift
│ ├── Collection+Extensions.swift
│ ├── Comparable+Extensions.swift
│ ├── UIGestureRecognizer+Extensions.swift
│ └── UIWindow+Extensions.swift
├── Info.plist
├── Models
│ ├── BlobMenuItem.swift
│ └── BlobMenuModel.swift
├── Utilities
│ ├── AnimationCompletion.swift
│ ├── BezierUtilities.swift
│ ├── CommonUtilities.swift
│ ├── KayframesAnimation.swift
│ ├── ScaleKeyframesAnimation.swift
│ ├── SizeKeyframesAnimation.swift
│ ├── Then.swift
│ └── ViewSwapper.swift
└── Views
│ ├── BackgroundView.swift
│ ├── BlobMenuView.swift
│ ├── HamburgerView.swift
│ ├── MenuItemView.swift
│ ├── StickyEffectView.swift
│ └── StickyPathGenerator.swift
└── blob-menu.podspec
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## Build generated
6 | build/
7 | DerivedData/
8 |
9 | ## Various settings
10 | *.pbxuser
11 | !default.pbxuser
12 | *.mode1v3
13 | !default.mode1v3
14 | *.mode2v3
15 | !default.mode2v3
16 | *.perspectivev3
17 | !default.perspectivev3
18 | xcuserdata/
19 |
20 | ## Other
21 | *.moved-aside
22 | *.xccheckout
23 | *.xcscmblueprint
24 |
25 | ## Obj-C/Swift specific
26 | *.hmap
27 | *.ipa
28 | *.dSYM.zip
29 | *.dSYM
30 |
31 | ## Playgrounds
32 | timeline.xctimeline
33 | playground.xcworkspace
34 |
35 | # Swift Package Manager
36 | #
37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
38 | # Packages/
39 | # Package.pins
40 | .build/
41 |
42 | # CocoaPods
43 | #
44 | # We recommend against adding the Pods directory to your .gitignore. However
45 | # you should judge for yourself, the pros and cons are mentioned at:
46 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
47 | #
48 | # Pods/
49 |
50 | # Carthage
51 | #
52 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
53 | # Carthage/Checkouts
54 |
55 | Carthage/Build
56 |
57 | # fastlane
58 | #
59 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
60 | # screenshots whenever they are needed.
61 | # For more information about the recommended setup visit:
62 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
63 |
64 | fastlane/report.xml
65 | fastlane/Preview.html
66 | fastlane/screenshots
67 | fastlane/test_output
68 |
--------------------------------------------------------------------------------
/.swift-version:
--------------------------------------------------------------------------------
1 | 5.0
2 |
--------------------------------------------------------------------------------
/BlobMenu.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/BlobMenu.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/BlobMenu.xcodeproj/xcshareddata/xcschemes/BlobMenu.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
43 |
44 |
50 |
51 |
57 |
58 |
59 |
60 |
62 |
63 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/Example/Example.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 50;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 3908003B2447D01D00E7727C /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3908003A2447D01D00E7727C /* AppDelegate.swift */; };
11 | 3908003D2447D01D00E7727C /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3908003C2447D01D00E7727C /* SceneDelegate.swift */; };
12 | 3908003F2447D01D00E7727C /* RootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3908003E2447D01D00E7727C /* RootView.swift */; };
13 | 390800412447D01F00E7727C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 390800402447D01F00E7727C /* Assets.xcassets */; };
14 | 390800442447D01F00E7727C /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 390800432447D01F00E7727C /* Preview Assets.xcassets */; };
15 | 390800472447D01F00E7727C /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 390800452447D01F00E7727C /* LaunchScreen.storyboard */; };
16 | 3908005A2447D25700E7727C /* BlobMenu.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 390800562447D23300E7727C /* BlobMenu.framework */; };
17 | 3908005B2447D25700E7727C /* BlobMenu.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 390800562447D23300E7727C /* BlobMenu.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
18 | 390800602447D49200E7727C /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = 3908005F2447D49200E7727C /* README.md */; };
19 | 393BF69A24742B4F004D193D /* ExtendedScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 393BF69924742B4F004D193D /* ExtendedScrollView.swift */; };
20 | 3948454224774B8A0046236D /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3948454124774B8A0046236D /* Theme.swift */; };
21 | 3948454424774EF40046236D /* Screen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3948454324774EF40046236D /* Screen.swift */; };
22 | 398270BF2475756E00BB7A2B /* SecondView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 398270BE2475756E00BB7A2B /* SecondView.swift */; };
23 | 398270C1247575A600BB7A2B /* ThirdView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 398270C0247575A600BB7A2B /* ThirdView.swift */; };
24 | 398270C3247575B500BB7A2B /* FourthView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 398270C2247575B500BB7A2B /* FourthView.swift */; };
25 | 39A6D75D2479E77C00E22881 /* Environment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39A6D75C2479E77C00E22881 /* Environment.swift */; };
26 | 39A6D8AB246172AE0090F507 /* FirstView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39A6D8AA246172AE0090F507 /* FirstView.swift */; };
27 | 39A6D8AF24619B650090F507 /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39A6D8AE24619B650090F507 /* Utilities.swift */; };
28 | /* End PBXBuildFile section */
29 |
30 | /* Begin PBXContainerItemProxy section */
31 | 390800552447D23300E7727C /* PBXContainerItemProxy */ = {
32 | isa = PBXContainerItemProxy;
33 | containerPortal = 390800512447D23300E7727C /* BlobMenu.xcodeproj */;
34 | proxyType = 2;
35 | remoteGlobalIDString = 3908002324474A3800E7727C;
36 | remoteInfo = BlobMenu;
37 | };
38 | 390800572447D24500E7727C /* PBXContainerItemProxy */ = {
39 | isa = PBXContainerItemProxy;
40 | containerPortal = 390800512447D23300E7727C /* BlobMenu.xcodeproj */;
41 | proxyType = 1;
42 | remoteGlobalIDString = 3908002224474A3800E7727C;
43 | remoteInfo = BlobMenu;
44 | };
45 | /* End PBXContainerItemProxy section */
46 |
47 | /* Begin PBXCopyFilesBuildPhase section */
48 | 3908005C2447D25700E7727C /* Embed Frameworks */ = {
49 | isa = PBXCopyFilesBuildPhase;
50 | buildActionMask = 2147483647;
51 | dstPath = "";
52 | dstSubfolderSpec = 10;
53 | files = (
54 | 3908005B2447D25700E7727C /* BlobMenu.framework in Embed Frameworks */,
55 | );
56 | name = "Embed Frameworks";
57 | runOnlyForDeploymentPostprocessing = 0;
58 | };
59 | /* End PBXCopyFilesBuildPhase section */
60 |
61 | /* Begin PBXFileReference section */
62 | 390800372447D01D00E7727C /* Blob Menu.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Blob Menu.app"; sourceTree = BUILT_PRODUCTS_DIR; };
63 | 3908003A2447D01D00E7727C /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
64 | 3908003C2447D01D00E7727C /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; };
65 | 3908003E2447D01D00E7727C /* RootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootView.swift; sourceTree = ""; };
66 | 390800402447D01F00E7727C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
67 | 390800432447D01F00E7727C /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; };
68 | 390800462447D01F00E7727C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
69 | 390800482447D01F00E7727C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
70 | 390800512447D23300E7727C /* BlobMenu.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = BlobMenu.xcodeproj; path = ../BlobMenu.xcodeproj; sourceTree = ""; };
71 | 3908005F2447D49200E7727C /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../../../README.md; sourceTree = ""; };
72 | 393BF69924742B4F004D193D /* ExtendedScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtendedScrollView.swift; sourceTree = ""; };
73 | 3948454124774B8A0046236D /* Theme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Theme.swift; sourceTree = ""; };
74 | 3948454324774EF40046236D /* Screen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Screen.swift; sourceTree = ""; };
75 | 398270BE2475756E00BB7A2B /* SecondView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecondView.swift; sourceTree = ""; };
76 | 398270C0247575A600BB7A2B /* ThirdView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThirdView.swift; sourceTree = ""; };
77 | 398270C2247575B500BB7A2B /* FourthView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FourthView.swift; sourceTree = ""; };
78 | 39A6D75C2479E77C00E22881 /* Environment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Environment.swift; sourceTree = ""; };
79 | 39A6D8AA246172AE0090F507 /* FirstView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FirstView.swift; sourceTree = ""; };
80 | 39A6D8AE24619B650090F507 /* Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utilities.swift; sourceTree = ""; };
81 | /* End PBXFileReference section */
82 |
83 | /* Begin PBXFrameworksBuildPhase section */
84 | 390800342447D01D00E7727C /* Frameworks */ = {
85 | isa = PBXFrameworksBuildPhase;
86 | buildActionMask = 2147483647;
87 | files = (
88 | 3908005A2447D25700E7727C /* BlobMenu.framework in Frameworks */,
89 | );
90 | runOnlyForDeploymentPostprocessing = 0;
91 | };
92 | /* End PBXFrameworksBuildPhase section */
93 |
94 | /* Begin PBXGroup section */
95 | 3908002E2447D01D00E7727C = {
96 | isa = PBXGroup;
97 | children = (
98 | 390800512447D23300E7727C /* BlobMenu.xcodeproj */,
99 | 390800392447D01D00E7727C /* Example */,
100 | 390800382447D01D00E7727C /* Products */,
101 | 390800592447D25700E7727C /* Frameworks */,
102 | );
103 | sourceTree = "";
104 | };
105 | 390800382447D01D00E7727C /* Products */ = {
106 | isa = PBXGroup;
107 | children = (
108 | 390800372447D01D00E7727C /* Blob Menu.app */,
109 | );
110 | name = Products;
111 | sourceTree = "";
112 | };
113 | 390800392447D01D00E7727C /* Example */ = {
114 | isa = PBXGroup;
115 | children = (
116 | 3908004E2447D04800E7727C /* App */,
117 | 390800502447D0D500E7727C /* Screens */,
118 | 393BF69824741D33004D193D /* UI */,
119 | 39A6D8B02461AC180090F507 /* Utilities */,
120 | 3908004F2447D05A00E7727C /* Resources */,
121 | 390800422447D01F00E7727C /* Preview Content */,
122 | );
123 | path = Example;
124 | sourceTree = "";
125 | };
126 | 390800422447D01F00E7727C /* Preview Content */ = {
127 | isa = PBXGroup;
128 | children = (
129 | 390800432447D01F00E7727C /* Preview Assets.xcassets */,
130 | );
131 | path = "Preview Content";
132 | sourceTree = "";
133 | };
134 | 3908004E2447D04800E7727C /* App */ = {
135 | isa = PBXGroup;
136 | children = (
137 | 3908003A2447D01D00E7727C /* AppDelegate.swift */,
138 | 3908003C2447D01D00E7727C /* SceneDelegate.swift */,
139 | 3948454124774B8A0046236D /* Theme.swift */,
140 | 39A6D75C2479E77C00E22881 /* Environment.swift */,
141 | );
142 | path = App;
143 | sourceTree = "";
144 | };
145 | 3908004F2447D05A00E7727C /* Resources */ = {
146 | isa = PBXGroup;
147 | children = (
148 | 3908005F2447D49200E7727C /* README.md */,
149 | 390800402447D01F00E7727C /* Assets.xcassets */,
150 | 390800452447D01F00E7727C /* LaunchScreen.storyboard */,
151 | 390800482447D01F00E7727C /* Info.plist */,
152 | );
153 | path = Resources;
154 | sourceTree = "";
155 | };
156 | 390800502447D0D500E7727C /* Screens */ = {
157 | isa = PBXGroup;
158 | children = (
159 | 3908003E2447D01D00E7727C /* RootView.swift */,
160 | 39A6D8AA246172AE0090F507 /* FirstView.swift */,
161 | 398270BE2475756E00BB7A2B /* SecondView.swift */,
162 | 398270C0247575A600BB7A2B /* ThirdView.swift */,
163 | 398270C2247575B500BB7A2B /* FourthView.swift */,
164 | );
165 | path = Screens;
166 | sourceTree = "";
167 | };
168 | 390800522447D23300E7727C /* Products */ = {
169 | isa = PBXGroup;
170 | children = (
171 | 390800562447D23300E7727C /* BlobMenu.framework */,
172 | );
173 | name = Products;
174 | sourceTree = "";
175 | };
176 | 390800592447D25700E7727C /* Frameworks */ = {
177 | isa = PBXGroup;
178 | children = (
179 | );
180 | name = Frameworks;
181 | sourceTree = "";
182 | };
183 | 393BF69824741D33004D193D /* UI */ = {
184 | isa = PBXGroup;
185 | children = (
186 | 393BF69924742B4F004D193D /* ExtendedScrollView.swift */,
187 | );
188 | path = UI;
189 | sourceTree = "";
190 | };
191 | 39A6D8B02461AC180090F507 /* Utilities */ = {
192 | isa = PBXGroup;
193 | children = (
194 | 39A6D8AE24619B650090F507 /* Utilities.swift */,
195 | 3948454324774EF40046236D /* Screen.swift */,
196 | );
197 | path = Utilities;
198 | sourceTree = "";
199 | };
200 | /* End PBXGroup section */
201 |
202 | /* Begin PBXNativeTarget section */
203 | 390800362447D01D00E7727C /* Example */ = {
204 | isa = PBXNativeTarget;
205 | buildConfigurationList = 3908004B2447D01F00E7727C /* Build configuration list for PBXNativeTarget "Example" */;
206 | buildPhases = (
207 | 390800332447D01D00E7727C /* Sources */,
208 | 390800342447D01D00E7727C /* Frameworks */,
209 | 390800352447D01D00E7727C /* Resources */,
210 | 3908005C2447D25700E7727C /* Embed Frameworks */,
211 | );
212 | buildRules = (
213 | );
214 | dependencies = (
215 | 390800582447D24500E7727C /* PBXTargetDependency */,
216 | );
217 | name = Example;
218 | productName = Example;
219 | productReference = 390800372447D01D00E7727C /* Blob Menu.app */;
220 | productType = "com.apple.product-type.application";
221 | };
222 | /* End PBXNativeTarget section */
223 |
224 | /* Begin PBXProject section */
225 | 3908002F2447D01D00E7727C /* Project object */ = {
226 | isa = PBXProject;
227 | attributes = {
228 | LastSwiftUpdateCheck = 1140;
229 | LastUpgradeCheck = 1140;
230 | ORGANIZATIONNAME = Ramotion;
231 | TargetAttributes = {
232 | 390800362447D01D00E7727C = {
233 | CreatedOnToolsVersion = 11.4;
234 | };
235 | };
236 | };
237 | buildConfigurationList = 390800322447D01D00E7727C /* Build configuration list for PBXProject "Example" */;
238 | compatibilityVersion = "Xcode 9.3";
239 | developmentRegion = en;
240 | hasScannedForEncodings = 0;
241 | knownRegions = (
242 | en,
243 | Base,
244 | );
245 | mainGroup = 3908002E2447D01D00E7727C;
246 | productRefGroup = 390800382447D01D00E7727C /* Products */;
247 | projectDirPath = "";
248 | projectReferences = (
249 | {
250 | ProductGroup = 390800522447D23300E7727C /* Products */;
251 | ProjectRef = 390800512447D23300E7727C /* BlobMenu.xcodeproj */;
252 | },
253 | );
254 | projectRoot = "";
255 | targets = (
256 | 390800362447D01D00E7727C /* Example */,
257 | );
258 | };
259 | /* End PBXProject section */
260 |
261 | /* Begin PBXReferenceProxy section */
262 | 390800562447D23300E7727C /* BlobMenu.framework */ = {
263 | isa = PBXReferenceProxy;
264 | fileType = wrapper.framework;
265 | path = BlobMenu.framework;
266 | remoteRef = 390800552447D23300E7727C /* PBXContainerItemProxy */;
267 | sourceTree = BUILT_PRODUCTS_DIR;
268 | };
269 | /* End PBXReferenceProxy section */
270 |
271 | /* Begin PBXResourcesBuildPhase section */
272 | 390800352447D01D00E7727C /* Resources */ = {
273 | isa = PBXResourcesBuildPhase;
274 | buildActionMask = 2147483647;
275 | files = (
276 | 390800472447D01F00E7727C /* LaunchScreen.storyboard in Resources */,
277 | 390800442447D01F00E7727C /* Preview Assets.xcassets in Resources */,
278 | 390800602447D49200E7727C /* README.md in Resources */,
279 | 390800412447D01F00E7727C /* Assets.xcassets in Resources */,
280 | );
281 | runOnlyForDeploymentPostprocessing = 0;
282 | };
283 | /* End PBXResourcesBuildPhase section */
284 |
285 | /* Begin PBXSourcesBuildPhase section */
286 | 390800332447D01D00E7727C /* Sources */ = {
287 | isa = PBXSourcesBuildPhase;
288 | buildActionMask = 2147483647;
289 | files = (
290 | 39A6D8AF24619B650090F507 /* Utilities.swift in Sources */,
291 | 398270C1247575A600BB7A2B /* ThirdView.swift in Sources */,
292 | 3908003B2447D01D00E7727C /* AppDelegate.swift in Sources */,
293 | 39A6D75D2479E77C00E22881 /* Environment.swift in Sources */,
294 | 398270BF2475756E00BB7A2B /* SecondView.swift in Sources */,
295 | 393BF69A24742B4F004D193D /* ExtendedScrollView.swift in Sources */,
296 | 3948454224774B8A0046236D /* Theme.swift in Sources */,
297 | 3908003D2447D01D00E7727C /* SceneDelegate.swift in Sources */,
298 | 398270C3247575B500BB7A2B /* FourthView.swift in Sources */,
299 | 3908003F2447D01D00E7727C /* RootView.swift in Sources */,
300 | 3948454424774EF40046236D /* Screen.swift in Sources */,
301 | 39A6D8AB246172AE0090F507 /* FirstView.swift in Sources */,
302 | );
303 | runOnlyForDeploymentPostprocessing = 0;
304 | };
305 | /* End PBXSourcesBuildPhase section */
306 |
307 | /* Begin PBXTargetDependency section */
308 | 390800582447D24500E7727C /* PBXTargetDependency */ = {
309 | isa = PBXTargetDependency;
310 | name = BlobMenu;
311 | targetProxy = 390800572447D24500E7727C /* PBXContainerItemProxy */;
312 | };
313 | /* End PBXTargetDependency section */
314 |
315 | /* Begin PBXVariantGroup section */
316 | 390800452447D01F00E7727C /* LaunchScreen.storyboard */ = {
317 | isa = PBXVariantGroup;
318 | children = (
319 | 390800462447D01F00E7727C /* Base */,
320 | );
321 | name = LaunchScreen.storyboard;
322 | sourceTree = "";
323 | };
324 | /* End PBXVariantGroup section */
325 |
326 | /* Begin XCBuildConfiguration section */
327 | 390800492447D01F00E7727C /* Debug */ = {
328 | isa = XCBuildConfiguration;
329 | buildSettings = {
330 | ALWAYS_SEARCH_USER_PATHS = NO;
331 | CLANG_ANALYZER_NONNULL = YES;
332 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
333 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
334 | CLANG_CXX_LIBRARY = "libc++";
335 | CLANG_ENABLE_MODULES = YES;
336 | CLANG_ENABLE_OBJC_ARC = YES;
337 | CLANG_ENABLE_OBJC_WEAK = YES;
338 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
339 | CLANG_WARN_BOOL_CONVERSION = YES;
340 | CLANG_WARN_COMMA = YES;
341 | CLANG_WARN_CONSTANT_CONVERSION = YES;
342 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
343 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
344 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
345 | CLANG_WARN_EMPTY_BODY = YES;
346 | CLANG_WARN_ENUM_CONVERSION = YES;
347 | CLANG_WARN_INFINITE_RECURSION = YES;
348 | CLANG_WARN_INT_CONVERSION = YES;
349 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
350 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
351 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
352 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
353 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
354 | CLANG_WARN_STRICT_PROTOTYPES = YES;
355 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
356 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
357 | CLANG_WARN_UNREACHABLE_CODE = YES;
358 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
359 | COPY_PHASE_STRIP = NO;
360 | DEBUG_INFORMATION_FORMAT = dwarf;
361 | ENABLE_STRICT_OBJC_MSGSEND = YES;
362 | ENABLE_TESTABILITY = YES;
363 | GCC_C_LANGUAGE_STANDARD = gnu11;
364 | GCC_DYNAMIC_NO_PIC = NO;
365 | GCC_NO_COMMON_BLOCKS = YES;
366 | GCC_OPTIMIZATION_LEVEL = 0;
367 | GCC_PREPROCESSOR_DEFINITIONS = (
368 | "DEBUG=1",
369 | "$(inherited)",
370 | );
371 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
372 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
373 | GCC_WARN_UNDECLARED_SELECTOR = YES;
374 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
375 | GCC_WARN_UNUSED_FUNCTION = YES;
376 | GCC_WARN_UNUSED_VARIABLE = YES;
377 | IPHONEOS_DEPLOYMENT_TARGET = 13.4;
378 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
379 | MTL_FAST_MATH = YES;
380 | ONLY_ACTIVE_ARCH = YES;
381 | SDKROOT = iphoneos;
382 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
383 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
384 | };
385 | name = Debug;
386 | };
387 | 3908004A2447D01F00E7727C /* Release */ = {
388 | isa = XCBuildConfiguration;
389 | buildSettings = {
390 | ALWAYS_SEARCH_USER_PATHS = NO;
391 | CLANG_ANALYZER_NONNULL = YES;
392 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
393 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
394 | CLANG_CXX_LIBRARY = "libc++";
395 | CLANG_ENABLE_MODULES = YES;
396 | CLANG_ENABLE_OBJC_ARC = YES;
397 | CLANG_ENABLE_OBJC_WEAK = YES;
398 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
399 | CLANG_WARN_BOOL_CONVERSION = YES;
400 | CLANG_WARN_COMMA = YES;
401 | CLANG_WARN_CONSTANT_CONVERSION = YES;
402 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
403 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
404 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
405 | CLANG_WARN_EMPTY_BODY = YES;
406 | CLANG_WARN_ENUM_CONVERSION = YES;
407 | CLANG_WARN_INFINITE_RECURSION = YES;
408 | CLANG_WARN_INT_CONVERSION = YES;
409 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
410 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
411 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
412 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
413 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
414 | CLANG_WARN_STRICT_PROTOTYPES = YES;
415 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
416 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
417 | CLANG_WARN_UNREACHABLE_CODE = YES;
418 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
419 | COPY_PHASE_STRIP = NO;
420 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
421 | ENABLE_NS_ASSERTIONS = NO;
422 | ENABLE_STRICT_OBJC_MSGSEND = YES;
423 | GCC_C_LANGUAGE_STANDARD = gnu11;
424 | GCC_NO_COMMON_BLOCKS = YES;
425 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
426 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
427 | GCC_WARN_UNDECLARED_SELECTOR = YES;
428 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
429 | GCC_WARN_UNUSED_FUNCTION = YES;
430 | GCC_WARN_UNUSED_VARIABLE = YES;
431 | IPHONEOS_DEPLOYMENT_TARGET = 13.4;
432 | MTL_ENABLE_DEBUG_INFO = NO;
433 | MTL_FAST_MATH = YES;
434 | SDKROOT = iphoneos;
435 | SWIFT_COMPILATION_MODE = wholemodule;
436 | SWIFT_OPTIMIZATION_LEVEL = "-O";
437 | VALIDATE_PRODUCT = YES;
438 | };
439 | name = Release;
440 | };
441 | 3908004C2447D01F00E7727C /* Debug */ = {
442 | isa = XCBuildConfiguration;
443 | buildSettings = {
444 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
445 | CODE_SIGN_STYLE = Automatic;
446 | DEVELOPMENT_ASSET_PATHS = "\"Example/Preview Content\"";
447 | DEVELOPMENT_TEAM = 34MUF9YXTA;
448 | ENABLE_PREVIEWS = YES;
449 | INFOPLIST_FILE = Example/Resources/Info.plist;
450 | IPHONEOS_DEPLOYMENT_TARGET = 13.0;
451 | LD_RUNPATH_SEARCH_PATHS = (
452 | "$(inherited)",
453 | "@executable_path/Frameworks",
454 | );
455 | PRODUCT_BUNDLE_IDENTIFIER = com.ramotion.Example;
456 | PRODUCT_NAME = "Blob Menu";
457 | SWIFT_VERSION = 5.0;
458 | TARGETED_DEVICE_FAMILY = 1;
459 | };
460 | name = Debug;
461 | };
462 | 3908004D2447D01F00E7727C /* Release */ = {
463 | isa = XCBuildConfiguration;
464 | buildSettings = {
465 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
466 | CODE_SIGN_STYLE = Automatic;
467 | DEVELOPMENT_ASSET_PATHS = "\"Example/Preview Content\"";
468 | DEVELOPMENT_TEAM = 34MUF9YXTA;
469 | ENABLE_PREVIEWS = YES;
470 | INFOPLIST_FILE = Example/Resources/Info.plist;
471 | IPHONEOS_DEPLOYMENT_TARGET = 13.0;
472 | LD_RUNPATH_SEARCH_PATHS = (
473 | "$(inherited)",
474 | "@executable_path/Frameworks",
475 | );
476 | PRODUCT_BUNDLE_IDENTIFIER = com.ramotion.Example;
477 | PRODUCT_NAME = "Blob Menu";
478 | SWIFT_VERSION = 5.0;
479 | TARGETED_DEVICE_FAMILY = 1;
480 | };
481 | name = Release;
482 | };
483 | /* End XCBuildConfiguration section */
484 |
485 | /* Begin XCConfigurationList section */
486 | 390800322447D01D00E7727C /* Build configuration list for PBXProject "Example" */ = {
487 | isa = XCConfigurationList;
488 | buildConfigurations = (
489 | 390800492447D01F00E7727C /* Debug */,
490 | 3908004A2447D01F00E7727C /* Release */,
491 | );
492 | defaultConfigurationIsVisible = 0;
493 | defaultConfigurationName = Release;
494 | };
495 | 3908004B2447D01F00E7727C /* Build configuration list for PBXNativeTarget "Example" */ = {
496 | isa = XCConfigurationList;
497 | buildConfigurations = (
498 | 3908004C2447D01F00E7727C /* Debug */,
499 | 3908004D2447D01F00E7727C /* Release */,
500 | );
501 | defaultConfigurationIsVisible = 0;
502 | defaultConfigurationName = Release;
503 | };
504 | /* End XCConfigurationList section */
505 | };
506 | rootObject = 3908002F2447D01D00E7727C /* Project object */;
507 | }
508 |
--------------------------------------------------------------------------------
/Example/Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Example/Example.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Example/Example/App/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // Example
4 | //
5 | // Created by Igor K. on 16.04.2020.
6 | // Copyright © 2020 Ramotion. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import BlobMenu
11 |
12 | @UIApplicationMain
13 | final class AppDelegate: UIResponder, UIApplicationDelegate {
14 |
15 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
16 |
17 | return true
18 | }
19 |
20 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
21 |
22 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
23 | }
24 | }
25 |
26 |
--------------------------------------------------------------------------------
/Example/Example/App/Environment.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Environment.swift
3 | // Example
4 | //
5 | // Created by Igor K. on 24.05.2020.
6 | // Copyright © 2020 Ramotion. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SwiftUI
11 | import BlobMenu
12 |
13 |
14 | extension EnvironmentValues {
15 | var menuEnvironment: BlobMenuModel {
16 | get { return self[MenuEnvironmentKey.self] }
17 | set { self[MenuEnvironmentKey.self] = newValue }
18 | }
19 | }
20 |
21 | struct MenuEnvironmentKey: EnvironmentKey {
22 | static let defaultValue = BlobMenuModel(items: BlobMenuItem.standard)
23 | }
24 |
25 | extension BlobMenuItem {
26 | static let standard: [BlobMenuItem] = [
27 | BlobMenuItem(selectedIcon: Image.calendarSelected, unselectedIcon: Image.calendarUnselected),
28 | BlobMenuItem(selectedIcon: Image.chatSelected, unselectedIcon: Image.chatUnselected),
29 | BlobMenuItem(selectedIcon: Image.favoriteSelected, unselectedIcon: Image.favoriteUnselected),
30 | ]
31 |
32 | static let extended: [BlobMenuItem] =
33 | standard + [BlobMenuItem(selectedIcon: Image.profileSelected, unselectedIcon: Image.profileUnselected)]
34 | }
35 |
36 |
37 | extension Image {
38 | static let calendarSelected = Image("Icon_Calendar_selected")
39 | static let calendarUnselected = Image("Icon_Calendar_unselected")
40 | static let chatSelected = Image("Icon_Chat_selected")
41 | static let chatUnselected = Image("Icon_Chat_unselected")
42 | static let favoriteSelected = Image("Icon_Favorite_selected")
43 | static let favoriteUnselected = Image("Icon_Favorite_unselected")
44 | static let profileSelected = Image("Icon_Profile_selected")
45 | static let profileUnselected = Image("Icon_Profile_unselected")
46 | }
47 |
--------------------------------------------------------------------------------
/Example/Example/App/SceneDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SceneDelegate.swift
3 | // Example
4 | //
5 | // Created by Igor K. on 16.04.2020.
6 | // Copyright © 2020 Ramotion. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import SwiftUI
11 |
12 | final class SceneDelegate: UIResponder, UIWindowSceneDelegate {
13 |
14 | var window: UIWindow?
15 |
16 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
17 |
18 | if let windowScene = scene as? UIWindowScene {
19 | let window = UIWindow(windowScene: windowScene)
20 | let view = RootView().environmentObject(MenuEnvironmentKey.defaultValue)
21 | window.rootViewController = UIHostingController(rootView: view)
22 | self.window = window
23 | window.makeKeyAndVisible()
24 | }
25 | }
26 | }
27 |
28 |
--------------------------------------------------------------------------------
/Example/Example/App/Theme.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Theme.swift
3 | // Example
4 | //
5 | // Created by Igor K. on 22.05.2020.
6 | // Copyright © 2020 Ramotion. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SwiftUI
11 |
12 | extension Color {
13 |
14 | static var background: Color {
15 | return Color(UIColor { $0.userInterfaceStyle == .dark ? #colorLiteral(red: 0.1960526407, green: 0.1960932612, blue: 0.1960500479, alpha: 1) : #colorLiteral(red: 0.9998916984, green: 1, blue: 0.9998809695, alpha: 1) })
16 | }
17 |
18 | static var cardBackgound: Color {
19 | return Color(UIColor { $0.userInterfaceStyle == .dark ? #colorLiteral(red: 0.2470277846, green: 0.2470766604, blue: 0.2470246851, alpha: 1) : #colorLiteral(red: 0.9998916984, green: 1, blue: 0.9998809695, alpha: 1) })
20 | }
21 |
22 | static var shadow: Color = Color.black.opacity(0.25)
23 |
24 | static var contrastText: Color {
25 | return Color(UIColor { $0.userInterfaceStyle == .dark ? #colorLiteral(red: 0.9528377652, green: 0.9530007243, blue: 0.9528275132, alpha: 1) : #colorLiteral(red: 0.1960526407, green: 0.1960932612, blue: 0.1960500479, alpha: 1) })
26 | }
27 |
28 | static var contrastInformation: Color {
29 | return Color(UIColor { $0.userInterfaceStyle == .dark ? #colorLiteral(red: 0.7371736169, green: 0.7373017669, blue: 0.7371655703, alpha: 1) : #colorLiteral(red: 0.5175882578, green: 0.517680943, blue: 0.5175824165, alpha: 1) })
30 | }
31 |
32 | static var buttonText: Color {
33 | return Color(UIColor { $0.userInterfaceStyle == .dark ? #colorLiteral(red: 0.9998916984, green: 1, blue: 0.9998809695, alpha: 1) : #colorLiteral(red: 0.09132350236, green: 0.3897008598, blue: 0.8640318513, alpha: 1) })
34 | }
35 |
36 | static var buttonBackground: Color {
37 | return Color(UIColor { $0.userInterfaceStyle == .dark ? #colorLiteral(red: 0.2744759619, green: 0.2745292187, blue: 0.2744725645, alpha: 1) : #colorLiteral(red: 0.9083589911, green: 0.9522650838, blue: 1, alpha: 1) })
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/Example/Example/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "20x20",
5 | "idiom" : "iphone",
6 | "filename" : "icon-40.png",
7 | "scale" : "2x"
8 | },
9 | {
10 | "size" : "20x20",
11 | "idiom" : "iphone",
12 | "filename" : "icon-60.png",
13 | "scale" : "3x"
14 | },
15 | {
16 | "size" : "29x29",
17 | "idiom" : "iphone",
18 | "filename" : "icon-58.png",
19 | "scale" : "2x"
20 | },
21 | {
22 | "size" : "29x29",
23 | "idiom" : "iphone",
24 | "filename" : "icon-87.png",
25 | "scale" : "3x"
26 | },
27 | {
28 | "size" : "40x40",
29 | "idiom" : "iphone",
30 | "filename" : "icon-80.png",
31 | "scale" : "2x"
32 | },
33 | {
34 | "size" : "40x40",
35 | "idiom" : "iphone",
36 | "filename" : "icon-120.png",
37 | "scale" : "3x"
38 | },
39 | {
40 | "size" : "60x60",
41 | "idiom" : "iphone",
42 | "filename" : "icon-120.png",
43 | "scale" : "2x"
44 | },
45 | {
46 | "size" : "60x60",
47 | "idiom" : "iphone",
48 | "filename" : "icon-180.png",
49 | "scale" : "3x"
50 | },
51 | {
52 | "size" : "20x20",
53 | "idiom" : "ipad",
54 | "filename" : "icon-20.png",
55 | "scale" : "1x"
56 | },
57 | {
58 | "size" : "20x20",
59 | "idiom" : "ipad",
60 | "filename" : "icon-40.png",
61 | "scale" : "2x"
62 | },
63 | {
64 | "size" : "29x29",
65 | "idiom" : "ipad",
66 | "filename" : "icon-29.png",
67 | "scale" : "1x"
68 | },
69 | {
70 | "size" : "29x29",
71 | "idiom" : "ipad",
72 | "filename" : "icon-58.png",
73 | "scale" : "2x"
74 | },
75 | {
76 | "size" : "40x40",
77 | "idiom" : "ipad",
78 | "filename" : "icon-40.png",
79 | "scale" : "1x"
80 | },
81 | {
82 | "size" : "40x40",
83 | "idiom" : "ipad",
84 | "filename" : "icon-80.png",
85 | "scale" : "2x"
86 | },
87 | {
88 | "size" : "76x76",
89 | "idiom" : "ipad",
90 | "filename" : "icon-76.png",
91 | "scale" : "1x"
92 | },
93 | {
94 | "size" : "76x76",
95 | "idiom" : "ipad",
96 | "filename" : "icon-152.png",
97 | "scale" : "2x"
98 | },
99 | {
100 | "size" : "83.5x83.5",
101 | "idiom" : "ipad",
102 | "filename" : "icon-167.png",
103 | "scale" : "2x"
104 | },
105 | {
106 | "size" : "1024x1024",
107 | "idiom" : "ios-marketing",
108 | "filename" : "Ramotion.png",
109 | "scale" : "1x"
110 | }
111 | ],
112 | "info" : {
113 | "version" : 1,
114 | "author" : "xcode"
115 | },
116 | "properties" : {
117 | "pre-rendered" : true
118 | }
119 | }
--------------------------------------------------------------------------------
/Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/Ramotion.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ramotion/blob-menu/4c67580e39dda4ddff0f75b9285e4a1919334fb6/Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/Ramotion.png
--------------------------------------------------------------------------------
/Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/icon-120.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ramotion/blob-menu/4c67580e39dda4ddff0f75b9285e4a1919334fb6/Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/icon-120.png
--------------------------------------------------------------------------------
/Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/icon-152.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ramotion/blob-menu/4c67580e39dda4ddff0f75b9285e4a1919334fb6/Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/icon-152.png
--------------------------------------------------------------------------------
/Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/icon-167.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ramotion/blob-menu/4c67580e39dda4ddff0f75b9285e4a1919334fb6/Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/icon-167.png
--------------------------------------------------------------------------------
/Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/icon-180.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ramotion/blob-menu/4c67580e39dda4ddff0f75b9285e4a1919334fb6/Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/icon-180.png
--------------------------------------------------------------------------------
/Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/icon-20.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ramotion/blob-menu/4c67580e39dda4ddff0f75b9285e4a1919334fb6/Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/icon-20.png
--------------------------------------------------------------------------------
/Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/icon-29.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ramotion/blob-menu/4c67580e39dda4ddff0f75b9285e4a1919334fb6/Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/icon-29.png
--------------------------------------------------------------------------------
/Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/icon-40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ramotion/blob-menu/4c67580e39dda4ddff0f75b9285e4a1919334fb6/Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/icon-40.png
--------------------------------------------------------------------------------
/Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/icon-58.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ramotion/blob-menu/4c67580e39dda4ddff0f75b9285e4a1919334fb6/Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/icon-58.png
--------------------------------------------------------------------------------
/Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/icon-60.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ramotion/blob-menu/4c67580e39dda4ddff0f75b9285e4a1919334fb6/Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/icon-60.png
--------------------------------------------------------------------------------
/Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/icon-76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ramotion/blob-menu/4c67580e39dda4ddff0f75b9285e4a1919334fb6/Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/icon-76.png
--------------------------------------------------------------------------------
/Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/icon-80.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ramotion/blob-menu/4c67580e39dda4ddff0f75b9285e4a1919334fb6/Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/icon-80.png
--------------------------------------------------------------------------------
/Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/icon-87.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ramotion/blob-menu/4c67580e39dda4ddff0f75b9285e4a1919334fb6/Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/icon-87.png
--------------------------------------------------------------------------------
/Example/Example/Resources/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Example/Example/Resources/Assets.xcassets/Icon_Calendar_selected.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Icon_Calendar_selected.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | },
12 | "properties" : {
13 | "preserves-vector-representation" : true
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Example/Example/Resources/Assets.xcassets/Icon_Calendar_selected.imageset/Icon_Calendar_selected.pdf:
--------------------------------------------------------------------------------
1 | %PDF-1.7
2 |
3 | 1 0 obj
4 | << >>
5 | endobj
6 |
7 | 2 0 obj
8 | << /Length 3 0 R >>
9 | stream
10 | /DeviceRGB CS
11 | /DeviceRGB cs
12 | q
13 | 1.000000 0.000000 -0.000000 1.000000 3.000000 20.500000 cm
14 | 0.196078 0.196078 0.196078 scn
15 | 24.000000 2.700000 m
16 | 24.000000 4.798682 22.298683 6.500000 20.200001 6.500000 c
17 | 3.800000 6.500000 l
18 | 1.701318 6.500000 0.000000 4.798682 0.000000 2.700000 c
19 | 0.000000 0.000000 l
20 | 24.000000 0.000000 l
21 | 24.000000 2.700000 l
22 | h
23 | f
24 | n
25 | Q
26 | q
27 | 1.000000 0.000000 -0.000000 1.000000 3.000000 1.000000 cm
28 | 0.196078 0.196078 0.196078 scn
29 | 3.800000 25.000000 m
30 | 20.200001 25.000000 l
31 | 20.200001 27.000000 l
32 | 3.800000 27.000000 l
33 | 3.800000 25.000000 l
34 | h
35 | 20.200001 3.000000 m
36 | 3.799999 3.000000 l
37 | 3.799999 1.000000 l
38 | 20.200001 1.000000 l
39 | 20.200001 3.000000 l
40 | h
41 | 1.000000 5.799999 m
42 | 1.000000 19.500000 l
43 | -1.000000 19.500000 l
44 | -1.000000 5.799999 l
45 | 1.000000 5.799999 l
46 | h
47 | 1.000000 19.500000 m
48 | 1.000000 22.200001 l
49 | -1.000000 22.200001 l
50 | -1.000000 19.500000 l
51 | 1.000000 19.500000 l
52 | h
53 | 23.000000 22.200001 m
54 | 23.000000 19.500000 l
55 | 25.000000 19.500000 l
56 | 25.000000 22.200001 l
57 | 23.000000 22.200001 l
58 | h
59 | 23.000000 19.500000 m
60 | 23.000000 5.800001 l
61 | 25.000000 5.800001 l
62 | 25.000000 19.500000 l
63 | 23.000000 19.500000 l
64 | h
65 | 0.000000 18.500000 m
66 | 24.000000 18.500000 l
67 | 24.000000 20.500000 l
68 | 0.000000 20.500000 l
69 | 0.000000 18.500000 l
70 | h
71 | 20.200001 1.000000 m
72 | 22.850967 1.000000 25.000000 3.149035 25.000000 5.800001 c
73 | 23.000000 5.800001 l
74 | 23.000000 4.253603 21.746397 3.000000 20.200001 3.000000 c
75 | 20.200001 1.000000 l
76 | h
77 | 20.200001 25.000000 m
78 | 21.746397 25.000000 23.000000 23.746397 23.000000 22.200001 c
79 | 25.000000 22.200001 l
80 | 25.000000 24.850967 22.850967 27.000000 20.200001 27.000000 c
81 | 20.200001 25.000000 l
82 | h
83 | 3.800000 27.000000 m
84 | 1.149033 27.000000 -1.000000 24.850967 -1.000000 22.200001 c
85 | 1.000000 22.200001 l
86 | 1.000000 23.746397 2.253603 25.000000 3.800000 25.000000 c
87 | 3.800000 27.000000 l
88 | h
89 | 3.799999 3.000000 m
90 | 2.253602 3.000000 1.000000 4.253603 1.000000 5.799999 c
91 | -1.000000 5.799999 l
92 | -1.000000 3.149033 1.149032 1.000000 3.799999 1.000000 c
93 | 3.799999 3.000000 l
94 | h
95 | f
96 | n
97 | Q
98 |
99 | endstream
100 | endobj
101 |
102 | 3 0 obj
103 | 1939
104 | endobj
105 |
106 | 4 0 obj
107 | << /Annots []
108 | /Type /Page
109 | /MediaBox [ 0.000000 0.000000 30.000000 30.000000 ]
110 | /Resources 1 0 R
111 | /Contents 2 0 R
112 | /Parent 5 0 R
113 | >>
114 | endobj
115 |
116 | 5 0 obj
117 | << /Kids [ 4 0 R ]
118 | /Count 1
119 | /Type /Pages
120 | >>
121 | endobj
122 |
123 | 6 0 obj
124 | << /Type /Catalog
125 | /Pages 5 0 R
126 | >>
127 | endobj
128 |
129 | xref
130 | 0 7
131 | 0000000000 65535 f
132 | 0000000010 00000 n
133 | 0000000034 00000 n
134 | 0000002029 00000 n
135 | 0000002052 00000 n
136 | 0000002225 00000 n
137 | 0000002299 00000 n
138 | trailer
139 | << /ID [ (some) (id) ]
140 | /Root 6 0 R
141 | /Size 7
142 | >>
143 | startxref
144 | 2358
145 | %%EOF
--------------------------------------------------------------------------------
/Example/Example/Resources/Assets.xcassets/Icon_Calendar_unselected.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Icon_Calendar_unselected.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | },
12 | "properties" : {
13 | "preserves-vector-representation" : true
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Example/Example/Resources/Assets.xcassets/Icon_Calendar_unselected.imageset/Icon_Calendar_unselected.pdf:
--------------------------------------------------------------------------------
1 | %PDF-1.7
2 |
3 | 1 0 obj
4 | << >>
5 | endobj
6 |
7 | 2 0 obj
8 | << /Length 3 0 R >>
9 | stream
10 | /DeviceRGB CS
11 | /DeviceRGB cs
12 | q
13 | 1.000000 0.000000 -0.000000 1.000000 3.000000 1.000000 cm
14 | 0.533333 0.533333 0.533333 scn
15 | 3.800000 25.000000 m
16 | 20.200001 25.000000 l
17 | 20.200001 27.000000 l
18 | 3.800000 27.000000 l
19 | 3.800000 25.000000 l
20 | h
21 | 20.200001 3.000000 m
22 | 3.799999 3.000000 l
23 | 3.799999 1.000000 l
24 | 20.200001 1.000000 l
25 | 20.200001 3.000000 l
26 | h
27 | 1.000000 5.799999 m
28 | 1.000000 19.500000 l
29 | -1.000000 19.500000 l
30 | -1.000000 5.799999 l
31 | 1.000000 5.799999 l
32 | h
33 | 1.000000 19.500000 m
34 | 1.000000 22.200001 l
35 | -1.000000 22.200001 l
36 | -1.000000 19.500000 l
37 | 1.000000 19.500000 l
38 | h
39 | 23.000000 22.200001 m
40 | 23.000000 19.500000 l
41 | 25.000000 19.500000 l
42 | 25.000000 22.200001 l
43 | 23.000000 22.200001 l
44 | h
45 | 23.000000 19.500000 m
46 | 23.000000 5.800001 l
47 | 25.000000 5.800001 l
48 | 25.000000 19.500000 l
49 | 23.000000 19.500000 l
50 | h
51 | 0.000000 18.500000 m
52 | 24.000000 18.500000 l
53 | 24.000000 20.500000 l
54 | 0.000000 20.500000 l
55 | 0.000000 18.500000 l
56 | h
57 | 20.200001 1.000000 m
58 | 22.850967 1.000000 25.000000 3.149035 25.000000 5.800001 c
59 | 23.000000 5.800001 l
60 | 23.000000 4.253603 21.746397 3.000000 20.200001 3.000000 c
61 | 20.200001 1.000000 l
62 | h
63 | 20.200001 25.000000 m
64 | 21.746397 25.000000 23.000000 23.746397 23.000000 22.200001 c
65 | 25.000000 22.200001 l
66 | 25.000000 24.850967 22.850967 27.000000 20.200001 27.000000 c
67 | 20.200001 25.000000 l
68 | h
69 | 3.800000 27.000000 m
70 | 1.149033 27.000000 -1.000000 24.850967 -1.000000 22.200001 c
71 | 1.000000 22.200001 l
72 | 1.000000 23.746397 2.253603 25.000000 3.800000 25.000000 c
73 | 3.800000 27.000000 l
74 | h
75 | 3.799999 3.000000 m
76 | 2.253602 3.000000 1.000000 4.253603 1.000000 5.799999 c
77 | -1.000000 5.799999 l
78 | -1.000000 3.149033 1.149032 1.000000 3.799999 1.000000 c
79 | 3.799999 3.000000 l
80 | h
81 | f
82 | n
83 | Q
84 |
85 | endstream
86 | endobj
87 |
88 | 3 0 obj
89 | 1621
90 | endobj
91 |
92 | 4 0 obj
93 | << /Annots []
94 | /Type /Page
95 | /MediaBox [ 0.000000 0.000000 30.000000 30.000000 ]
96 | /Resources 1 0 R
97 | /Contents 2 0 R
98 | /Parent 5 0 R
99 | >>
100 | endobj
101 |
102 | 5 0 obj
103 | << /Kids [ 4 0 R ]
104 | /Count 1
105 | /Type /Pages
106 | >>
107 | endobj
108 |
109 | 6 0 obj
110 | << /Type /Catalog
111 | /Pages 5 0 R
112 | >>
113 | endobj
114 |
115 | xref
116 | 0 7
117 | 0000000000 65535 f
118 | 0000000010 00000 n
119 | 0000000034 00000 n
120 | 0000001711 00000 n
121 | 0000001734 00000 n
122 | 0000001907 00000 n
123 | 0000001981 00000 n
124 | trailer
125 | << /ID [ (some) (id) ]
126 | /Root 6 0 R
127 | /Size 7
128 | >>
129 | startxref
130 | 2040
131 | %%EOF
--------------------------------------------------------------------------------
/Example/Example/Resources/Assets.xcassets/Icon_Chat_selected.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Icon_Chat_selected.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | },
12 | "properties" : {
13 | "preserves-vector-representation" : true
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Example/Example/Resources/Assets.xcassets/Icon_Chat_selected.imageset/Icon_Chat_selected.pdf:
--------------------------------------------------------------------------------
1 | %PDF-1.7
2 |
3 | 1 0 obj
4 | << >>
5 | endobj
6 |
7 | 2 0 obj
8 | << /Length 3 0 R >>
9 | stream
10 | /DeviceRGB CS
11 | /DeviceRGB cs
12 | q
13 | 1.000000 0.000000 -0.000000 1.000000 1.000000 1.093994 cm
14 | 0.196078 0.196078 0.196078 scn
15 | 0.000000 22.906006 m
16 | 0.000000 25.115145 1.790861 26.906006 4.000000 26.906006 c
17 | 24.000000 26.906006 l
18 | 26.209139 26.906006 28.000000 25.115145 28.000000 22.906006 c
19 | 28.000000 8.906006 l
20 | 28.000000 6.696867 26.209139 4.906006 24.000000 4.906006 c
21 | 15.250000 4.906006 l
22 | 8.555461 0.442980 l
23 | 7.890906 -0.000057 7.000000 0.475828 7.000000 1.274523 c
24 | 7.000000 4.906006 l
25 | 4.000000 4.906006 l
26 | 1.790861 4.906006 0.000000 6.696867 0.000000 8.906006 c
27 | 0.000000 22.906006 l
28 | h
29 | 8.000000 18.906006 m
30 | 8.000000 19.458290 8.447716 19.906006 9.000000 19.906006 c
31 | 19.000000 19.906006 l
32 | 19.552284 19.906006 20.000000 19.458290 20.000000 18.906006 c
33 | 20.000000 18.353722 19.552284 17.906006 19.000000 17.906006 c
34 | 9.000000 17.906006 l
35 | 8.447715 17.906006 8.000000 18.353722 8.000000 18.906006 c
36 | h
37 | 8.000000 12.906006 m
38 | 8.000000 13.458290 8.447716 13.906006 9.000000 13.906006 c
39 | 19.000000 13.906006 l
40 | 19.552284 13.906006 20.000000 13.458290 20.000000 12.906006 c
41 | 20.000000 12.353722 19.552284 11.906006 19.000000 11.906006 c
42 | 9.000000 11.906006 l
43 | 8.447715 11.906006 8.000000 12.353722 8.000000 12.906006 c
44 | h
45 | f*
46 | n
47 | Q
48 |
49 | endstream
50 | endobj
51 |
52 | 3 0 obj
53 | 1203
54 | endobj
55 |
56 | 4 0 obj
57 | << /Annots []
58 | /Type /Page
59 | /MediaBox [ 0.000000 0.000000 30.000000 30.000000 ]
60 | /Resources 1 0 R
61 | /Contents 2 0 R
62 | /Parent 5 0 R
63 | >>
64 | endobj
65 |
66 | 5 0 obj
67 | << /Kids [ 4 0 R ]
68 | /Count 1
69 | /Type /Pages
70 | >>
71 | endobj
72 |
73 | 6 0 obj
74 | << /Type /Catalog
75 | /Pages 5 0 R
76 | >>
77 | endobj
78 |
79 | xref
80 | 0 7
81 | 0000000000 65535 f
82 | 0000000010 00000 n
83 | 0000000034 00000 n
84 | 0000001293 00000 n
85 | 0000001316 00000 n
86 | 0000001489 00000 n
87 | 0000001563 00000 n
88 | trailer
89 | << /ID [ (some) (id) ]
90 | /Root 6 0 R
91 | /Size 7
92 | >>
93 | startxref
94 | 1622
95 | %%EOF
--------------------------------------------------------------------------------
/Example/Example/Resources/Assets.xcassets/Icon_Chat_unselected.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Icon_Chat_unselected.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | },
12 | "properties" : {
13 | "preserves-vector-representation" : true
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Example/Example/Resources/Assets.xcassets/Icon_Chat_unselected.imageset/Icon_Chat_unselected.pdf:
--------------------------------------------------------------------------------
1 | %PDF-1.7
2 |
3 | 1 0 obj
4 | << /BBox [ 0.000000 0.000000 30.000000 30.000000 ]
5 | /Resources << >>
6 | /Subtype /Form
7 | /Length 2 0 R
8 | /Group << /Type /Group
9 | /S /Transparency
10 | >>
11 | /Type /XObject
12 | >>
13 | stream
14 | /DeviceRGB CS
15 | /DeviceRGB cs
16 | q
17 | 1.000000 0.000000 -0.000000 1.000000 0.000000 27.000000 cm
18 | 0.533333 0.533333 0.533333 scn
19 | 9.000000 -7.000000 m
20 | 9.000000 -6.447715 9.447716 -6.000000 10.000000 -6.000000 c
21 | 20.000000 -6.000000 l
22 | 20.552284 -6.000000 21.000000 -6.447715 21.000000 -7.000000 c
23 | 21.000000 -7.552284 20.552284 -8.000000 20.000000 -8.000000 c
24 | 10.000000 -8.000000 l
25 | 9.447715 -8.000000 9.000000 -7.552284 9.000000 -7.000000 c
26 | h
27 | f
28 | n
29 | Q
30 | q
31 | 1.000000 0.000000 -0.000000 1.000000 0.000000 27.000000 cm
32 | 0.533333 0.533333 0.533333 scn
33 | 10.000000 -12.000000 m
34 | 9.447716 -12.000000 9.000000 -12.447715 9.000000 -13.000000 c
35 | 9.000000 -13.552284 9.447715 -14.000000 10.000000 -14.000000 c
36 | 20.000000 -14.000000 l
37 | 20.552284 -14.000000 21.000000 -13.552284 21.000000 -13.000000 c
38 | 21.000000 -12.447715 20.552284 -12.000000 20.000000 -12.000000 c
39 | 10.000000 -12.000000 l
40 | h
41 | f
42 | n
43 | Q
44 | q
45 | 1.000000 0.000000 -0.000000 1.000000 0.000000 -0.198975 cm
46 | 0.533333 0.533333 0.533333 scn
47 | 30.000000 24.198975 m
48 | 30.000000 26.960398 27.761423 29.198975 25.000000 29.198975 c
49 | 5.000000 29.198975 l
50 | 2.238577 29.198975 0.000000 26.960398 0.000000 24.198975 c
51 | 0.000000 10.198975 l
52 | 0.000000 7.437551 2.238575 5.198973 5.000000 5.198973 c
53 | 7.000000 5.198973 l
54 | 7.000000 2.541660 l
55 | 7.000000 0.954786 8.759470 -0.000057 10.089977 0.864773 c
56 | 16.757977 5.198973 l
57 | 25.000000 5.198973 l
58 | 27.761423 5.198973 30.000000 7.437550 30.000000 10.198973 c
59 | 30.000000 24.198975 l
60 | h
61 | 25.000000 27.198975 m
62 | 26.656855 27.198975 28.000000 25.855829 28.000000 24.198975 c
63 | 28.000000 10.198973 l
64 | 28.000000 8.542118 26.656855 7.198973 25.000000 7.198973 c
65 | 16.461536 7.198973 l
66 | 16.268070 7.198973 16.078758 7.142853 15.916547 7.037416 c
67 | 9.000000 2.541660 l
68 | 9.000000 6.198973 l
69 | 9.000000 6.751257 8.552284 7.198973 8.000000 7.198973 c
70 | 5.000000 7.198973 l
71 | 3.343147 7.198973 2.000000 8.542120 2.000000 10.198975 c
72 | 2.000000 24.198975 l
73 | 2.000000 25.855829 3.343145 27.198975 5.000000 27.198975 c
74 | 25.000000 27.198975 l
75 | h
76 | f*
77 | n
78 | Q
79 |
80 | endstream
81 | endobj
82 |
83 | 2 0 obj
84 | 1946
85 | endobj
86 |
87 | 3 0 obj
88 | << /BBox [ 0.000000 0.000000 30.000000 30.000000 ]
89 | /Resources << >>
90 | /Subtype /Form
91 | /Length 4 0 R
92 | /Group << /Type /Group
93 | /S /Transparency
94 | >>
95 | /Type /XObject
96 | >>
97 | stream
98 | /DeviceRGB CS
99 | /DeviceRGB cs
100 | q
101 | 1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm
102 | 0.000000 0.000000 0.000000 scn
103 | 0.000000 30.000000 m
104 | 30.000000 30.000000 l
105 | 30.000000 0.000000 l
106 | 0.000000 0.000000 l
107 | 0.000000 30.000000 l
108 | h
109 | f
110 | n
111 | Q
112 |
113 | endstream
114 | endobj
115 |
116 | 4 0 obj
117 | 232
118 | endobj
119 |
120 | 5 0 obj
121 | << /XObject << /X1 1 0 R >>
122 | /ExtGState << /E1 << /SMask << /Type /Mask
123 | /G 3 0 R
124 | /S /Alpha
125 | >>
126 | /Type /ExtGState
127 | >> >>
128 | >>
129 | endobj
130 |
131 | 6 0 obj
132 | << /Length 7 0 R >>
133 | stream
134 | /DeviceRGB CS
135 | /DeviceRGB cs
136 | q
137 | /E1 gs
138 | /X1 Do
139 | Q
140 |
141 | endstream
142 | endobj
143 |
144 | 7 0 obj
145 | 46
146 | endobj
147 |
148 | 8 0 obj
149 | << /Annots []
150 | /Type /Page
151 | /MediaBox [ 0.000000 0.000000 30.000000 30.000000 ]
152 | /Resources 5 0 R
153 | /Contents 6 0 R
154 | /Parent 9 0 R
155 | >>
156 | endobj
157 |
158 | 9 0 obj
159 | << /Kids [ 8 0 R ]
160 | /Count 1
161 | /Type /Pages
162 | >>
163 | endobj
164 |
165 | 10 0 obj
166 | << /Type /Catalog
167 | /Pages 9 0 R
168 | >>
169 | endobj
170 |
171 | xref
172 | 0 11
173 | 0000000000 65535 f
174 | 0000000010 00000 n
175 | 0000002204 00000 n
176 | 0000002227 00000 n
177 | 0000002707 00000 n
178 | 0000002729 00000 n
179 | 0000003027 00000 n
180 | 0000003129 00000 n
181 | 0000003150 00000 n
182 | 0000003323 00000 n
183 | 0000003397 00000 n
184 | trailer
185 | << /ID [ (some) (id) ]
186 | /Root 10 0 R
187 | /Size 11
188 | >>
189 | startxref
190 | 3457
191 | %%EOF
--------------------------------------------------------------------------------
/Example/Example/Resources/Assets.xcassets/Icon_Favorite_selected.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Icon_Favorite_selected.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | },
12 | "properties" : {
13 | "preserves-vector-representation" : true
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Example/Example/Resources/Assets.xcassets/Icon_Favorite_selected.imageset/Icon_Favorite_selected.pdf:
--------------------------------------------------------------------------------
1 | %PDF-1.7
2 |
3 | 1 0 obj
4 | << >>
5 | endobj
6 |
7 | 2 0 obj
8 | << /Length 3 0 R >>
9 | stream
10 | /DeviceRGB CS
11 | /DeviceRGB cs
12 | q
13 | 1.000000 0.000000 -0.000000 1.000000 1.229797 1.398682 cm
14 | 0.196078 0.196078 0.196078 scn
15 | 18.626476 19.285370 m
16 | 15.106291 26.183186 l
17 | 14.549581 27.274061 12.990855 27.274063 12.434144 26.183186 c
18 | 8.913960 19.285370 l
19 | 1.265949 18.069017 l
20 | 0.056432 17.876654 -0.425244 16.394220 0.440210 15.527656 c
21 | 5.912626 10.048218 l
22 | 4.706082 2.398655 l
23 | 4.515268 1.188887 5.776306 0.272697 6.867893 0.828007 c
24 | 13.770217 4.339342 l
25 | 20.672541 0.828007 l
26 | 21.764126 0.272697 23.025166 1.188887 22.834352 2.398655 c
27 | 21.627808 10.048220 l
28 | 27.100224 15.527655 l
29 | 27.965673 16.394215 27.484007 17.876652 26.274487 18.069017 c
30 | 18.626476 19.285370 l
31 | h
32 | f*
33 | n
34 | Q
35 |
36 | endstream
37 | endobj
38 |
39 | 3 0 obj
40 | 662
41 | endobj
42 |
43 | 4 0 obj
44 | << /Annots []
45 | /Type /Page
46 | /MediaBox [ 0.000000 0.000000 30.000000 30.000000 ]
47 | /Resources 1 0 R
48 | /Contents 2 0 R
49 | /Parent 5 0 R
50 | >>
51 | endobj
52 |
53 | 5 0 obj
54 | << /Kids [ 4 0 R ]
55 | /Count 1
56 | /Type /Pages
57 | >>
58 | endobj
59 |
60 | 6 0 obj
61 | << /Type /Catalog
62 | /Pages 5 0 R
63 | >>
64 | endobj
65 |
66 | xref
67 | 0 7
68 | 0000000000 65535 f
69 | 0000000010 00000 n
70 | 0000000034 00000 n
71 | 0000000752 00000 n
72 | 0000000774 00000 n
73 | 0000000947 00000 n
74 | 0000001021 00000 n
75 | trailer
76 | << /ID [ (some) (id) ]
77 | /Root 6 0 R
78 | /Size 7
79 | >>
80 | startxref
81 | 1080
82 | %%EOF
--------------------------------------------------------------------------------
/Example/Example/Resources/Assets.xcassets/Icon_Favorite_unselected.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Icon_Favorite_unselected.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | },
12 | "properties" : {
13 | "preserves-vector-representation" : true
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Example/Example/Resources/Assets.xcassets/Icon_Favorite_unselected.imageset/Icon_Favorite_unselected.pdf:
--------------------------------------------------------------------------------
1 | %PDF-1.7
2 |
3 | 1 0 obj
4 | << /BBox [ 0.000000 0.000000 30.000000 30.000000 ]
5 | /Resources << >>
6 | /Subtype /Form
7 | /Length 2 0 R
8 | /Group << /Type /Group
9 | /S /Transparency
10 | >>
11 | /Type /XObject
12 | >>
13 | stream
14 | /DeviceRGB CS
15 | /DeviceRGB cs
16 | q
17 | 1.000000 0.000000 -0.000000 1.000000 0.774353 0.123535 cm
18 | 0.533333 0.533333 0.533333 scn
19 | 19.741375 21.468185 m
20 | 16.897787 27.040199 l
21 | 15.784368 29.221949 12.666919 29.221954 11.553496 27.040199 c
22 | 8.709911 21.468185 l
23 | 2.531898 20.485624 l
24 | 0.112862 20.100897 -0.850486 17.136028 0.880420 15.402901 c
25 | 5.301004 10.976645 l
26 | 4.326365 4.797375 l
27 | 3.944739 2.377848 6.466807 0.545458 8.649986 1.656080 c
28 | 14.225642 4.492516 l
29 | 19.801298 1.656080 l
30 | 21.984478 0.545456 24.506544 2.377850 24.124918 4.797375 c
31 | 23.150280 10.976643 l
32 | 27.570864 15.402901 l
33 | 29.301765 17.136023 28.338430 20.100895 25.919388 20.485624 c
34 | 19.741375 21.468185 l
35 | h
36 | 15.116357 26.131075 m
37 | 14.745216 26.858326 13.706067 26.858326 13.334927 26.131075 c
38 | 10.260098 20.105938 l
39 | 10.114797 19.821218 9.842136 19.623119 9.526451 19.572912 c
40 | 2.846033 18.510448 l
41 | 2.039687 18.382206 1.718572 17.393915 2.295540 16.816208 c
42 | 7.075611 12.030003 l
43 | 7.301495 11.803829 7.405642 11.483295 7.355840 11.167547 c
44 | 6.301941 4.485773 l
45 | 6.174733 3.679264 7.015422 3.068468 7.743148 3.438675 c
46 | 13.772223 6.505774 l
47 | 14.057128 6.650711 14.394156 6.650711 14.679061 6.505774 c
48 | 20.708136 3.438675 l
49 | 21.435862 3.068468 22.276550 3.679264 22.149342 4.485773 c
50 | 21.095444 11.167547 l
51 | 21.045641 11.483295 21.149790 11.803829 21.375673 12.030003 c
52 | 26.155745 16.816208 l
53 | 26.732714 17.393915 26.411598 18.382206 25.605253 18.510448 c
54 | 18.924833 19.572912 l
55 | 18.609148 19.623119 18.336489 19.821218 18.191187 20.105938 c
56 | 15.116357 26.131075 l
57 | h
58 | f*
59 | n
60 | Q
61 |
62 | endstream
63 | endobj
64 |
65 | 2 0 obj
66 | 1500
67 | endobj
68 |
69 | 3 0 obj
70 | << /BBox [ 0.000000 0.000000 30.000000 30.000000 ]
71 | /Resources << >>
72 | /Subtype /Form
73 | /Length 4 0 R
74 | /Group << /Type /Group
75 | /S /Transparency
76 | >>
77 | /Type /XObject
78 | >>
79 | stream
80 | /DeviceRGB CS
81 | /DeviceRGB cs
82 | q
83 | 1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm
84 | 0.000000 0.000000 0.000000 scn
85 | 0.000000 30.000000 m
86 | 30.000000 30.000000 l
87 | 30.000000 0.000000 l
88 | 0.000000 0.000000 l
89 | 0.000000 30.000000 l
90 | h
91 | f
92 | n
93 | Q
94 |
95 | endstream
96 | endobj
97 |
98 | 4 0 obj
99 | 232
100 | endobj
101 |
102 | 5 0 obj
103 | << /XObject << /X1 1 0 R >>
104 | /ExtGState << /E1 << /SMask << /Type /Mask
105 | /G 3 0 R
106 | /S /Alpha
107 | >>
108 | /Type /ExtGState
109 | >> >>
110 | >>
111 | endobj
112 |
113 | 6 0 obj
114 | << /Length 7 0 R >>
115 | stream
116 | /DeviceRGB CS
117 | /DeviceRGB cs
118 | q
119 | /E1 gs
120 | /X1 Do
121 | Q
122 |
123 | endstream
124 | endobj
125 |
126 | 7 0 obj
127 | 46
128 | endobj
129 |
130 | 8 0 obj
131 | << /Annots []
132 | /Type /Page
133 | /MediaBox [ 0.000000 0.000000 30.000000 30.000000 ]
134 | /Resources 5 0 R
135 | /Contents 6 0 R
136 | /Parent 9 0 R
137 | >>
138 | endobj
139 |
140 | 9 0 obj
141 | << /Kids [ 8 0 R ]
142 | /Count 1
143 | /Type /Pages
144 | >>
145 | endobj
146 |
147 | 10 0 obj
148 | << /Type /Catalog
149 | /Pages 9 0 R
150 | >>
151 | endobj
152 |
153 | xref
154 | 0 11
155 | 0000000000 65535 f
156 | 0000000010 00000 n
157 | 0000001758 00000 n
158 | 0000001781 00000 n
159 | 0000002261 00000 n
160 | 0000002283 00000 n
161 | 0000002581 00000 n
162 | 0000002683 00000 n
163 | 0000002704 00000 n
164 | 0000002877 00000 n
165 | 0000002951 00000 n
166 | trailer
167 | << /ID [ (some) (id) ]
168 | /Root 10 0 R
169 | /Size 11
170 | >>
171 | startxref
172 | 3011
173 | %%EOF
--------------------------------------------------------------------------------
/Example/Example/Resources/Assets.xcassets/Icon_Profile_selected.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Icon_Profile_selected.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | },
12 | "properties" : {
13 | "preserves-vector-representation" : true
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Example/Example/Resources/Assets.xcassets/Icon_Profile_selected.imageset/Icon_Profile_selected.pdf:
--------------------------------------------------------------------------------
1 | %PDF-1.7
2 |
3 | 1 0 obj
4 | << >>
5 | endobj
6 |
7 | 2 0 obj
8 | << /Length 3 0 R >>
9 | stream
10 | /DeviceRGB CS
11 | /DeviceRGB cs
12 | q
13 | 1.000000 0.000000 -0.000000 1.000000 3.000000 17.000000 cm
14 | 0.196078 0.196078 0.196078 scn
15 | 18.000000 6.000000 m
16 | 18.000000 2.686292 15.313708 0.000000 12.000000 0.000000 c
17 | 8.686292 0.000000 6.000000 2.686292 6.000000 6.000000 c
18 | 6.000000 9.313708 8.686292 12.000000 12.000000 12.000000 c
19 | 15.313708 12.000000 18.000000 9.313708 18.000000 6.000000 c
20 | h
21 | f
22 | n
23 | Q
24 | q
25 | 1.000000 0.000000 -0.000000 1.000000 3.000000 17.000000 cm
26 | 0.196078 0.196078 0.196078 scn
27 | 0.000000 -10.000000 m
28 | 0.000000 -6.134007 3.134007 -3.000000 7.000000 -3.000000 c
29 | 17.000000 -3.000000 l
30 | 20.865993 -3.000000 24.000000 -6.134007 24.000000 -10.000000 c
31 | 24.000000 -12.000000 l
32 | 24.000000 -13.656855 22.656855 -15.000000 21.000000 -15.000000 c
33 | 3.000000 -15.000000 l
34 | 1.343146 -15.000000 0.000000 -13.656855 0.000000 -12.000000 c
35 | 0.000000 -10.000000 l
36 | h
37 | f
38 | n
39 | Q
40 |
41 | endstream
42 | endobj
43 |
44 | 3 0 obj
45 | 843
46 | endobj
47 |
48 | 4 0 obj
49 | << /Annots []
50 | /Type /Page
51 | /MediaBox [ 0.000000 0.000000 30.000000 30.000000 ]
52 | /Resources 1 0 R
53 | /Contents 2 0 R
54 | /Parent 5 0 R
55 | >>
56 | endobj
57 |
58 | 5 0 obj
59 | << /Kids [ 4 0 R ]
60 | /Count 1
61 | /Type /Pages
62 | >>
63 | endobj
64 |
65 | 6 0 obj
66 | << /Type /Catalog
67 | /Pages 5 0 R
68 | >>
69 | endobj
70 |
71 | xref
72 | 0 7
73 | 0000000000 65535 f
74 | 0000000010 00000 n
75 | 0000000034 00000 n
76 | 0000000933 00000 n
77 | 0000000955 00000 n
78 | 0000001128 00000 n
79 | 0000001202 00000 n
80 | trailer
81 | << /ID [ (some) (id) ]
82 | /Root 6 0 R
83 | /Size 7
84 | >>
85 | startxref
86 | 1261
87 | %%EOF
--------------------------------------------------------------------------------
/Example/Example/Resources/Assets.xcassets/Icon_Profile_unselected.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Icon_Profile_unselected.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | },
12 | "properties" : {
13 | "preserves-vector-representation" : true
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Example/Example/Resources/Assets.xcassets/Icon_Profile_unselected.imageset/Icon_Profile_unselected.pdf:
--------------------------------------------------------------------------------
1 | %PDF-1.7
2 |
3 | 1 0 obj
4 | << >>
5 | endobj
6 |
7 | 2 0 obj
8 | << /Length 3 0 R >>
9 | stream
10 | /DeviceRGB CS
11 | /DeviceRGB cs
12 | q
13 | 1.000000 0.000000 -0.000000 1.000000 2.000000 16.000000 cm
14 | 0.533333 0.533333 0.533333 scn
15 | 20.000000 7.000000 m
16 | 20.000000 3.134007 16.865993 0.000000 13.000000 0.000000 c
17 | 9.134007 0.000000 6.000000 3.134007 6.000000 7.000000 c
18 | 6.000000 10.865993 9.134007 14.000000 13.000000 14.000000 c
19 | 16.865993 14.000000 20.000000 10.865993 20.000000 7.000000 c
20 | h
21 | 18.000000 7.000000 m
22 | 18.000000 4.238576 15.761424 2.000000 13.000000 2.000000 c
23 | 10.238577 2.000000 8.000000 4.238576 8.000000 7.000000 c
24 | 8.000000 9.761423 10.238577 12.000000 13.000000 12.000000 c
25 | 15.761424 12.000000 18.000000 9.761423 18.000000 7.000000 c
26 | h
27 | f*
28 | n
29 | Q
30 | q
31 | 1.000000 0.000000 -0.000000 1.000000 2.000000 16.000000 cm
32 | 0.533333 0.533333 0.533333 scn
33 | 0.000000 -10.000000 m
34 | 0.000000 -5.581722 3.581722 -2.000000 8.000000 -2.000000 c
35 | 18.000000 -2.000000 l
36 | 22.418278 -2.000000 26.000000 -5.581722 26.000000 -10.000000 c
37 | 26.000000 -12.000000 l
38 | 26.000000 -14.209139 24.209139 -16.000000 22.000000 -16.000000 c
39 | 4.000000 -16.000000 l
40 | 1.790861 -16.000000 0.000000 -14.209139 0.000000 -12.000000 c
41 | 0.000000 -10.000000 l
42 | h
43 | 2.000000 -10.000000 m
44 | 2.000000 -6.686291 4.686292 -4.000000 8.000000 -4.000000 c
45 | 18.000000 -4.000000 l
46 | 21.313709 -4.000000 24.000000 -6.686291 24.000000 -10.000000 c
47 | 24.000000 -12.000000 l
48 | 24.000000 -13.104568 23.104570 -14.000000 22.000000 -14.000000 c
49 | 4.000000 -14.000000 l
50 | 2.895431 -14.000000 2.000000 -13.104568 2.000000 -12.000000 c
51 | 2.000000 -10.000000 l
52 | h
53 | f*
54 | n
55 | Q
56 |
57 | endstream
58 | endobj
59 |
60 | 3 0 obj
61 | 1468
62 | endobj
63 |
64 | 4 0 obj
65 | << /Annots []
66 | /Type /Page
67 | /MediaBox [ 0.000000 0.000000 30.000000 30.000000 ]
68 | /Resources 1 0 R
69 | /Contents 2 0 R
70 | /Parent 5 0 R
71 | >>
72 | endobj
73 |
74 | 5 0 obj
75 | << /Kids [ 4 0 R ]
76 | /Count 1
77 | /Type /Pages
78 | >>
79 | endobj
80 |
81 | 6 0 obj
82 | << /Type /Catalog
83 | /Pages 5 0 R
84 | >>
85 | endobj
86 |
87 | xref
88 | 0 7
89 | 0000000000 65535 f
90 | 0000000010 00000 n
91 | 0000000034 00000 n
92 | 0000001558 00000 n
93 | 0000001581 00000 n
94 | 0000001754 00000 n
95 | 0000001828 00000 n
96 | trailer
97 | << /ID [ (some) (id) ]
98 | /Root 6 0 R
99 | /Size 7
100 | >>
101 | startxref
102 | 1887
103 | %%EOF
--------------------------------------------------------------------------------
/Example/Example/Resources/Assets.xcassets/Icon_swipe.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Icon_Swipe_light.pdf",
5 | "idiom" : "universal"
6 | },
7 | {
8 | "appearances" : [
9 | {
10 | "appearance" : "luminosity",
11 | "value" : "dark"
12 | }
13 | ],
14 | "filename" : "Icon_Swipe_dark.pdf",
15 | "idiom" : "universal"
16 | }
17 | ],
18 | "info" : {
19 | "author" : "xcode",
20 | "version" : 1
21 | },
22 | "properties" : {
23 | "preserves-vector-representation" : true
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Example/Example/Resources/Assets.xcassets/Icon_swipe.imageset/Icon_Swipe_dark.pdf:
--------------------------------------------------------------------------------
1 | %PDF-1.7
2 |
3 | 1 0 obj
4 | << >>
5 | endobj
6 |
7 | 2 0 obj
8 | << /Length 3 0 R >>
9 | stream
10 | /DeviceRGB CS
11 | /DeviceRGB cs
12 | q
13 | 1.000000 0.000000 -0.000000 1.000000 107.597168 19.904053 cm
14 | 0.629167 0.629167 0.629167 scn
15 | 30.101513 3.293037 m
16 | 30.101513 2.464615 30.773085 1.793037 31.601513 1.793037 c
17 | 32.429939 1.793037 33.101513 2.464615 33.101513 3.293037 c
18 | 30.101513 3.293037 l
19 | h
20 | 25.067957 42.223915 m
21 | 23.674679 41.668217 l
22 | 23.676331 41.664108 l
23 | 25.067957 42.223915 l
24 | h
25 | 14.178696 65.718819 m
26 | 15.513352 66.403534 l
27 | 15.507772 66.414200 l
28 | 14.178696 65.718819 l
29 | h
30 | 4.098353 68.942398 m
31 | 3.422246 70.281395 l
32 | 3.416876 70.278656 l
33 | 4.098353 68.942398 l
34 | h
35 | 0.800461 58.899719 m
36 | 2.141096 59.572552 l
37 | 2.139977 59.574772 l
38 | 0.800461 58.899719 l
39 | h
40 | 2.633274 51.903721 m
41 | 3.004868 51.163307 3.906327 50.864326 4.646739 51.235920 c
42 | 5.387150 51.607513 5.686135 52.508972 5.314540 53.249382 c
43 | 2.633274 51.903721 l
44 | h
45 | 33.101513 3.293037 m
46 | 33.101513 9.816757 33.109821 26.251789 26.459581 42.783718 c
47 | 23.676331 41.664108 l
48 | 30.093204 25.712311 30.101513 9.787609 30.101513 3.293037 c
49 | 33.101513 3.293037 l
50 | h
51 | 26.461227 42.779610 m
52 | 22.700573 52.208557 18.380539 60.814678 15.513309 66.403519 c
53 | 12.844082 65.034126 l
54 | 15.701491 59.464432 19.968418 50.960808 23.674685 41.668221 c
55 | 26.461227 42.779610 l
56 | h
57 | 15.507772 66.414200 m
58 | 14.408175 68.515862 12.522062 70.099152 10.260141 70.822487 c
59 | 9.346363 67.965034 l
60 | 10.858726 67.481400 12.116976 66.423737 12.849618 65.023438 c
61 | 15.507772 66.414200 l
62 | h
63 | 10.260141 70.822487 m
64 | 7.998360 71.545776 5.541566 71.351501 3.422251 70.281387 c
65 | 4.774456 67.603409 l
66 | 6.190899 68.318626 7.833860 68.448715 9.346363 67.965034 c
67 | 10.260141 70.822487 l
68 | h
69 | 3.416876 70.278656 m
70 | 1.296715 69.197395 -0.310238 67.324211 -1.051887 65.065750 c
71 | 1.798364 64.129768 l
72 | 2.292186 65.633545 3.363156 66.883652 4.779831 67.606140 c
73 | 3.416876 70.278656 l
74 | h
75 | -1.051887 65.065750 m
76 | -1.793585 62.807144 -1.609107 60.347977 -0.539054 58.224663 c
77 | 2.139977 59.574772 l
78 | 1.427231 60.989079 1.304591 62.626137 1.798364 64.129768 c
79 | -1.051887 65.065750 l
80 | h
81 | -0.540172 58.226887 m
82 | 2.633274 51.903721 l
83 | 5.314540 53.249382 l
84 | 2.141094 59.572548 l
85 | -0.540172 58.226887 l
86 | h
87 | f
88 | n
89 | Q
90 | q
91 | 1.000000 0.000000 -0.000000 1.000000 88.944702 66.358154 cm
92 | 0.629167 0.629167 0.629167 scn
93 | 4.181628 3.823135 m
94 | 4.561399 3.086884 5.466113 2.797899 6.202364 3.177670 c
95 | 6.938615 3.557440 7.227600 4.462154 6.847828 5.198406 c
96 | 4.181628 3.823135 l
97 | h
98 | 0.910119 13.437599 m
99 | 2.243223 14.125237 l
100 | 2.241588 14.128388 l
101 | 0.910119 13.437599 l
102 | h
103 | 14.848384 21.248571 m
104 | 13.539284 20.516172 l
105 | 13.545394 20.505459 l
106 | 14.848384 21.248571 l
107 | h
108 | 16.403994 18.520935 m
109 | 17.743010 19.196978 l
110 | 17.725847 19.230968 l
111 | 17.706984 19.264048 l
112 | 16.403994 18.520935 l
113 | h
114 | 20.291819 7.492249 m
115 | 20.665186 6.752731 21.567360 6.455908 22.306877 6.829275 c
116 | 23.046396 7.202642 23.343220 8.104816 22.969852 8.844336 c
117 | 20.291819 7.492249 l
118 | h
119 | 6.847828 5.198406 m
120 | 2.243219 14.125236 l
121 | -0.422982 12.749964 l
122 | 4.181628 3.823135 l
123 | 6.847828 5.198406 l
124 | h
125 | 2.241588 14.128388 m
126 | 1.484908 15.586862 1.299585 17.272894 1.721112 18.859379 c
127 | -1.178290 19.629747 l
128 | -1.791699 17.321083 -1.521738 14.867766 -0.421351 12.746811 c
129 | 2.241588 14.128388 l
130 | h
131 | 1.721112 18.859379 m
132 | 2.142666 20.445965 3.141511 21.821243 4.525321 22.717007 c
133 | 2.895110 25.235422 l
134 | 0.887509 23.935867 -0.564908 21.938311 -1.178290 19.629747 c
135 | 1.721112 18.859379 l
136 | h
137 | 4.499123 22.700430 m
138 | 7.435656 24.516294 11.871108 23.497993 13.539328 20.516197 c
139 | 16.157440 21.980946 l
140 | 13.594401 26.562155 7.202802 27.899548 2.921308 25.251999 c
141 | 4.499123 22.700430 l
142 | h
143 | 13.545394 20.505459 m
144 | 15.101003 17.777821 l
145 | 17.706984 19.264048 l
146 | 16.151373 21.991684 l
147 | 13.545394 20.505459 l
148 | h
149 | 15.064976 17.844891 m
150 | 20.291819 7.492249 l
151 | 22.969852 8.844336 l
152 | 17.743010 19.196978 l
153 | 15.064976 17.844891 l
154 | h
155 | f
156 | n
157 | Q
158 | q
159 | 1.000000 0.000000 -0.000000 1.000000 70.995850 57.220703 cm
160 | 0.629167 0.629167 0.629167 scn
161 | 8.562653 2.799356 m
162 | 8.939391 2.061548 9.842909 1.768845 10.580716 2.145582 c
163 | 11.318523 2.522322 11.611228 3.425838 11.234490 4.163647 c
164 | 8.562653 2.799356 l
165 | h
166 | 0.813819 21.273163 m
167 | 2.149764 21.955322 l
168 | 2.145404 21.963730 l
169 | 0.813819 21.273163 l
170 | h
171 | 4.422827 31.439829 m
172 | 3.759426 32.785152 l
173 | 3.752470 32.781723 l
174 | 3.745549 32.778221 l
175 | 4.422827 31.439829 l
176 | h
177 | 15.249868 28.960155 m
178 | 16.551033 29.706461 l
179 | 16.498585 29.797901 l
180 | 16.433865 29.881107 l
181 | 15.249868 28.960155 l
182 | h
183 | 16.245453 27.224380 m
184 | 17.561163 27.944733 l
185 | 17.554018 27.957781 l
186 | 17.546616 27.970686 l
187 | 16.245453 27.224380 l
188 | h
189 | 17.543161 21.730659 m
190 | 17.941002 21.004011 18.852577 20.737461 19.579224 21.135300 c
191 | 20.305870 21.533138 20.572422 22.444714 20.174583 23.171360 c
192 | 17.543161 21.730659 l
193 | h
194 | 11.234490 4.163647 m
195 | 2.149737 21.955307 l
196 | -0.522100 20.591019 l
197 | 8.562653 2.799356 l
198 | 11.234490 4.163647 l
199 | h
200 | 2.145404 21.963730 m
201 | 0.640692 24.865192 1.844065 28.453754 5.100106 30.101437 c
202 | 3.745549 32.778221 l
203 | -0.838677 30.458426 -2.870964 25.120155 -0.517767 20.582596 c
204 | 2.145404 21.963730 l
205 | h
206 | 5.086229 30.094505 m
207 | 6.591342 30.836700 8.308313 31.037558 9.945602 30.662575 c
208 | 10.615340 33.586861 l
209 | 8.305876 34.115788 5.883752 33.832695 3.759426 32.785152 c
210 | 5.086229 30.094505 l
211 | h
212 | 9.945602 30.662575 m
213 | 11.582829 30.287607 13.038308 29.360258 14.065873 28.039202 c
214 | 16.433865 29.881107 l
215 | 14.980840 31.749144 12.924868 33.057919 10.615340 33.586861 c
216 | 9.945602 30.662575 l
217 | h
218 | 13.948705 28.213848 m
219 | 14.944288 26.478075 l
220 | 17.546616 27.970686 l
221 | 16.551033 29.706461 l
222 | 13.948705 28.213848 l
223 | h
224 | 14.929742 26.504028 m
225 | 17.543161 21.730659 l
226 | 20.174583 23.171360 l
227 | 17.561163 27.944733 l
228 | 14.929742 26.504028 l
229 | h
230 | f
231 | n
232 | Q
233 | q
234 | 1.000000 0.000000 -0.000000 1.000000 41.874756 -1.757080 cm
235 | 0.629167 0.629167 0.629167 scn
236 | 52.255276 2.280106 m
237 | 52.794823 1.651466 53.741817 1.579247 54.370453 2.118790 c
238 | 54.999092 2.658340 55.071312 3.605331 54.531769 4.233971 c
239 | 52.255276 2.280106 l
240 | h
241 | 19.605707 23.094429 m
242 | 18.641115 21.945709 l
243 | 18.641294 21.945557 l
244 | 19.605707 23.094429 l
245 | h
246 | 1.685100 41.444016 m
247 | 0.574166 40.436134 l
248 | 0.574391 40.435883 l
249 | 1.685100 41.444016 l
250 | h
251 | 14.938883 49.750931 m
252 | 14.147062 48.476936 l
253 | 14.151858 48.473984 l
254 | 14.938883 49.750931 l
255 | h
256 | 29.623823 40.700111 m
257 | 28.836800 39.423164 l
258 | 29.388638 39.083054 30.096523 39.135902 30.591740 39.554192 c
259 | 29.623823 40.700111 l
260 | h
261 | 32.486141 43.117798 m
262 | 33.454056 41.971878 l
263 | 33.992607 42.426765 34.141724 43.195297 33.812378 43.818581 c
264 | 32.486141 43.117798 l
265 | h
266 | 0.876184 102.939926 m
267 | -0.451024 102.240982 l
268 | -0.450053 102.239143 l
269 | 0.876184 102.939926 l
270 | h
271 | 14.378863 110.068993 m
272 | 15.710394 110.759659 l
273 | 15.707027 110.766151 15.703614 110.772614 15.700153 110.779060 c
274 | 14.378863 110.068993 l
275 | h
276 | 29.350103 77.948456 m
277 | 29.731550 77.213074 30.636921 76.926147 31.372305 77.307594 c
278 | 32.107689 77.689041 32.394611 78.594414 32.013168 79.329796 c
279 | 29.350103 77.948456 l
280 | h
281 | 54.531769 4.233971 m
282 | 53.941753 4.921410 53.042351 5.633354 52.014084 6.341515 c
283 | 50.962776 7.065536 49.682655 7.848564 48.258492 8.668015 c
284 | 45.411137 10.306351 41.902874 12.138573 38.335388 13.994934 c
285 | 34.754112 15.858467 31.108093 17.748909 27.936901 19.525139 c
286 | 24.741688 21.314827 22.131063 22.932976 20.570122 24.243301 c
287 | 18.641294 21.945557 l
288 | 20.440464 20.435249 23.274471 18.698097 26.470863 16.907753 c
289 | 29.691277 15.103951 33.383839 13.189644 36.950584 11.333672 c
290 | 40.531120 9.470520 43.980316 7.668465 46.762318 6.067734 c
291 | 48.152836 5.267639 49.355370 4.529930 50.312496 3.870766 c
292 | 51.292667 3.195732 51.927483 2.662025 52.255276 2.280106 c
293 | 54.531769 4.233971 l
294 | h
295 | 20.570299 24.243149 m
296 | 16.308598 27.821739 8.105620 36.602058 2.795808 42.452148 c
297 | 0.574391 40.435883 l
298 | 5.842715 34.631508 14.191412 25.682159 18.641115 21.945709 c
299 | 20.570299 24.243149 l
300 | h
301 | 2.796033 42.451897 m
302 | 2.368433 42.923218 2.055722 43.485779 1.881652 44.095924 c
303 | -1.003242 43.272888 l
304 | -0.702537 42.218864 -0.162680 41.248314 0.574166 40.436134 c
305 | 2.796033 42.451897 l
306 | h
307 | 1.881652 44.095924 m
308 | 1.707587 44.706055 1.676713 45.347778 1.791360 45.971588 c
309 | -1.159222 46.513863 l
310 | -1.357340 45.435875 -1.303951 44.326935 -1.003242 43.272888 c
311 | 1.881652 44.095924 l
312 | h
313 | 1.791360 45.971588 m
314 | 1.906011 46.595413 2.163192 47.185020 2.543410 47.694733 c
315 | 0.138738 49.488487 l
316 | -0.516876 48.609581 -0.961107 47.591835 -1.159222 46.513863 c
317 | 1.791360 45.971588 l
318 | h
319 | 2.543410 47.694733 m
320 | 2.923647 48.204468 3.416828 48.620750 3.985142 48.910973 c
321 | 2.620721 51.582745 l
322 | 1.643657 51.083778 0.794334 50.367363 0.138738 49.488487 c
323 | 2.543410 47.694733 l
324 | h
325 | 3.983269 48.910019 m
326 | 5.570595 49.717834 7.341166 50.102669 9.122582 50.026741 c
327 | 9.250330 53.024025 l
328 | 6.953609 53.121914 4.670328 52.625820 2.622593 51.583698 c
329 | 3.983269 48.910019 l
330 | h
331 | 9.122582 50.026741 m
332 | 10.903997 49.950821 12.634983 49.416748 14.147068 48.476944 c
333 | 15.730698 51.024910 l
334 | 13.779592 52.237576 11.547053 52.926136 9.250330 53.024025 c
335 | 9.122582 50.026741 l
336 | h
337 | 14.151858 48.473984 m
338 | 28.836800 39.423164 l
339 | 30.410849 41.977058 l
340 | 15.725907 51.027874 l
341 | 14.151858 48.473984 l
342 | h
343 | 30.591740 39.554192 m
344 | 33.454056 41.971878 l
345 | 31.518227 44.263725 l
346 | 28.655907 41.846039 l
347 | 30.591740 39.554192 l
348 | h
349 | 33.812378 43.818581 m
350 | 2.202421 103.640709 l
351 | -0.450053 102.239143 l
352 | 31.159904 42.417023 l
353 | 33.812378 43.818581 l
354 | h
355 | 2.203390 103.638870 m
356 | 1.463501 105.043831 1.300187 106.679459 1.747344 108.201538 c
357 | -1.131015 109.047150 l
358 | -1.798195 106.776131 -1.554216 104.335808 -0.451022 102.240982 c
359 | 2.203390 103.638870 l
360 | h
361 | 1.747344 108.201538 m
362 | 2.194540 109.723755 3.217949 111.015175 4.603770 111.802299 c
363 | 3.122133 114.410889 l
364 | 1.061644 113.240562 -0.463873 111.318031 -1.131015 109.047150 c
365 | 1.747344 108.201538 l
366 | h
367 | 4.599438 111.799843 m
368 | 5.313483 112.202286 6.101340 112.458595 6.916702 112.553513 c
369 | 6.569831 115.533386 l
370 | 5.358835 115.392426 4.188111 115.011688 3.126465 114.413338 c
371 | 4.599438 111.799843 l
372 | h
373 | 6.916702 112.553513 m
374 | 7.732068 112.648422 8.558220 112.579979 9.346520 112.352272 c
375 | 10.179056 115.234444 l
376 | 9.007913 115.572739 7.780822 115.674355 6.569831 115.533386 c
377 | 6.916702 112.553513 l
378 | h
379 | 9.346520 112.352272 m
380 | 10.134811 112.124573 10.869125 111.742264 11.506388 111.228104 c
381 | 13.390175 113.562920 l
382 | 12.442001 114.327927 11.350207 114.896149 10.179056 115.234444 c
383 | 9.346520 112.352272 l
384 | h
385 | 11.506388 111.228104 m
386 | 12.143638 110.713959 12.670938 110.078377 13.057572 109.358925 c
387 | 15.700153 110.779060 l
388 | 15.123836 111.851471 14.338361 112.797897 13.390175 113.562920 c
389 | 11.506388 111.228104 l
390 | h
391 | 13.047332 109.378326 m
392 | 29.350103 77.948456 l
393 | 32.013168 79.329796 l
394 | 15.710394 110.759659 l
395 | 13.047332 109.378326 l
396 | h
397 | f
398 | n
399 | Q
400 | q
401 | 0.980061 -0.198696 -0.198696 -0.980061 18.139366 124.988129 cm
402 | 0.629167 0.629167 0.629167 scn
403 | 14.304977 14.235680 m
404 | 14.304977 10.721280 11.443786 7.862720 7.902489 7.862720 c
405 | 7.902489 4.862720 l
406 | 13.090040 4.862720 17.304977 9.053846 17.304977 14.235680 c
407 | 14.304977 14.235680 l
408 | h
409 | 7.902489 7.862720 m
410 | 4.361192 7.862720 1.500000 10.721280 1.500000 14.235680 c
411 | -1.500000 14.235680 l
412 | -1.500000 9.053846 2.714937 4.862720 7.902489 4.862720 c
413 | 7.902489 7.862720 l
414 | h
415 | 1.500000 14.235680 m
416 | 1.500000 17.750080 4.361194 20.608643 7.902489 20.608643 c
417 | 7.902489 23.608643 l
418 | 2.714936 23.608643 -1.500000 19.417511 -1.500000 14.235680 c
419 | 1.500000 14.235680 l
420 | h
421 | 7.902489 20.608643 m
422 | 11.443784 20.608643 14.304977 17.750080 14.304977 14.235680 c
423 | 17.304977 14.235680 l
424 | 17.304977 19.417511 13.090041 23.608643 7.902489 23.608643 c
425 | 7.902489 20.608643 l
426 | h
427 | f
428 | n
429 | Q
430 | q
431 | 0.980061 -0.198696 -0.198696 -0.980061 18.744465 108.637100 cm
432 | 0.629167 0.629167 0.629167 scn
433 | 17.366123 73.170517 m
434 | 17.836908 73.852165 17.665968 74.786407 16.984314 75.257195 c
435 | 16.302660 75.727982 15.368423 75.557037 14.897636 74.875381 c
436 | 17.366123 73.170517 l
437 | h
438 | -0.216979 6.223732 m
439 | -0.062933 5.409760 0.721807 4.874779 1.535785 5.028824 c
440 | 2.349764 5.182869 2.884745 5.967606 2.730698 6.781586 c
441 | -0.216979 6.223732 l
442 | h
443 | 14.897636 74.875381 m
444 | 5.401996 61.126610 -5.226364 32.693237 -0.216979 6.223732 c
445 | 2.730698 6.781586 l
446 | -2.091360 32.261257 8.194466 59.890839 17.366123 73.170517 c
447 | 14.897636 74.875381 l
448 | h
449 | f
450 | n
451 | Q
452 | q
453 | 0.980061 -0.198696 -0.198696 -0.980061 12.316921 50.234398 cm
454 | 0.629167 0.629167 0.629167 scn
455 | -0.590866 13.476933 m
456 | -1.352314 13.150606 -1.705049 12.268791 -1.378723 11.507343 c
457 | -1.052397 10.745895 -0.170581 10.393160 0.590866 10.719486 c
458 | -0.590866 13.476933 l
459 | h
460 | 11.138151 16.871582 m
461 | 12.604726 17.186476 l
462 | 12.510608 17.624821 12.225348 17.998085 11.827100 18.204004 c
463 | 11.428851 18.409925 10.959372 18.426908 10.547285 18.250305 c
464 | 11.138151 16.871582 l
465 | h
466 | 12.714355 5.946751 m
467 | 12.996131 5.167717 13.856087 4.764609 14.635121 5.046386 c
468 | 15.414155 5.328161 15.817262 6.188118 15.535486 6.967152 c
469 | 12.714355 5.946751 l
470 | h
471 | 0.590866 10.719486 m
472 | 11.729017 15.492859 l
473 | 10.547285 18.250305 l
474 | -0.590866 13.476933 l
475 | 0.590866 10.719486 l
476 | h
477 | 9.671576 16.556688 m
478 | 10.444746 12.955757 11.461326 9.411034 12.714355 5.946751 c
479 | 15.535486 6.967152 l
480 | 14.328569 10.303946 13.349420 13.718168 12.604726 17.186476 c
481 | 9.671576 16.556688 l
482 | h
483 | f
484 | n
485 | Q
486 |
487 | endstream
488 | endobj
489 |
490 | 3 0 obj
491 | 12527
492 | endobj
493 |
494 | 4 0 obj
495 | << /Annots []
496 | /Type /Page
497 | /MediaBox [ 0.000000 0.000000 140.698730 118.842041 ]
498 | /Resources 1 0 R
499 | /Contents 2 0 R
500 | /Parent 5 0 R
501 | >>
502 | endobj
503 |
504 | 5 0 obj
505 | << /Kids [ 4 0 R ]
506 | /Count 1
507 | /Type /Pages
508 | >>
509 | endobj
510 |
511 | 6 0 obj
512 | << /Type /Catalog
513 | /Pages 5 0 R
514 | >>
515 | endobj
516 |
517 | xref
518 | 0 7
519 | 0000000000 65535 f
520 | 0000000010 00000 n
521 | 0000000034 00000 n
522 | 0000012617 00000 n
523 | 0000012641 00000 n
524 | 0000012816 00000 n
525 | 0000012890 00000 n
526 | trailer
527 | << /ID [ (some) (id) ]
528 | /Root 6 0 R
529 | /Size 7
530 | >>
531 | startxref
532 | 12949
533 | %%EOF
--------------------------------------------------------------------------------
/Example/Example/Resources/Assets.xcassets/Icon_swipe.imageset/Icon_Swipe_light.pdf:
--------------------------------------------------------------------------------
1 | %PDF-1.7
2 |
3 | 1 0 obj
4 | << >>
5 | endobj
6 |
7 | 2 0 obj
8 | << /Length 3 0 R >>
9 | stream
10 | /DeviceRGB CS
11 | /DeviceRGB cs
12 | q
13 | 1.000000 0.000000 -0.000000 1.000000 107.597168 19.904175 cm
14 | 0.082353 0.494118 0.984314 scn
15 | 30.101513 3.293037 m
16 | 30.101513 2.464615 30.773085 1.793037 31.601513 1.793037 c
17 | 32.429939 1.793037 33.101513 2.464615 33.101513 3.293037 c
18 | 30.101513 3.293037 l
19 | h
20 | 25.067957 42.223915 m
21 | 23.674679 41.668217 l
22 | 23.676331 41.664108 l
23 | 25.067957 42.223915 l
24 | h
25 | 14.178696 65.718819 m
26 | 15.513352 66.403534 l
27 | 15.507772 66.414200 l
28 | 14.178696 65.718819 l
29 | h
30 | 4.098353 68.942398 m
31 | 3.422246 70.281395 l
32 | 3.416876 70.278656 l
33 | 4.098353 68.942398 l
34 | h
35 | 0.800461 58.899719 m
36 | 2.141096 59.572552 l
37 | 2.139977 59.574772 l
38 | 0.800461 58.899719 l
39 | h
40 | 2.633274 51.903721 m
41 | 3.004868 51.163307 3.906327 50.864326 4.646739 51.235920 c
42 | 5.387150 51.607513 5.686135 52.508972 5.314540 53.249382 c
43 | 2.633274 51.903721 l
44 | h
45 | 33.101513 3.293037 m
46 | 33.101513 9.816757 33.109821 26.251789 26.459581 42.783718 c
47 | 23.676331 41.664108 l
48 | 30.093204 25.712311 30.101513 9.787609 30.101513 3.293037 c
49 | 33.101513 3.293037 l
50 | h
51 | 26.461227 42.779610 m
52 | 22.700573 52.208557 18.380539 60.814678 15.513309 66.403519 c
53 | 12.844082 65.034126 l
54 | 15.701491 59.464432 19.968418 50.960808 23.674685 41.668221 c
55 | 26.461227 42.779610 l
56 | h
57 | 15.507772 66.414200 m
58 | 14.408175 68.515862 12.522062 70.099152 10.260141 70.822487 c
59 | 9.346363 67.965034 l
60 | 10.858726 67.481400 12.116976 66.423737 12.849618 65.023438 c
61 | 15.507772 66.414200 l
62 | h
63 | 10.260141 70.822487 m
64 | 7.998360 71.545776 5.541566 71.351501 3.422251 70.281387 c
65 | 4.774456 67.603409 l
66 | 6.190899 68.318626 7.833860 68.448715 9.346363 67.965034 c
67 | 10.260141 70.822487 l
68 | h
69 | 3.416876 70.278656 m
70 | 1.296715 69.197395 -0.310238 67.324211 -1.051887 65.065750 c
71 | 1.798364 64.129768 l
72 | 2.292186 65.633545 3.363156 66.883652 4.779831 67.606140 c
73 | 3.416876 70.278656 l
74 | h
75 | -1.051887 65.065750 m
76 | -1.793585 62.807144 -1.609107 60.347977 -0.539054 58.224663 c
77 | 2.139977 59.574772 l
78 | 1.427231 60.989079 1.304591 62.626137 1.798364 64.129768 c
79 | -1.051887 65.065750 l
80 | h
81 | -0.540172 58.226887 m
82 | 2.633274 51.903721 l
83 | 5.314540 53.249382 l
84 | 2.141094 59.572548 l
85 | -0.540172 58.226887 l
86 | h
87 | f
88 | n
89 | Q
90 | q
91 | 1.000000 0.000000 -0.000000 1.000000 88.944702 66.358276 cm
92 | 0.082353 0.494118 0.984314 scn
93 | 4.181628 3.823013 m
94 | 4.561399 3.086761 5.466113 2.797777 6.202364 3.177547 c
95 | 6.938615 3.557318 7.227600 4.462032 6.847828 5.198284 c
96 | 4.181628 3.823013 l
97 | h
98 | 0.910119 13.437477 m
99 | 2.243223 14.125115 l
100 | 2.241588 14.128266 l
101 | 0.910119 13.437477 l
102 | h
103 | 14.848384 21.248449 m
104 | 13.539284 20.516050 l
105 | 13.545394 20.505337 l
106 | 14.848384 21.248449 l
107 | h
108 | 16.403994 18.520813 m
109 | 17.743010 19.196856 l
110 | 17.725847 19.230846 l
111 | 17.706984 19.263926 l
112 | 16.403994 18.520813 l
113 | h
114 | 20.291819 7.492126 m
115 | 20.665186 6.752609 21.567360 6.455786 22.306877 6.829153 c
116 | 23.046396 7.202520 23.343220 8.104694 22.969852 8.844213 c
117 | 20.291819 7.492126 l
118 | h
119 | 6.847828 5.198284 m
120 | 2.243219 14.125113 l
121 | -0.422982 12.749842 l
122 | 4.181628 3.823013 l
123 | 6.847828 5.198284 l
124 | h
125 | 2.241588 14.128266 m
126 | 1.484908 15.586740 1.299585 17.272772 1.721112 18.859257 c
127 | -1.178290 19.629625 l
128 | -1.791699 17.320961 -1.521738 14.867644 -0.421351 12.746689 c
129 | 2.241588 14.128266 l
130 | h
131 | 1.721112 18.859257 m
132 | 2.142666 20.445843 3.141511 21.821121 4.525321 22.716885 c
133 | 2.895110 25.235300 l
134 | 0.887509 23.935745 -0.564908 21.938189 -1.178290 19.629625 c
135 | 1.721112 18.859257 l
136 | h
137 | 4.499123 22.700308 m
138 | 7.435656 24.516172 11.871108 23.497871 13.539328 20.516075 c
139 | 16.157440 21.980824 l
140 | 13.594401 26.562033 7.202802 27.899426 2.921308 25.251877 c
141 | 4.499123 22.700308 l
142 | h
143 | 13.545394 20.505337 m
144 | 15.101003 17.777699 l
145 | 17.706984 19.263926 l
146 | 16.151373 21.991562 l
147 | 13.545394 20.505337 l
148 | h
149 | 15.064976 17.844769 m
150 | 20.291819 7.492126 l
151 | 22.969852 8.844213 l
152 | 17.743010 19.196856 l
153 | 15.064976 17.844769 l
154 | h
155 | f
156 | n
157 | Q
158 | q
159 | 1.000000 0.000000 -0.000000 1.000000 70.995850 57.220581 cm
160 | 0.082353 0.494118 0.984314 scn
161 | 8.562653 2.799479 m
162 | 8.939391 2.061670 9.842909 1.768967 10.580716 2.145704 c
163 | 11.318523 2.522444 11.611228 3.425961 11.234490 4.163769 c
164 | 8.562653 2.799479 l
165 | h
166 | 0.813819 21.273285 m
167 | 2.149764 21.955444 l
168 | 2.145404 21.963852 l
169 | 0.813819 21.273285 l
170 | h
171 | 4.422827 31.439951 m
172 | 3.759426 32.785275 l
173 | 3.752470 32.781845 l
174 | 3.745549 32.778343 l
175 | 4.422827 31.439951 l
176 | h
177 | 15.249868 28.960278 m
178 | 16.551033 29.706583 l
179 | 16.498585 29.798023 l
180 | 16.433865 29.881229 l
181 | 15.249868 28.960278 l
182 | h
183 | 16.245453 27.224503 m
184 | 17.561163 27.944855 l
185 | 17.554018 27.957903 l
186 | 17.546616 27.970808 l
187 | 16.245453 27.224503 l
188 | h
189 | 17.543161 21.730782 m
190 | 17.941002 21.004133 18.852577 20.737583 19.579224 21.135422 c
191 | 20.305870 21.533260 20.572422 22.444836 20.174583 23.171482 c
192 | 17.543161 21.730782 l
193 | h
194 | 11.234490 4.163769 m
195 | 2.149737 21.955429 l
196 | -0.522100 20.591141 l
197 | 8.562653 2.799479 l
198 | 11.234490 4.163769 l
199 | h
200 | 2.145404 21.963852 m
201 | 0.640692 24.865314 1.844065 28.453876 5.100106 30.101559 c
202 | 3.745549 32.778343 l
203 | -0.838677 30.458548 -2.870964 25.120277 -0.517767 20.582718 c
204 | 2.145404 21.963852 l
205 | h
206 | 5.086229 30.094627 m
207 | 6.591342 30.836823 8.308313 31.037680 9.945602 30.662697 c
208 | 10.615340 33.586983 l
209 | 8.305876 34.115910 5.883752 33.832817 3.759426 32.785275 c
210 | 5.086229 30.094627 l
211 | h
212 | 9.945602 30.662697 m
213 | 11.582829 30.287729 13.038308 29.360380 14.065873 28.039324 c
214 | 16.433865 29.881229 l
215 | 14.980840 31.749266 12.924868 33.058041 10.615340 33.586983 c
216 | 9.945602 30.662697 l
217 | h
218 | 13.948705 28.213970 m
219 | 14.944288 26.478197 l
220 | 17.546616 27.970808 l
221 | 16.551033 29.706583 l
222 | 13.948705 28.213970 l
223 | h
224 | 14.929742 26.504150 m
225 | 17.543161 21.730782 l
226 | 20.174583 23.171482 l
227 | 17.561163 27.944855 l
228 | 14.929742 26.504150 l
229 | h
230 | f
231 | n
232 | Q
233 | q
234 | 1.000000 0.000000 -0.000000 1.000000 41.874756 -1.757080 cm
235 | 0.082353 0.494118 0.984314 scn
236 | 52.255276 2.280228 m
237 | 52.794823 1.651588 53.741817 1.579369 54.370453 2.118912 c
238 | 54.999092 2.658463 55.071312 3.605453 54.531769 4.234093 c
239 | 52.255276 2.280228 l
240 | h
241 | 19.605707 23.094551 m
242 | 18.641115 21.945831 l
243 | 18.641294 21.945679 l
244 | 19.605707 23.094551 l
245 | h
246 | 1.685100 41.444138 m
247 | 0.574166 40.436256 l
248 | 0.574391 40.436005 l
249 | 1.685100 41.444138 l
250 | h
251 | 14.938883 49.751053 m
252 | 14.147062 48.477058 l
253 | 14.151858 48.474106 l
254 | 14.938883 49.751053 l
255 | h
256 | 29.623823 40.700233 m
257 | 28.836800 39.423286 l
258 | 29.388638 39.083176 30.096523 39.136024 30.591740 39.554314 c
259 | 29.623823 40.700233 l
260 | h
261 | 32.486141 43.117920 m
262 | 33.454056 41.972000 l
263 | 33.992607 42.426888 34.141724 43.195419 33.812378 43.818703 c
264 | 32.486141 43.117920 l
265 | h
266 | 0.876184 102.940048 m
267 | -0.451024 102.241104 l
268 | -0.450053 102.239265 l
269 | 0.876184 102.940048 l
270 | h
271 | 14.378863 110.069115 m
272 | 15.710394 110.759781 l
273 | 15.707027 110.766273 15.703614 110.772736 15.700153 110.779182 c
274 | 14.378863 110.069115 l
275 | h
276 | 29.350103 77.948578 m
277 | 29.731550 77.213196 30.636921 76.926270 31.372305 77.307716 c
278 | 32.107689 77.689163 32.394611 78.594536 32.013168 79.329918 c
279 | 29.350103 77.948578 l
280 | h
281 | 54.531769 4.234093 m
282 | 53.941753 4.921532 53.042351 5.633476 52.014084 6.341637 c
283 | 50.962776 7.065659 49.682655 7.848686 48.258492 8.668137 c
284 | 45.411137 10.306473 41.902874 12.138695 38.335388 13.995056 c
285 | 34.754112 15.858589 31.108093 17.749031 27.936901 19.525261 c
286 | 24.741688 21.314949 22.131063 22.933098 20.570122 24.243423 c
287 | 18.641294 21.945679 l
288 | 20.440464 20.435371 23.274471 18.698219 26.470863 16.907875 c
289 | 29.691277 15.104073 33.383839 13.189766 36.950584 11.333794 c
290 | 40.531120 9.470642 43.980316 7.668587 46.762318 6.067856 c
291 | 48.152836 5.267761 49.355370 4.530052 50.312496 3.870888 c
292 | 51.292667 3.195854 51.927483 2.662148 52.255276 2.280228 c
293 | 54.531769 4.234093 l
294 | h
295 | 20.570299 24.243271 m
296 | 16.308598 27.821861 8.105620 36.602180 2.795808 42.452271 c
297 | 0.574391 40.436005 l
298 | 5.842715 34.631630 14.191412 25.682281 18.641115 21.945831 c
299 | 20.570299 24.243271 l
300 | h
301 | 2.796033 42.452019 m
302 | 2.368433 42.923340 2.055722 43.485901 1.881652 44.096046 c
303 | -1.003242 43.273010 l
304 | -0.702537 42.218987 -0.162680 41.248436 0.574166 40.436256 c
305 | 2.796033 42.452019 l
306 | h
307 | 1.881652 44.096046 m
308 | 1.707587 44.706177 1.676713 45.347900 1.791360 45.971710 c
309 | -1.159222 46.513985 l
310 | -1.357340 45.435997 -1.303951 44.327057 -1.003242 43.273010 c
311 | 1.881652 44.096046 l
312 | h
313 | 1.791360 45.971710 m
314 | 1.906011 46.595535 2.163192 47.185143 2.543410 47.694855 c
315 | 0.138738 49.488609 l
316 | -0.516876 48.609703 -0.961107 47.591957 -1.159222 46.513985 c
317 | 1.791360 45.971710 l
318 | h
319 | 2.543410 47.694855 m
320 | 2.923647 48.204590 3.416828 48.620872 3.985142 48.911095 c
321 | 2.620721 51.582867 l
322 | 1.643657 51.083900 0.794334 50.367485 0.138738 49.488609 c
323 | 2.543410 47.694855 l
324 | h
325 | 3.983269 48.910141 m
326 | 5.570595 49.717957 7.341166 50.102791 9.122582 50.026863 c
327 | 9.250330 53.024147 l
328 | 6.953609 53.122036 4.670328 52.625942 2.622593 51.583820 c
329 | 3.983269 48.910141 l
330 | h
331 | 9.122582 50.026863 m
332 | 10.903997 49.950943 12.634983 49.416870 14.147068 48.477066 c
333 | 15.730698 51.025032 l
334 | 13.779592 52.237698 11.547053 52.926258 9.250330 53.024147 c
335 | 9.122582 50.026863 l
336 | h
337 | 14.151858 48.474106 m
338 | 28.836800 39.423286 l
339 | 30.410849 41.977180 l
340 | 15.725907 51.027996 l
341 | 14.151858 48.474106 l
342 | h
343 | 30.591740 39.554314 m
344 | 33.454056 41.972000 l
345 | 31.518227 44.263847 l
346 | 28.655907 41.846161 l
347 | 30.591740 39.554314 l
348 | h
349 | 33.812378 43.818703 m
350 | 2.202421 103.640831 l
351 | -0.450053 102.239265 l
352 | 31.159904 42.417145 l
353 | 33.812378 43.818703 l
354 | h
355 | 2.203390 103.638992 m
356 | 1.463501 105.043953 1.300187 106.679581 1.747344 108.201660 c
357 | -1.131015 109.047272 l
358 | -1.798195 106.776253 -1.554216 104.335930 -0.451022 102.241104 c
359 | 2.203390 103.638992 l
360 | h
361 | 1.747344 108.201660 m
362 | 2.194540 109.723877 3.217949 111.015297 4.603770 111.802422 c
363 | 3.122133 114.411011 l
364 | 1.061644 113.240685 -0.463873 111.318153 -1.131015 109.047272 c
365 | 1.747344 108.201660 l
366 | h
367 | 4.599438 111.799965 m
368 | 5.313483 112.202408 6.101340 112.458717 6.916702 112.553635 c
369 | 6.569831 115.533508 l
370 | 5.358835 115.392548 4.188111 115.011810 3.126465 114.413460 c
371 | 4.599438 111.799965 l
372 | h
373 | 6.916702 112.553635 m
374 | 7.732068 112.648544 8.558220 112.580101 9.346520 112.352394 c
375 | 10.179056 115.234566 l
376 | 9.007913 115.572861 7.780822 115.674477 6.569831 115.533508 c
377 | 6.916702 112.553635 l
378 | h
379 | 9.346520 112.352394 m
380 | 10.134811 112.124695 10.869125 111.742386 11.506388 111.228226 c
381 | 13.390175 113.563042 l
382 | 12.442001 114.328049 11.350207 114.896271 10.179056 115.234566 c
383 | 9.346520 112.352394 l
384 | h
385 | 11.506388 111.228226 m
386 | 12.143638 110.714081 12.670938 110.078499 13.057572 109.359047 c
387 | 15.700153 110.779182 l
388 | 15.123836 111.851593 14.338361 112.798019 13.390175 113.563042 c
389 | 11.506388 111.228226 l
390 | h
391 | 13.047332 109.378448 m
392 | 29.350103 77.948578 l
393 | 32.013168 79.329918 l
394 | 15.710394 110.759781 l
395 | 13.047332 109.378448 l
396 | h
397 | f
398 | n
399 | Q
400 | q
401 | 0.980061 -0.198696 -0.198696 -0.980061 18.139366 124.988373 cm
402 | 0.082353 0.494118 0.984314 scn
403 | 14.304977 14.235680 m
404 | 14.304977 10.721280 11.443786 7.862720 7.902489 7.862720 c
405 | 7.902489 4.862720 l
406 | 13.090040 4.862720 17.304977 9.053846 17.304977 14.235680 c
407 | 14.304977 14.235680 l
408 | h
409 | 7.902489 7.862720 m
410 | 4.361192 7.862720 1.500000 10.721280 1.500000 14.235680 c
411 | -1.500000 14.235680 l
412 | -1.500000 9.053846 2.714937 4.862720 7.902489 4.862720 c
413 | 7.902489 7.862720 l
414 | h
415 | 1.500000 14.235680 m
416 | 1.500000 17.750080 4.361194 20.608643 7.902489 20.608643 c
417 | 7.902489 23.608643 l
418 | 2.714936 23.608643 -1.500000 19.417511 -1.500000 14.235680 c
419 | 1.500000 14.235680 l
420 | h
421 | 7.902489 20.608643 m
422 | 11.443784 20.608643 14.304977 17.750080 14.304977 14.235680 c
423 | 17.304977 14.235680 l
424 | 17.304977 19.417511 13.090041 23.608643 7.902489 23.608643 c
425 | 7.902489 20.608643 l
426 | h
427 | f
428 | n
429 | Q
430 | q
431 | 0.980061 -0.198696 -0.198696 -0.980061 18.744490 108.637466 cm
432 | 0.082353 0.494118 0.984314 scn
433 | 17.366123 73.170639 m
434 | 17.836908 73.852287 17.665968 74.786530 16.984314 75.257317 c
435 | 16.302660 75.728104 15.368423 75.557159 14.897636 74.875504 c
436 | 17.366123 73.170639 l
437 | h
438 | -0.216979 6.223854 m
439 | -0.062933 5.409882 0.721807 4.874901 1.535785 5.028946 c
440 | 2.349764 5.182991 2.884745 5.967728 2.730698 6.781708 c
441 | -0.216979 6.223854 l
442 | h
443 | 14.897636 74.875504 m
444 | 5.401996 61.126732 -5.226364 32.693359 -0.216979 6.223854 c
445 | 2.730698 6.781708 l
446 | -2.091360 32.261379 8.194466 59.890961 17.366123 73.170639 c
447 | 14.897636 74.875504 l
448 | h
449 | f
450 | n
451 | Q
452 | q
453 | 0.980061 -0.198696 -0.198696 -0.980061 12.316896 50.234650 cm
454 | 0.082353 0.494118 0.984314 scn
455 | -0.590866 13.476810 m
456 | -1.352314 13.150484 -1.705049 12.268669 -1.378723 11.507221 c
457 | -1.052397 10.745773 -0.170581 10.393038 0.590866 10.719364 c
458 | -0.590866 13.476810 l
459 | h
460 | 11.138151 16.871460 m
461 | 12.604726 17.186354 l
462 | 12.510608 17.624699 12.225348 17.997963 11.827100 18.203882 c
463 | 11.428851 18.409803 10.959372 18.426786 10.547285 18.250183 c
464 | 11.138151 16.871460 l
465 | h
466 | 12.714355 5.946629 m
467 | 12.996131 5.167595 13.856087 4.764487 14.635121 5.046264 c
468 | 15.414155 5.328039 15.817262 6.187996 15.535486 6.967030 c
469 | 12.714355 5.946629 l
470 | h
471 | 0.590866 10.719364 m
472 | 11.729017 15.492737 l
473 | 10.547285 18.250183 l
474 | -0.590866 13.476810 l
475 | 0.590866 10.719364 l
476 | h
477 | 9.671576 16.556566 m
478 | 10.444746 12.955635 11.461326 9.410912 12.714355 5.946629 c
479 | 15.535486 6.967030 l
480 | 14.328569 10.303824 13.349420 13.718046 12.604726 17.186354 c
481 | 9.671576 16.556566 l
482 | h
483 | f
484 | n
485 | Q
486 |
487 | endstream
488 | endobj
489 |
490 | 3 0 obj
491 | 12527
492 | endobj
493 |
494 | 4 0 obj
495 | << /Annots []
496 | /Type /Page
497 | /MediaBox [ 0.000000 0.000000 140.698730 120.520630 ]
498 | /Resources 1 0 R
499 | /Contents 2 0 R
500 | /Parent 5 0 R
501 | >>
502 | endobj
503 |
504 | 5 0 obj
505 | << /Kids [ 4 0 R ]
506 | /Count 1
507 | /Type /Pages
508 | >>
509 | endobj
510 |
511 | 6 0 obj
512 | << /Type /Catalog
513 | /Pages 5 0 R
514 | >>
515 | endobj
516 |
517 | xref
518 | 0 7
519 | 0000000000 65535 f
520 | 0000000010 00000 n
521 | 0000000034 00000 n
522 | 0000012617 00000 n
523 | 0000012641 00000 n
524 | 0000012816 00000 n
525 | 0000012890 00000 n
526 | trailer
527 | << /ID [ (some) (id) ]
528 | /Root 6 0 R
529 | /Size 7
530 | >>
531 | startxref
532 | 12949
533 | %%EOF
--------------------------------------------------------------------------------
/Example/Example/Resources/Assets.xcassets/Logo.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Logo_light.pdf",
5 | "idiom" : "universal"
6 | },
7 | {
8 | "appearances" : [
9 | {
10 | "appearance" : "luminosity",
11 | "value" : "dark"
12 | }
13 | ],
14 | "filename" : "Logo_dark.pdf",
15 | "idiom" : "universal"
16 | }
17 | ],
18 | "info" : {
19 | "author" : "xcode",
20 | "version" : 1
21 | },
22 | "properties" : {
23 | "preserves-vector-representation" : true
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Example/Example/Resources/Assets.xcassets/Logo.imageset/Logo_dark.pdf:
--------------------------------------------------------------------------------
1 | %PDF-1.7
2 |
3 | 1 0 obj
4 | << /ExtGState << /E1 << /ca 0.620000 >> >> >>
5 | endobj
6 |
7 | 2 0 obj
8 | << /Length 3 0 R >>
9 | stream
10 | /DeviceRGB CS
11 | /DeviceRGB cs
12 | q
13 | /E1 gs
14 | 1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm
15 | 1.000000 1.000000 1.000000 scn
16 | 20.122700 64.000000 m
17 | 0.000000 64.000000 l
18 | 0.000000 0.000000 l
19 | 40.000000 0.000000 l
20 | 24.539877 24.861538 l
21 | 33.374233 27.076923 40.000000 34.707691 40.000000 44.307693 c
22 | 40.000000 55.138462 31.165646 64.000000 20.122700 64.000000 c
23 | h
24 | f
25 | n
26 | Q
27 | q
28 | 1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm
29 | 1.000000 1.000000 1.000000 scn
30 | 0.000000 0.000000 m
31 | 40.000000 0.000000 l
32 | 0.000000 64.000000 l
33 | 0.000000 0.000000 l
34 | h
35 | f
36 | n
37 | Q
38 |
39 | endstream
40 | endobj
41 |
42 | 3 0 obj
43 | 545
44 | endobj
45 |
46 | 4 0 obj
47 | << /Annots []
48 | /Type /Page
49 | /MediaBox [ 0.000000 0.000000 40.000000 64.000000 ]
50 | /Resources 1 0 R
51 | /Contents 2 0 R
52 | /Parent 5 0 R
53 | >>
54 | endobj
55 |
56 | 5 0 obj
57 | << /Kids [ 4 0 R ]
58 | /Count 1
59 | /Type /Pages
60 | >>
61 | endobj
62 |
63 | 6 0 obj
64 | << /Type /Catalog
65 | /Pages 5 0 R
66 | >>
67 | endobj
68 |
69 | xref
70 | 0 7
71 | 0000000000 65535 f
72 | 0000000010 00000 n
73 | 0000000074 00000 n
74 | 0000000675 00000 n
75 | 0000000697 00000 n
76 | 0000000870 00000 n
77 | 0000000944 00000 n
78 | trailer
79 | << /ID [ (some) (id) ]
80 | /Root 6 0 R
81 | /Size 7
82 | >>
83 | startxref
84 | 1003
85 | %%EOF
--------------------------------------------------------------------------------
/Example/Example/Resources/Assets.xcassets/Logo.imageset/Logo_light.pdf:
--------------------------------------------------------------------------------
1 | %PDF-1.7
2 |
3 | 1 0 obj
4 | << /ExtGState << /E1 << /ca 0.620000 >> >> >>
5 | endobj
6 |
7 | 2 0 obj
8 | << /Length 3 0 R >>
9 | stream
10 | /DeviceRGB CS
11 | /DeviceRGB cs
12 | q
13 | /E1 gs
14 | 1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm
15 | 0.121569 0.403922 0.850980 scn
16 | 20.122700 64.000000 m
17 | 0.000000 64.000000 l
18 | 0.000000 0.000000 l
19 | 40.000000 0.000000 l
20 | 24.539877 24.861538 l
21 | 33.374233 27.076923 40.000000 34.707691 40.000000 44.307693 c
22 | 40.000000 55.138462 31.165646 64.000000 20.122700 64.000000 c
23 | h
24 | f
25 | n
26 | Q
27 | q
28 | 1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm
29 | 0.121569 0.403922 0.850980 scn
30 | 0.000000 0.000000 m
31 | 40.000000 0.000000 l
32 | 0.000000 64.000000 l
33 | 0.000000 0.000000 l
34 | h
35 | f
36 | n
37 | Q
38 |
39 | endstream
40 | endobj
41 |
42 | 3 0 obj
43 | 545
44 | endobj
45 |
46 | 4 0 obj
47 | << /Annots []
48 | /Type /Page
49 | /MediaBox [ 0.000000 0.000000 40.000000 64.000000 ]
50 | /Resources 1 0 R
51 | /Contents 2 0 R
52 | /Parent 5 0 R
53 | >>
54 | endobj
55 |
56 | 5 0 obj
57 | << /Kids [ 4 0 R ]
58 | /Count 1
59 | /Type /Pages
60 | >>
61 | endobj
62 |
63 | 6 0 obj
64 | << /Type /Catalog
65 | /Pages 5 0 R
66 | >>
67 | endobj
68 |
69 | xref
70 | 0 7
71 | 0000000000 65535 f
72 | 0000000010 00000 n
73 | 0000000074 00000 n
74 | 0000000675 00000 n
75 | 0000000697 00000 n
76 | 0000000870 00000 n
77 | 0000000944 00000 n
78 | trailer
79 | << /ID [ (some) (id) ]
80 | /Root 6 0 R
81 | /Size 7
82 | >>
83 | startxref
84 | 1003
85 | %%EOF
--------------------------------------------------------------------------------
/Example/Example/Resources/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
24 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/Example/Example/Resources/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UIApplicationSceneManifest
24 |
25 | UIApplicationSupportsMultipleScenes
26 |
27 | UISceneConfigurations
28 |
29 | UIWindowSceneSessionRoleApplication
30 |
31 |
32 | UISceneConfigurationName
33 | Default Configuration
34 | UISceneDelegateClassName
35 | $(PRODUCT_MODULE_NAME).SceneDelegate
36 |
37 |
38 |
39 |
40 | UILaunchStoryboardName
41 | LaunchScreen
42 | UIRequiredDeviceCapabilities
43 |
44 | armv7
45 |
46 | UISupportedInterfaceOrientations
47 |
48 | UIInterfaceOrientationPortrait
49 |
50 | UISupportedInterfaceOrientations~ipad
51 |
52 | UIInterfaceOrientationPortrait
53 | UIInterfaceOrientationPortraitUpsideDown
54 | UIInterfaceOrientationLandscapeLeft
55 | UIInterfaceOrientationLandscapeRight
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/Example/Example/Screens/FirstView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FirstView.swift
3 | // Example
4 | //
5 | // Created by Igor K. on 05.05.2020.
6 | // Copyright © 2020 Ramotion. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SwiftUI
11 |
12 | struct FirstView: View {
13 |
14 | var body: some View {
15 | Screen(color: .background) {
16 | VStack(alignment: .leading) {
17 | Image("Logo")
18 | .padding(.leading, 40)
19 |
20 | Text("SwiftUI Blob Menu")
21 | .font(Font.system(size: 24, weight: Font.Weight.semibold))
22 | .foregroundColor(.contrastText)
23 | .lineLimit(1)
24 | .padding(.top, 110)
25 | .padding(.horizontal, 40)
26 |
27 | Text("Open the menu by tapping on the hamburger. To close the menu, just tap outside. In this example, by tapping on the menu you will switch the screen view, but it can be used for other purposes. In code you can update the configuration to the close menu after item has been selected.")
28 | .font(Font.system(size: 16))
29 | .lineSpacing(6)
30 | .foregroundColor(.contrastInformation)
31 | .fixedSize(horizontal: false, vertical: true)
32 | .padding(.top, 24)
33 | .padding(.horizontal, 40)
34 |
35 | Spacer()
36 | HStack { Spacer() }
37 | }
38 | .frame(maxHeight: 500)
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Example/Example/Screens/FourthView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FourthView.swift
3 | // Example
4 | //
5 | // Created by Igor K. on 20.05.2020.
6 | // Copyright © 2020 Ramotion. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 |
11 | private let cardHeight: CGFloat = 156
12 |
13 | struct FourthView: View {
14 |
15 | @Binding var isDragging: Bool
16 |
17 | var body: some View {
18 | let v = (UIScreen.main.bounds.height - cardHeight) / 2
19 | let vd: CGFloat = 50
20 | let insets = UIEdgeInsets(top: v - vd, left: 0, bottom: v + vd, right: 0)
21 |
22 | return Screen(color: .background) {
23 | ZStack {
24 | VStack(alignment: .leading) {
25 | Image("Logo")
26 | .padding(.leading, 40)
27 |
28 | Spacer()
29 |
30 | HStack {
31 | Spacer()
32 | Image("Icon_Swipe")
33 | Spacer()
34 | }
35 | .padding(.bottom, UIScreen.main.bounds.height < 800 ? 50 : 0)
36 | }
37 | .frame(maxHeight: 500)
38 |
39 | ExtendedScrollView(isDragging: self.$isDragging, contentInset: insets) {
40 | ItemCell().background(Color.clear)
41 | }
42 | }
43 | }
44 | }
45 | }
46 |
47 | private struct ItemCell: View {
48 |
49 | var body: some View {
50 | VStack(alignment: .leading) {
51 |
52 | Text("Drag scroll view")
53 | .font(Font.system(size: 24, weight: Font.Weight.semibold))
54 | .foregroundColor(Color.contrastText)
55 | .lineLimit(1)
56 | .padding(Edge.Set.all.subtracting(.bottom))
57 |
58 | Text("This is example shows how you can hide the menu while the user is interacting with other UI.")
59 | .font(Font.system(size: 16))
60 | .lineSpacing(6)
61 | .foregroundColor(Color.contrastInformation)
62 | .fixedSize(horizontal: false, vertical: true)
63 | .padding(.trailing, 60)
64 | .padding(Edge.Set.all.subtracting(.trailing))
65 | }
66 | .frame(height: cardHeight)
67 | .background(Color.cardBackgound)
68 | .clipShape(RoundedRectangle(cornerRadius: 8))
69 | .shadow(color: Color.shadow, radius: 7, y: 3)
70 | .padding(EdgeInsets(top: 7, leading: 15, bottom: 7, trailing: 15))
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/Example/Example/Screens/RootView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ContentView.swift
3 | // Example
4 | //
5 | // Created by Igor K. on 16.04.2020.
6 | // Copyright © 2020 Ramotion. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 | import BlobMenu
11 |
12 | struct RootView: View {
13 |
14 | enum Screen: Int, CaseIterable {
15 | case wallet
16 | case exchange
17 | case commerce
18 | case stocks
19 | }
20 |
21 | @State private var screen: Screen = .wallet
22 | @State private var isDragging: Bool = false
23 |
24 | //You can use blob menu as simple observable object
25 | //@ObservedObject private var blobMenuModel = BlobMenuModel(items: BlobMenuItem.all)
26 |
27 | //Or by using it as environment object
28 | @EnvironmentObject var blobMenuModel: BlobMenuModel
29 |
30 |
31 | var body: some View {
32 | ZStack {
33 | screenView.edgesIgnoringSafeArea(Edge.Set.all.subtracting(.top))
34 | menuView.opacity(isDragging ? 0.05 : 1)
35 | }
36 | .background(Color.background)
37 | }
38 |
39 | private var screenView: some View {
40 | let screen = Screen(rawValue: blobMenuModel.selectedIndex) ?? .wallet
41 | switch screen {
42 | case .wallet: return FirstView().asAnyView
43 | case .exchange: return SecondView().asAnyView
44 | case .commerce: return ThirdView().asAnyView
45 | case .stocks: return FourthView(isDragging: $isDragging.animatable).asAnyView
46 | }
47 | }
48 |
49 | private var menuView: some View {
50 | VStack {
51 | Spacer()
52 | BlobMenuView(model: blobMenuModel).padding(.bottom, 50)
53 | }
54 | }
55 | }
56 |
57 | struct RootView_Previews: PreviewProvider {
58 | static var previews: some View {
59 | RootView()
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/Example/Example/Screens/SecondView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SecondView.swift
3 | // Example
4 | //
5 | // Created by Igor K. on 20.05.2020.
6 | // Copyright © 2020 Ramotion. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 | import BlobMenu
11 |
12 | struct SecondView: View {
13 |
14 | @Environment(\.menuEnvironment) private var menuModel: BlobMenuModel
15 |
16 | var body: some View {
17 | Screen(color: .background) {
18 | VStack(alignment: .leading) {
19 |
20 | Image("Logo")
21 | .padding(.leading, 40)
22 |
23 | Text("Menu model")
24 | .font(Font.system(size: 24, weight: Font.Weight.semibold))
25 | .foregroundColor(.contrastText)
26 | .lineLimit(1)
27 | .padding(.top, 110)
28 | .padding(.horizontal, 40)
29 |
30 | Text("Also, the menu can be opened and closed programmatically. Or switch the selected index")
31 | .font(Font.system(size: 16))
32 | .lineSpacing(6)
33 | .foregroundColor(.contrastInformation)
34 | .fixedSize(horizontal: false, vertical: true)
35 | .padding(.top, 24)
36 | .padding(.horizontal, 40)
37 |
38 | HStack(spacing: 16) {
39 | Buttons.roundRect(title: "Close") { self.menuModel.closeMenu() }
40 | Buttons.roundRect(title: "Open") { self.menuModel.openMenu() }
41 | Buttons.roundRect(title: "Next") { self.menuModel.selectIndex(2) }
42 | }
43 | .padding(.horizontal, 40)
44 | .padding(.top, 40)
45 |
46 | Spacer()
47 | HStack { Spacer() }
48 | }
49 | .frame(maxHeight: 500)
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/Example/Example/Screens/ThirdView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ThirdView.swift
3 | // Example
4 | //
5 | // Created by Igor K. on 20.05.2020.
6 | // Copyright © 2020 Ramotion. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 | import BlobMenu
11 |
12 | struct ThirdView: View {
13 |
14 | @Environment(\.menuEnvironment) private var menuModel: BlobMenuModel
15 |
16 | var body: some View {
17 | Screen(color: .background) {
18 | VStack(alignment: .leading) {
19 |
20 | Image("Logo")
21 | .padding(.leading, 40)
22 |
23 | Text("Menu items")
24 | .font(Font.system(size: 24, weight: Font.Weight.semibold))
25 | .foregroundColor(.contrastText)
26 | .lineLimit(1)
27 | .padding(.top, 110)
28 | .padding(.horizontal, 40)
29 |
30 | Text("The menu supports dynamically updating item count. If the UI layout can’t to show all items simultaneously, they will be wrapped in a scroll view.")
31 | .font(Font.system(size: 16))
32 | .lineSpacing(6)
33 | .foregroundColor(.contrastInformation)
34 | .fixedSize(horizontal: false, vertical: true)
35 | .padding(.top, 24)
36 | .padding(.horizontal, 40)
37 |
38 | HStack(spacing: 16) {
39 | Buttons.roundRect(title: "Remove") { self.menuModel.items = BlobMenuItem.standard }
40 | Buttons.roundRect(title: "Add") { self.menuModel.items = BlobMenuItem.extended }
41 | }
42 | .padding(.horizontal, 40)
43 | .padding(.top, 40)
44 |
45 | Spacer()
46 | HStack { Spacer() }
47 | }
48 | .frame(maxHeight: 500)
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/Example/Example/UI/ExtendedScrollView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ExtendedScrollView.swift
3 | // Example
4 | //
5 | // Created by Igor K. on 19.05.2020.
6 | // Copyright © 2020 Ramotion. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SwiftUI
11 |
12 | struct ExtendedScrollView: UIViewRepresentable {
13 |
14 | private let isDragging: Binding
15 | private let scrollView = UIScrollView()
16 |
17 | func makeCoordinator() -> Coordinator {
18 | return Coordinator(control: self, isDragging: isDragging)
19 | }
20 |
21 | func makeUIView(context: Context) -> UIScrollView {
22 | scrollView.delegate = context.coordinator
23 | return scrollView
24 | }
25 |
26 | func updateUIView(_ uiView: UIScrollView, context: Context) {
27 |
28 | }
29 |
30 | init(axis: Axis = .vertical,
31 | isDragging: Binding = .constant(false),
32 | showsIndicators: Bool = false,
33 | alwaysBounce: Bool = true,
34 | contentInset: UIEdgeInsets = .zero,
35 | @ViewBuilder content: () -> Content) {
36 |
37 | self.isDragging = isDragging
38 |
39 | let hosting = UIHostingController(rootView: content())
40 | hosting.view.translatesAutoresizingMaskIntoConstraints = false
41 | hosting.view.backgroundColor = .clear
42 | hosting.edgesForExtendedLayout = .all
43 | hosting.extendedLayoutIncludesOpaqueBars = true
44 |
45 | scrollView.addSubview(hosting.view)
46 | scrollView.showsVerticalScrollIndicator = showsIndicators
47 | scrollView.showsHorizontalScrollIndicator = showsIndicators
48 | scrollView.contentInsetAdjustmentBehavior = .never
49 | scrollView.contentInset = contentInset
50 | scrollView.contentOffset = .zero
51 | scrollView.backgroundColor = .clear
52 |
53 | let constraints: [NSLayoutConstraint]
54 | switch axis {
55 | case .horizontal:
56 | scrollView.alwaysBounceHorizontal = alwaysBounce
57 |
58 | constraints = [
59 | hosting.view.leadingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.leadingAnchor),
60 | hosting.view.trailingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.trailingAnchor),
61 | hosting.view.topAnchor.constraint(equalTo: scrollView.topAnchor),
62 | hosting.view.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
63 | hosting.view.heightAnchor.constraint(equalTo: scrollView.heightAnchor)
64 | ]
65 | case .vertical:
66 | scrollView.alwaysBounceVertical = alwaysBounce
67 |
68 | constraints = [
69 | hosting.view.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
70 | hosting.view.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor),
71 | hosting.view.topAnchor.constraint(equalTo: scrollView.contentLayoutGuide.topAnchor),
72 | hosting.view.bottomAnchor.constraint(equalTo: scrollView.contentLayoutGuide.bottomAnchor),
73 | hosting.view.widthAnchor.constraint(equalTo: scrollView.widthAnchor)
74 | ]
75 | }
76 | scrollView.addConstraints(constraints)
77 | }
78 |
79 | final class Coordinator: NSObject, UIScrollViewDelegate {
80 | private let isDragging: Binding
81 | private let control: ExtendedScrollView
82 |
83 | init(control: ExtendedScrollView, isDragging: Binding) {
84 | self.control = control
85 | self.isDragging = isDragging
86 | }
87 |
88 | //MARK: - UIScrollView delegate methods
89 | func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
90 | isDragging.wrappedValue = true
91 | }
92 |
93 | func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
94 | isDragging.wrappedValue = false
95 | }
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/Example/Example/Utilities/Screen.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Screen.swift
3 | // Example
4 | //
5 | // Created by Igor K. on 22.05.2020.
6 | // Copyright © 2020 Ramotion. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SwiftUI
11 |
12 | struct Screen: View where Content: View {
13 | let content: () -> Content
14 | let backgoundColor: Color
15 |
16 | init(color: Color = .white, @ViewBuilder content: @escaping () -> Content) {
17 | self.content = content
18 | self.backgoundColor = color
19 | }
20 |
21 | var body: some View {
22 | ZStack {
23 | backgoundColor.edgesIgnoringSafeArea(.all)
24 | content()
25 | }
26 | .frame(size: UIScreen.main.bounds.size)
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Example/Example/Utilities/Utilities.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Utilities.swift
3 | // Example
4 | //
5 | // Created by Igor K. on 05.05.2020.
6 | // Copyright © 2020 Ramotion. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SwiftUI
11 |
12 | extension Binding where Value == Bool {
13 | var animatable: Binding {
14 | return Binding(get: { return self.wrappedValue },
15 | set: { b in withAnimation { self.wrappedValue = b } })
16 | }
17 | }
18 |
19 | struct Buttons {
20 | static func roundRect(title: String, action: @escaping () -> Void) -> some View {
21 | Button(action: action, label: {
22 | Text(title).font(Font.system(size: 15))
23 | })
24 | .buttonStyle(RoundRectStyle(fontColor: Color.buttonText, backgroundColor: Color.buttonBackground))
25 | }
26 | }
27 |
28 | struct RoundRectStyle: ButtonStyle {
29 | let fontColor: Color
30 | let backgroundColor: Color
31 |
32 | public func makeBody(configuration: RoundRectStyle.Configuration) -> some View {
33 | return RoundRectButton(fontColor: fontColor, backgroundColor: backgroundColor, configuration: configuration)
34 | }
35 |
36 | private struct RoundRectButton: View {
37 | let fontColor: Color
38 | let backgroundColor: Color
39 | let configuration: ButtonStyle.Configuration
40 |
41 | @Environment(\.isEnabled) private var isEnabled: Bool
42 |
43 | var body: some View {
44 | configuration.label
45 | .foregroundColor(isEnabled ? fontColor : backgroundColor.opacity(0.6))
46 | .padding(.horizontal, 24)
47 | .frame(height: 50)
48 | .background(
49 | RoundedRectangle(cornerRadius: 25)
50 | .fill(isEnabled ? backgroundColor : backgroundColor.opacity(0.1))
51 | )
52 | .compositingGroup()
53 | .opacity(configuration.isPressed ? 0.7 : 1.0)
54 | .scaleEffect(configuration.isPressed ? 0.97 : 1.0)
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Ramotion
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.1
2 | //
3 | // Package.swift
4 | //
5 | // Copyright (c) Ramotion Inc. (https://www.ramotion.com/)
6 | //
7 | // Permission is hereby granted, free of charge, to any person obtaining a copy
8 | // of this software and associated documentation files (the "Software"), to deal
9 | // in the Software without restriction, including without limitation the rights
10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | // copies of the Software, and to permit persons to whom the Software is
12 | // furnished to do so, subject to the following conditions:
13 | //
14 | // The above copyright notice and this permission notice shall be included in
15 | // all copies or substantial portions of the Software.
16 | //
17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | // THE SOFTWARE.
24 | //
25 |
26 | import PackageDescription
27 |
28 |
29 | let package = Package(
30 | name: "BlobMenu",
31 | platforms: [
32 | .iOS(.v13)
33 | ],
34 | products: [
35 | .library(name: "BlobMenu",
36 | targets: ["BlobMenu"]),
37 | ],
38 | targets: [
39 | .target(name: "BlobMenu",
40 | path: "Sources")
41 | ],
42 | swiftLanguageVersions: [.v5]
43 | )
44 |
--------------------------------------------------------------------------------
/Promo/Blob-Menu-dark.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ramotion/blob-menu/4c67580e39dda4ddff0f75b9285e4a1919334fb6/Promo/Blob-Menu-dark.gif
--------------------------------------------------------------------------------
/Promo/Blob-Menu-full.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ramotion/blob-menu/4c67580e39dda4ddff0f75b9285e4a1919334fb6/Promo/Blob-Menu-full.gif
--------------------------------------------------------------------------------
/Promo/Blob-Menu-light.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ramotion/blob-menu/4c67580e39dda4ddff0f75b9285e4a1919334fb6/Promo/Blob-Menu-light.gif
--------------------------------------------------------------------------------
/Promo/Header.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ramotion/blob-menu/4c67580e39dda4ddff0f75b9285e4a1919334fb6/Promo/Header.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | BLOB MENU
7 |
8 | Swift UI menu library.
9 | ___
10 |
11 | We specialize in the designing and coding of custom UI for Mobile Apps and Websites.
12 |
13 |
14 |
15 | Stay tuned for the latest updates:
16 |
17 |
18 |
19 |
20 |
21 | [](http://twitter.com/Ramotion)
22 | [](https://github.com/Ramotion/blob-menu)
23 | [](https://codebeat.co/projects/github-com-ramotion-blob-menu)
24 | [](https://developer.apple.com/swift/)
25 | [](https://paypal.me/Ramotion)
26 |
27 |
28 | ## Requirements
29 |
30 | - iOS 13.0
31 | - Xcode 11.4
32 | - Swift 5.0
33 |
34 |
35 | ## Installation
36 | You can install `blob-menu` in several ways:
37 |
38 | - By adding the source files to your project.
39 |
40 |
41 |
42 | - Via [Swift Package Manager](https://swift.org/package-manager/).
43 |
44 |
45 |
46 | - Via [CocoaPods](https://cocoapods.org):
47 | ``` ruby
48 | pod 'blob-menu'
49 | ```
50 |
51 |
52 |
53 | - Via [Carthage](https://github.com/Carthage/Carthage):
54 | ```
55 | github "Ramotion/blob-menu"
56 | ```
57 |
58 | ## Usage
59 |
60 | ### Blob Menu
61 |
62 | 1. Create several menu items. You need to provide at least one image icon to initialize `BlobMenuItem`.
63 | 2. Use these menu items to create a menu model `BlobMenuModel`.
64 |
65 | `public init(items: [BlobMenuItem], selectedIndex: Int = 0, isOpened: Bool = false)`
66 |
67 | 3. Finally, use the menu model to initialize `BlobMenuView`. You can use this view in your layout.
68 |
69 | ### Example
70 | ```
71 | extension BlobMenuItem {
72 | static let all = [
73 | BlobMenuItem(icon: <#Image#>),
74 | BlobMenuItem(icon: <#Image#>),
75 | BlobMenuItem(icon: <#Image#>),
76 | BlobMenuItem(icon: <#Image#>)
77 | ]
78 | }
79 |
80 | struct ContentView: View {
81 |
82 | @ObservedObject private var model = BlobMenuModel(items: BlobMenuItem.all)
83 |
84 | var body: some View {
85 | VStack {
86 | Spacer()
87 | BlobMenuView(model: model).padding(.bottom, 30)
88 | }
89 | }
90 | }
91 | ```
92 |
93 | ### Configuration
94 |
95 | To configure additional menu parameters, use a custom `BlobMenuConfiguration` in the `BlobMenuView` init method.
96 |
97 | `public init(model: BlobMenuModel, configuration: BlobMenuConfiguration = .default)`
98 |
99 | Right now we provide only color parameters. If you have ideas on what else should be configurable, please create an issue with `suggestion` label.
100 | For more integration details, please take a look at the `Example` project.
101 |
102 | ## �� License
103 |
104 | Blob Menu is released under the MIT license.
105 | See [LICENSE](./LICENSE) for details.
106 |
107 | This library is a part of a selection of our best UI open-source projects.
108 |
109 | If you use the open-source library in your project, please make sure to credit and backlink to www.ramotion.com
110 |
111 |
112 | ## �� Get the Showroom App for iOS to give it a try
113 | Try this UI component and more like this in our iOS app. Contact us if interested.
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
--------------------------------------------------------------------------------
/Sources/BlobMenu.h:
--------------------------------------------------------------------------------
1 | //
2 | // BlobMenu.h
3 | // BlobMenu
4 | //
5 | // Created by Igor K. on 15.04.2020.
6 | // Copyright © 2020 Ramotion. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | //! Project version number for BlobMenu.
12 | FOUNDATION_EXPORT double BlobMenuVersionNumber;
13 |
14 | //! Project version string for BlobMenu.
15 | FOUNDATION_EXPORT const unsigned char BlobMenuVersionString[];
16 |
17 | // In this header, you should import all the public headers of your framework using statements like #import
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Sources/Configuration/BlobMenuConfiguration.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BlobMenuConfiguration.swift
3 | // BlobMenu
4 | //
5 | // Created by Igor K. on 23.05.2020.
6 | // Copyright © 2020 Ramotion. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SwiftUI
11 |
12 | public struct BlobMenuConfiguration {
13 |
14 | public let hamburgerColor: Color
15 | public let backgroundColor: Color
16 | public let selectionColor: Color
17 |
18 | public init(hamburgerColor: Color,
19 | backgroundColor: Color,
20 | selectionColor: Color) {
21 |
22 | self.hamburgerColor = hamburgerColor
23 | self.backgroundColor = backgroundColor
24 | self.selectionColor = selectionColor
25 | }
26 |
27 | public static var `default`: BlobMenuConfiguration {
28 | BlobMenuConfiguration(hamburgerColor: Color.hamburgerColor,
29 | backgroundColor: Color.backgroundColor,
30 | selectionColor: Color.selectionColor)
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Sources/Configuration/Theme.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Theme.swift
3 | // BlobMenu
4 | //
5 | // Created by Igor K. on 16.04.2020.
6 | // Copyright © 2020 Ramotion. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SwiftUI
11 |
12 | extension Color {
13 |
14 | static var hamburgerColor: Color {
15 | return Color(UIColor { $0.userInterfaceStyle == .dark ? #colorLiteral(red: 0.1960526407, green: 0.1960932612, blue: 0.1960500479, alpha: 1) : #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0) })
16 | }
17 |
18 | static var backgroundColor: Color {
19 | return Color(UIColor { $0.userInterfaceStyle == .dark ? #colorLiteral(red: 0.9705940673, green: 0.9705940673, blue: 0.9705940673, alpha: 1) : #colorLiteral(red: 0.1960526407, green: 0.1960932612, blue: 0.1960500479, alpha: 1) })
20 | }
21 |
22 | static let selectionColor = Color(#colorLiteral(red: 0.9983773828, green: 0.7375702262, blue: 0.1739521325, alpha: 1))
23 | }
24 |
--------------------------------------------------------------------------------
/Sources/Effects/Transitions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Transitions.swift
3 | // BlobMenu
4 | //
5 | // Created by Igor K. on 23.04.2020.
6 | // Copyright © 2020 Ramotion. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SwiftUI
11 |
12 | extension AnyTransition {
13 | static var blobBlobMenuItem: AnyTransition {
14 | return AnyTransition.scale.combined(with: AnyTransition.rotation)
15 | .animation(.easeOut(duration: 0.35))
16 | }
17 | }
18 |
19 |
20 | extension AnyTransition {
21 |
22 | static var rotation: AnyTransition {
23 | return AnyTransition.asymmetric(insertion: .rotationIn, removal: .rotationOut)
24 | }
25 |
26 | static var rotationIn: AnyTransition {
27 | return AnyTransition.modifier(active: RotationTransition(percentage: -0.15), identity: RotationTransition(percentage: 0))
28 | }
29 |
30 | static var rotationOut: AnyTransition {
31 | return AnyTransition.modifier(active: RotationTransition(percentage: 0.85), identity: RotationTransition(percentage: 1))
32 | }
33 | }
34 |
35 | struct RotationTransition: GeometryEffect {
36 | var percentage: CGFloat
37 |
38 | var animatableData: CGFloat {
39 | get { percentage }
40 | set { percentage = newValue }
41 | }
42 |
43 | func effectValue(size: CGSize) -> ProjectionTransform {
44 | let angle = CGFloat.animationAngle(percentage: percentage)
45 | let rotation = CGAffineTransform(rotationAngle: angle)
46 | let from = CGAffineTransform(translationX: -size.width / 2.0, y: -size.height / 2.0)
47 | let to = CGAffineTransform(translationX: size.width / 2.0, y: size.height / 2.0)
48 | return ProjectionTransform(from.concatenating(rotation).concatenating(to))
49 | }
50 | }
51 |
52 |
53 | //MARK: - Utilities
54 | extension FloatingPoint {
55 |
56 | static func animationAngle(percentage: T) -> T {
57 | let pi2 = T.pi * 2
58 | let sign: T = percentage < 0 ? -1 : 1
59 | return sign * (pi2 * (1 - percentage)).truncatingRemainder(dividingBy: pi2)
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/Sources/Extensions/CGPoint+Extensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CGPoint+Extensions.swift
3 | // BlobMenu
4 | //
5 | // Created by Igor K. on 29.04.2020.
6 | // Copyright © 2020 Ramotion. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SwiftUI
11 |
12 |
13 | func + (lhs: CGPoint, rhs: CGPoint) -> CGPoint {
14 | return CGPoint(x: lhs.x + rhs.x, y: lhs.y + rhs.y)
15 | }
16 |
17 | func += (lhs: inout CGPoint, rhs: CGPoint) {
18 | lhs = lhs + rhs
19 | }
20 |
21 | func - (lhs: CGPoint, rhs: CGPoint) -> CGPoint {
22 | return CGPoint(x: lhs.x - rhs.x, y: lhs.y - rhs.y)
23 | }
24 |
25 | func -= (lhs: inout CGPoint, rhs: CGPoint) {
26 | lhs = lhs - rhs
27 | }
28 |
29 | func * (lhs: CGPoint, rhs: CGPoint) -> CGPoint {
30 | return CGPoint(x: lhs.x * rhs.x, y: lhs.y * rhs.y)
31 | }
32 |
33 | func *= (lhs: inout CGPoint, rhs: CGPoint) {
34 | lhs = lhs * rhs
35 | }
36 |
37 | func / (lhs: CGPoint, rhs: CGPoint) -> CGPoint {
38 | return CGPoint(x: lhs.x / rhs.x, y: lhs.y / rhs.y)
39 | }
40 |
41 | func /= (lhs: inout CGPoint, rhs: CGPoint) {
42 | lhs = lhs / rhs
43 | }
44 |
45 | func * (lhs: CGSize, rhs: CGFloat) -> CGSize {
46 | return CGSize(width: lhs.width * rhs, height: lhs.height * rhs)
47 | }
48 |
49 |
50 | extension CGPoint {
51 | var length: CGFloat {
52 | return sqrt(self.x * self.x + self.y * self.y)
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/Sources/Extensions/CGRect+Extensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CGRect+Extensions.swift
3 | // BlobMenu
4 | //
5 | // Created by Igor K. on 20.05.2020.
6 | // Copyright © 2020 Ramotion. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SwiftUI
11 |
12 | extension CGRect {
13 |
14 | public init(size: CGSize) {
15 | self.init(origin: .zero, size: size)
16 | }
17 |
18 | public var center: CGPoint {
19 | return CGPoint(x: midX, y: midY)
20 | }
21 |
22 | public init(center: CGPoint, size: CGSize) {
23 | self.init(x: center.x - size.width / 2, y: center.y - size.height / 2, width: size.width, height: size.height)
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Sources/Extensions/CGSize+Extensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CGSize+Extensions.swift
3 | // BlobMenu
4 | //
5 | // Created by Igor K. on 26.04.2020.
6 | // Copyright © 2020 Ramotion. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SwiftUI
11 |
12 | extension CGSize {
13 | public init(uniform side: CGFloat) {
14 | self.init(width: side, height: side)
15 | }
16 | }
17 |
18 |
19 | extension CGSize: VectorArithmetic {
20 |
21 | public mutating func scale(by rhs: Double) {
22 | self.width = self.width * CGFloat(rhs)
23 | self.height = self.height * CGFloat(rhs)
24 | }
25 |
26 | public var magnitudeSquared: Double {
27 | return Double(sqrt( self.width * self.width + self.height * self.height ))
28 | }
29 |
30 | public static func + (lhs: CGSize, rhs: CGSize) -> CGSize {
31 | return CGSize(width: lhs.width + rhs.width, height: lhs.height + rhs.height)
32 | }
33 |
34 | public static func - (lhs: CGSize, rhs: CGSize) -> CGSize {
35 | return CGSize(width: lhs.width - rhs.width, height: lhs.height - rhs.height)
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Sources/Extensions/Collection+Extensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Collection+Extensions.swift
3 | // BlobMenu
4 | //
5 | // Created by Igor K. on 23.04.2020.
6 | // Copyright © 2020 Ramotion. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension Collection {
12 |
13 | func enumeratedArray() -> Array<(offset: Int, element: Self.Element)> {
14 | return Array(self.enumerated())
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Sources/Extensions/Comparable+Extensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Compare+Extensions.swift
3 | // BlobMenu
4 | //
5 | // Created by Igor K. on 23.04.2020.
6 | // Copyright © 2020 Ramotion. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension Comparable {
12 |
13 | /**
14 | Bound the current value between a minimum and maximum value
15 |
16 | - parameter min: The minimum possible value
17 | - parameter max: The maximum possible value
18 |
19 | - returns: The current value bounded between a minimum and maximum value
20 | */
21 | func limited(min: Self, max: Self) -> Self {
22 | var value = self
23 | value.limit(min: min, max: max)
24 | return value
25 | }
26 |
27 | /**
28 | Bound the current value between a minimum and maximum value
29 |
30 | - parameter min: The minimum possible value
31 | - parameter max: The maximum possible value
32 |
33 | - returns: The current value bounded between a minimum and maximum value
34 | */
35 | func limited(_ min: Self, _ max: Self) -> Self {
36 | return limited(min: min, max: max)
37 | }
38 |
39 | /**
40 | Bound self between a minimum and maximum value, in place
41 |
42 | - parameter min: The minimum possible value
43 | - parameter max: The maximum possible value
44 | */
45 | mutating func limit(min: Self, max: Self) {
46 | self = Swift.max(Swift.min(self, max), min)
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/Sources/Extensions/UIGestureRecognizer+Extensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIGestureRecognizer+Extensions.swift
3 | // BlobMenu
4 | //
5 | // Created by Igor K. on 23.04.2020.
6 | // Copyright © 2020 Ramotion. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 |
12 | extension UIGestureRecognizer {
13 | private class GestureAction {
14 | var action: (UIGestureRecognizer) -> Void
15 |
16 | init(action: @escaping (UIGestureRecognizer) -> Void) {
17 | self.action = action
18 | }
19 | }
20 |
21 | private struct AssociatedKeys {
22 | static var ActionName = "action"
23 | }
24 |
25 | private var gestureAction: GestureAction? {
26 | set { objc_setAssociatedObject(self, &AssociatedKeys.ActionName, newValue, .OBJC_ASSOCIATION_RETAIN) }
27 | get { return objc_getAssociatedObject(self, &AssociatedKeys.ActionName) as? GestureAction }
28 | }
29 |
30 | /**
31 | Convenience initializer, associating an action closure with the gesture recognizer (instead of the more traditional target/action).
32 |
33 | - parameter action: The closure for the recognizer to execute. There is no pre-logic to conditionally invoke the closure or not (e.g. only invoke the closure if the gesture recognizer is in a particular state). The closure is merely invoked directly; all handler logic is up to the closure.
34 |
35 | - returns: The UIGestureRecognizer.
36 | */
37 | public convenience init(action: @escaping (UIGestureRecognizer) -> Void) {
38 | self.init()
39 | gestureAction = GestureAction(action: action)
40 | addTarget(self, action: #selector(handleAction(_:)))
41 | }
42 |
43 | @objc private dynamic func handleAction(_ recognizer: UIGestureRecognizer) {
44 | gestureAction?.action(recognizer)
45 | }
46 | }
47 |
48 |
49 | extension UIGestureRecognizer {
50 |
51 | public var isActive: Bool {
52 | return isEnabled && (state == .changed || state == .began)
53 | }
54 | }
55 |
56 |
57 | extension UIView {
58 |
59 | public enum GestureType {
60 | case tap
61 | case longPress
62 | case pan
63 | case swipe(UISwipeGestureRecognizer.Direction)
64 | case tapCount(Int)
65 | }
66 |
67 | public func addGesture(type: GestureType, callback: @escaping (UIGestureRecognizer) -> Void) {
68 | switch type {
69 | case .tap:
70 | addGestureRecognizer(UITapGestureRecognizer(action: callback))
71 | case let .tapCount(count):
72 | addGestureRecognizer(UITapGestureRecognizer(action: callback).then { $0.numberOfTapsRequired = count })
73 | case .longPress:
74 | addGestureRecognizer(UILongPressGestureRecognizer(action: callback))
75 | case .pan:
76 | addGestureRecognizer(UIPanGestureRecognizer(action: callback))
77 | case let .swipe(direction):
78 | addGestureRecognizer(UISwipeGestureRecognizer(action: callback).then { $0.direction = direction })
79 | }
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/Sources/Extensions/UIWindow+Extensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIWindow+Extensions.swift
3 | // BlobMenu
4 | //
5 | // Created by Igor K. on 23.04.2020.
6 | // Copyright © 2020 Ramotion. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 |
12 | extension UIWindow {
13 |
14 | static var current: UIWindow? {
15 | //if scene is not connected will use first normal level key window
16 | return sceneActiveWindow ?? keyWindow
17 | }
18 |
19 | static var keyWindow: UIWindow? {
20 | var window = UIApplication.shared.windows.first { $0.isKeyWindow }
21 | if window == nil || window?.windowLevel != .normal {
22 | for w in UIApplication.shared.windows {
23 | if w.windowLevel == .normal {
24 | window = w
25 | }
26 | }
27 | }
28 | return window
29 | }
30 |
31 | static var sceneActiveWindow: UIWindow? {
32 | let window = UIApplication.shared.connectedScenes
33 | .filter({ $0.activationState == .foregroundActive })
34 | .map({ $0 as? UIWindowScene })
35 | .compactMap({ $0 })
36 | .first?.windows
37 | .filter({ $0.isKeyWindow }).first
38 | return window
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Sources/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | $(CURRENT_PROJECT_VERSION)
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Sources/Models/BlobMenuItem.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BlobMenuItem.swift
3 | // BlobMenu
4 | //
5 | // Created by Igor K. on 29.04.2020.
6 | // Copyright © 2020 Ramotion. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SwiftUI
11 |
12 | public struct BlobMenuItem: Identifiable, Hashable {
13 |
14 | public let id = UUID()
15 | public let selectedIcon: Image
16 | public let unselectedIcon: Image
17 | public let offset: CGPoint
18 |
19 | public init(selectedIcon: Image,
20 | unselectedIcon: Image,
21 | offset: CGPoint = .zero) {
22 |
23 | self.selectedIcon = selectedIcon
24 | self.unselectedIcon = unselectedIcon
25 | self.offset = offset
26 | }
27 |
28 | public init(icon: Image, offset: CGPoint = .zero) {
29 | self.selectedIcon = icon
30 | self.unselectedIcon = icon
31 | self.offset = offset
32 | }
33 |
34 | public func hash(into hasher: inout Hasher) {
35 | hasher.combine(id)
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Sources/Models/BlobMenuModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BlobMenuMode.swift
3 | // BlobMenu
4 | //
5 | // Created by Igor K. on 22.05.2020.
6 | // Copyright © 2020 Ramotion. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SwiftUI
11 |
12 | public final class BlobMenuModel: ObservableObject {
13 | @Published public var items: [BlobMenuItem]
14 | @Published public internal(set) var isOpened: Bool
15 | @Published public internal(set) var isBlobMenuItemsVisible: Bool = false
16 | @Published public private(set) var selectedIndex: Int
17 |
18 | private let closeOnSelect: Bool
19 |
20 | public init(items: [BlobMenuItem],
21 | selectedIndex: Int = 0,
22 | isOpened: Bool = false,
23 | closeMenuOnSelect: Bool = false) {
24 |
25 | self.items = items
26 | self.isOpened = isOpened
27 | self.selectedIndex = selectedIndex.limited(0, items.count - 1)
28 | self.closeOnSelect = closeMenuOnSelect
29 | }
30 |
31 | public func selectIndex(_ index: Int) {
32 | let limitedIndex = index.limited(0, items.count - 1)
33 | selectedIndex = limitedIndex
34 | if closeOnSelect {
35 | delay(0.25) { self.closeMenu() }
36 | }
37 | }
38 |
39 | public func closeMenu() {
40 | isOpened = false
41 | isBlobMenuItemsVisible = false
42 | }
43 |
44 | public func openMenu() {
45 | withAnimation { self.isOpened = true }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/Sources/Utilities/AnimationCompletion.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BlobMenuBackgoundEffect.swift
3 | // BlobMenu
4 | //
5 | // Created by Igor K. on 21.04.2020.
6 | // Copyright © 2020 Ramotion. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SwiftUI
11 |
12 | private struct CompletionPreferenceKey: PreferenceKey {
13 | typealias Value = CGFloat
14 |
15 | static let defaultValue: CGFloat = 0
16 |
17 | static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
18 | value = nextValue()
19 | }
20 | }
21 |
22 |
23 | private struct CompletionModifier: AnimatableModifier {
24 | var progress: CGFloat = 0
25 |
26 | var animatableData: CGFloat {
27 | get { progress }
28 | set { progress = newValue }
29 | }
30 |
31 | func body(content: Content) -> some View {
32 | return content.preference(key: CompletionPreferenceKey.self, value: progress)
33 | }
34 | }
35 |
36 |
37 | private final class AnimationHolder {
38 | var animation: Animation?
39 | var disablesAnimations: Bool = false
40 | }
41 |
42 |
43 | extension View {
44 |
45 | func onAnimationCompleted(condition: Bool, completion: @escaping () -> Void) -> some View {
46 | let holder = AnimationHolder()
47 | var completed = false
48 | return self
49 | .transaction { transaction in
50 | // Restore the transaction values
51 | transaction.animation = holder.animation
52 | transaction.disablesAnimations = holder.disablesAnimations
53 | }
54 | .modifier(CompletionModifier(progress: condition ? 1 : 0))
55 | .transaction { transaction in
56 | // Store transaction value before modification
57 | holder.animation = transaction.animation
58 | holder.disablesAnimations = transaction.disablesAnimations
59 | }
60 | .onPreferenceChange(CompletionPreferenceKey.self) { progress in
61 | if completed == false && progress >= 1 {
62 | completed = true
63 | completion()
64 | }
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/Sources/Utilities/BezierUtilities.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BezierUtilities.swift
3 | // BlobMenu
4 | //
5 | // Created by Igor K. on 29.04.2020.
6 | // Copyright © 2020 Ramotion. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SwiftUI
11 |
12 | struct BezierUtilities {
13 | //
14 | // get Beizer curve points
15 | // p0 - start point
16 | // p1 - first control point
17 | // p2 - second control point
18 | // p3 - finish point
19 | //
20 | static func getCubeCurvePoints(p0: CGPoint, p1: CGPoint, p2: CGPoint, p3: CGPoint) -> [CGPoint] {
21 | //0 <= t >= 1
22 | // used t = 0.3...0.7 for fast calculation
23 | var points: [CGPoint] = []
24 | for t in stride(from: 0.3, to: 0.7, by: 0.1) {
25 | let x0 = pow((1-t), 3) * Double(p0.x)
26 | let x1 = 3 * t * pow((1-t), 2) * Double(p1.x)
27 | let x2 = 3 * pow(t, 2) * (1-t) * Double(p2.x)
28 | let x3 = pow(t, 3) * Double(p3.x)
29 | let X = x0 + x1 + x2 + x3
30 |
31 | let y0 = pow((1-t), 3) * Double(p0.y)
32 | let y1 = 3 * t * pow((1-t), 2) * Double(p1.y)
33 | let y2 = 3 * pow(t, 2) * (1-t) * Double(p2.y)
34 | let y3 = pow(t, 3) * Double(p3.y)
35 | let Y = y0 + y1 + y2 + y3
36 | points.append(CGPoint(x: X, y: Y))
37 | }
38 |
39 | return points
40 | }
41 |
42 | //
43 | // get Beizer curve Apex point (Middle of the line)
44 | // p0 - start point
45 | // p1 - first control point
46 | // p2 - second control point
47 | // p3 - finish point
48 | //
49 | static func getCubeCurveApexPoint(p0: CGPoint, p1: CGPoint, p2: CGPoint, p3: CGPoint) -> CGPoint {
50 | let t = 0.5 //Apex need only, 0 <= t >= 1
51 |
52 | let x0 = pow((1-t), 3) * Double(p0.x)
53 | let x1 = 3 * t * pow((1-t), 2) * Double(p1.x)
54 | let x2 = 3 * pow(t, 2) * (1-t) * Double(p2.x)
55 | let x3 = pow(t, 3) * Double(p3.x)
56 | let X = x0 + x1 + x2 + x3
57 |
58 | let y0 = pow((1-t), 3) * Double(p0.y)
59 | let y1 = 3 * t * pow((1-t), 2) * Double(p1.y)
60 | let y2 = 3 * pow(t, 2) * (1-t) * Double(p2.y)
61 | let y3 = pow(t, 3) * Double(p3.y)
62 | let Y = y0 + y1 + y2 + y3
63 |
64 | return CGPoint(x: X, y: Y)
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/Sources/Utilities/CommonUtilities.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CommonUtilities.swift
3 | // BlobMenu
4 | //
5 | // Created by Igor K. on 21.04.2020.
6 | // Copyright © 2020 Ramotion. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SwiftUI
11 |
12 | extension View {
13 | public func frame(size: CGSize, alignment: Alignment = .center) -> some View {
14 |
15 | return self.frame(width: size.width, height: size.height, alignment: alignment)
16 | }
17 |
18 | public func offset(_ offset: CGPoint) -> some View {
19 | return self.offset(x: offset.x, y: offset.y)
20 | }
21 |
22 | public var asAnyView: AnyView {
23 | return AnyView(self)
24 | }
25 | }
26 |
27 |
28 | extension Spacer {
29 |
30 | public func size(width: CGFloat? = nil, height: CGFloat? = nil) -> some View {
31 |
32 | guard width != nil || height != nil else {
33 | return AnyView(self)
34 | }
35 |
36 | return AnyView(self.frame(width: width, height: height))
37 | }
38 | }
39 |
40 |
41 | extension View {
42 | // If condition is met, apply modifier, otherwise, leave the view untouched
43 | public func conditionalModifier(_ condition: Bool, _ modifier: T) -> some View where T: ViewModifier {
44 | Group {
45 | if condition {
46 | self.modifier(modifier)
47 | } else {
48 | self
49 | }
50 | }
51 | }
52 |
53 | // Apply trueModifier if condition is met, or falseModifier if not.
54 | public func conditionalModifier(_ condition: Bool, _ trueModifier: M1, _ falseModifier: M2) -> some View where M1: ViewModifier, M2: ViewModifier {
55 | Group {
56 | if condition {
57 | self.modifier(trueModifier)
58 | } else {
59 | self.modifier(falseModifier)
60 | }
61 | }
62 | }
63 | }
64 |
65 | extension CGFloat {
66 | static let minAlowedScale: CGFloat = 0.001
67 | }
68 |
69 |
70 | func delay(_ delay: TimeInterval, _ closure: @escaping () -> Void) {
71 | DispatchQueue.main.asyncAfter(
72 | deadline: DispatchTime.now() + Double(Int64(delay * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC), execute: closure)
73 | }
74 |
--------------------------------------------------------------------------------
/Sources/Utilities/KayframesAnimation.swift:
--------------------------------------------------------------------------------
1 | //
2 | // KayframesAnimation.swift
3 | // BlobMenu
4 | //
5 | // Created by Igor K. on 26.04.2020.
6 | // Copyright © 2020 Ramotion. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SwiftUI
11 |
12 | /// One keyframe in the animation.
13 | public struct Keyframe: Equatable {
14 |
15 | /// The value of the keyfame.
16 | public let value: V
17 |
18 | /// The point in time where the `value` is reached, must be in range [0, 1].
19 | public let progress: Double
20 |
21 | /// Initialize with a value and progress.
22 | public init(value: V, progress: Double) {
23 | self.value = value
24 | self.progress = progress
25 | }
26 | }
27 |
28 | public struct KeyframeModifier: AnimatableModifier {
29 |
30 | /// The array of keyframes.
31 | public let keyframes: [Keyframe]
32 |
33 |
34 | /// The `AnimatableModifier`.
35 | public let modifier: M
36 |
37 |
38 | /// The current progress. Must be in range [0, 1].
39 | private var progress: Double
40 |
41 |
42 | public var animatableData: Double {
43 | get { progress }
44 | set { progress = newValue }
45 | }
46 |
47 | /// Initialize a new `KeyframeModifier`.
48 | /// - Parameter keyframes: An array of the keyframes. Does not have to be sorted.
49 | /// - Parameter modifier: The modifier used to apply the changes.
50 | /// - Parameter progress: The progress of the animation. Use a `@State` variable of value `0` and set to `1` to start animation.
51 | public init(keyframes: [Keyframe], modifier: M, progress: Double) {
52 | self.keyframes = keyframes.sorted(by: { $0.progress <= $1.progress })
53 | self.modifier = modifier
54 | self.progress = progress
55 | }
56 |
57 | /// Initialize a new `KeyframeModifier`.
58 | /// - Parameter keyframes: An array of values which will be used as equally distributed keyframes.
59 | /// - Parameter modifier: The modifier used to apply the changes.
60 | /// - Parameter progress: The progress of the animation. Use a `@State` variable of value `0` and set to `1` to start animation.
61 | public init(values: [M.AnimatableData], modifier: M, progress: Double) {
62 | let frames = values.enumerated()
63 | .map { Keyframe(value: $1, progress: 1 / Double(values.count - 1) * Double($0)) }
64 | self.init(keyframes: frames, modifier: modifier, progress: progress)
65 | }
66 |
67 | public func body(content: Content) -> some View {
68 | let progress = min(max(self.progress, 0), 1)
69 | let prev = keyframes.last(where: { $0.progress <= progress })!
70 | let next = keyframes.first(where: { $0.progress >= progress })!
71 | var m = modifier
72 |
73 | if prev != next {
74 | let factor = 1 / (next.progress - prev.progress)
75 | var val1 = next.value
76 | val1.scale(by: (progress - prev.progress) * factor)
77 |
78 | var val2 = prev.value
79 | val2.scale(by: (next.progress - progress) * factor)
80 |
81 | m.animatableData = val1 + val2
82 | } else {
83 | m.animatableData = prev.value
84 | }
85 | return content.modifier(m)
86 | }
87 | }
88 |
89 | extension View {
90 |
91 | /// Creates a keyframe animation. Use an `.animation` modifier to specify the animation.
92 | /// - Parameter keyframes: The array of keyframes. Does not have to be sorted.
93 | /// - Parameter progress: The progress of the animation. Use a `@State` variable of value `0` and set to `1` to start animation.
94 | /// - Parameter modifier: The modifier used to apply the changes.
95 | public func keyframes(_ keyframes: [Keyframe],
96 | progress: Double,
97 | modifier: M) -> some View {
98 | return self.modifier(KeyframeModifier(keyframes: keyframes, modifier: modifier, progress: progress))
99 | }
100 |
101 | /// Creates a keyframe animation by equally distributing the `values`. Use an `.animation` modifier to specify the animation.
102 | /// - Parameter values: The values of the animation.
103 | /// - Parameter progress: The progress of the animation. Use a `@State` variable of value `0` and set to `1` to start animation.
104 | /// - Parameter modifier: The modifier used to apply the changes.
105 | public func keyframes(_ values: [M.AnimatableData],
106 | progress: Double,
107 | modifier: M) -> some View {
108 | return self.modifier(KeyframeModifier(values: values, modifier: modifier, progress: progress))
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/Sources/Utilities/ScaleKeyframesAnimation.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ScaleKeyframesAnimation.swift
3 | // BlobMenu
4 | //
5 | // Created by Igor K. on 26.04.2020.
6 | // Copyright © 2020 Ramotion. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SwiftUI
11 |
12 | /// An example of a `AnimatableModifier` to animate the `.scale` of a `View ` in an keyframe animation.
13 | private struct ScaleModifier: AnimatableModifier {
14 | var animatableData: CGFloat = .zero
15 |
16 | func body(content: Content) -> some View {
17 | return content.scaleEffect(animatableData, anchor: .center)
18 | }
19 | }
20 |
21 | extension View {
22 |
23 | /// Create a keyframe animation of the scale of the view. Use an `.animation` modifier to specify the animation.
24 | /// - Parameter keyframes: The array of keyframes. Does not have to be sorted.
25 | /// - Parameter progress: The progress of the animation. Use a `@State` variable of value `0` and set to `1` to start animation.
26 | private func keyframes(scale keyframes: [Keyframe], progress: Double) -> some View {
27 | return self.keyframes(keyframes, progress: progress, modifier: ScaleModifier())
28 | }
29 |
30 | /// Create a keyframe animation of the scale of the view by equally distributing the `values`. Use an `.animation` modifier to specify the animation.
31 | /// - Parameter keyframes: The scale values of the animation.
32 | /// - Parameter progress: The progress of the animation. Use a `@State` variable of value `0` and set to `1` to start animation.
33 | public func keyframes(scale values: [CGFloat], progress: Double) -> some View {
34 | return self.keyframes(values, progress: progress, modifier: ScaleModifier())
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Sources/Utilities/SizeKeyframesAnimation.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FrameKeyframesAnimation.swift
3 | // BlobMenu
4 | //
5 | // Created by Igor K. on 26.04.2020.
6 | // Copyright © 2020 Ramotion. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SwiftUI
11 |
12 | /// An example of a `AnimatableModifier` to animate the `.scale` of a `View ` in an keyframe animation.
13 | private struct SizeModifier: AnimatableModifier {
14 | var animatableData: CGSize = .zero
15 |
16 | func body(content: Content) -> some View {
17 | return content.frame(size: animatableData)
18 | }
19 | }
20 |
21 |
22 | extension View {
23 |
24 | /// Create a keyframe animation of the size of the view. Use an `.animation` modifier to specify the animation.
25 | /// - Parameter keyframes: The array of keyframes. Does not have to be sorted.
26 | /// - Parameter progress: The progress of the animation. Use a `@State` variable of value `0` and set to `1` to start animation.
27 | public func keyframes(size keyframes: [Keyframe], progress: Double) -> some View {
28 | return self.keyframes(keyframes, progress: progress, modifier: SizeModifier())
29 | }
30 |
31 | /// Create a keyframe animation of the size of the view by equally distributing the `values`. Use an `.animation` modifier to specify the animation.
32 | /// - Parameter keyframes: The size values of the animation.
33 | /// - Parameter progress: The progress of the animation. Use a `@State` variable of value `0` and set to `1` to start animation.
34 | public func keyframes(size values: [CGSize], progress: Double) -> some View {
35 | return self.keyframes(values, progress: progress, modifier: SizeModifier())
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Sources/Utilities/Then.swift:
--------------------------------------------------------------------------------
1 | import CoreGraphics
2 | import Foundation
3 | #if os(iOS) || os(tvOS)
4 | import UIKit.UIGeometry
5 | #endif
6 |
7 | public protocol Then {}
8 |
9 | extension Then where Self: Any {
10 |
11 | /// Makes it available to set properties with closures just after initializing and copying the value types.
12 | ///
13 | /// let frame = CGRect().with {
14 | /// $0.origin.x = 10
15 | /// $0.size.width = 100
16 | /// }
17 | public func with(_ block: (inout Self) throws -> Void) rethrows -> Self {
18 | var copy = self
19 | try block(©)
20 | return copy
21 | }
22 |
23 | /// Makes it available to execute something with closures.
24 | ///
25 | /// UserDefaults.standard.do {
26 | /// $0.set("devxoul", forKey: "username")
27 | /// $0.set("devxoul@gmail.com", forKey: "email")
28 | /// $0.synchronize()
29 | /// }
30 | public func `do`(_ block: (Self) throws -> Void) rethrows {
31 | try block(self)
32 | }
33 | }
34 |
35 | extension Then where Self: AnyObject {
36 |
37 | /// Makes it available to set properties with closures just after initializing.
38 | ///
39 | /// let label = UILabel().then {
40 | /// $0.textAlignment = .Center
41 | /// $0.textColor = UIColor.blackColor()
42 | /// $0.text = "Hello, World!"
43 | /// }
44 | public func then(_ block: (Self) throws -> Void) rethrows -> Self {
45 | try block(self)
46 | return self
47 | }
48 | }
49 |
50 | extension NSObject: Then {}
51 |
52 | extension CGPoint: Then {}
53 | extension CGRect: Then {}
54 | extension CGSize: Then {}
55 | extension CGVector: Then {}
56 |
57 | #if os(iOS) || os(tvOS)
58 | extension UIEdgeInsets: Then {}
59 | extension UIOffset: Then {}
60 | extension UIRectEdge: Then {}
61 | #endif
62 |
--------------------------------------------------------------------------------
/Sources/Utilities/ViewSwapper.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewSwapper.swift
3 | // BlobMenu
4 | //
5 | // Created by Igor K. on 21.04.2020.
6 | // Copyright © 2020 Ramotion. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SwiftUI
11 |
12 | struct ViewSwapper: View {
13 | let first: T
14 | let second: M
15 | let isFirst: Bool
16 |
17 | var body: some View {
18 | Group {
19 | if isFirst {
20 | first
21 | } else {
22 | second
23 | }
24 | }
25 | }
26 | }
27 |
28 |
29 | extension View {
30 | @ViewBuilder func add(if condition: Bool, transform: (Self) -> T) -> some View where T : View {
31 |
32 | if condition {
33 | transform(self)
34 | } else {
35 | self
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Sources/Views/BackgroundView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BackgroundView.swift
3 | // BlobMenu
4 | //
5 | // Created by Igor K. on 27.04.2020.
6 | // Copyright © 2020 Ramotion. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SwiftUI
11 |
12 |
13 | struct BackgroundPreferenceData {
14 | let bounds: Anchor
15 | }
16 |
17 | struct BackgroundPreferenceKey: PreferenceKey {
18 | typealias Value = [BackgroundPreferenceData]
19 |
20 | static var defaultValue: [BackgroundPreferenceData] = []
21 |
22 | static func reduce(value: inout [BackgroundPreferenceData], nextValue: () -> [BackgroundPreferenceData]) {
23 | value.append(contentsOf: nextValue())
24 | }
25 | }
26 |
27 | struct BackgroundView: View {
28 |
29 | let color: Color
30 |
31 | var body: some View {
32 | return Rectangle()
33 | .fill(color)
34 | .anchorPreference(key: BackgroundPreferenceKey.self, value: .bounds, transform: { [BackgroundPreferenceData(bounds: $0)] })
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Sources/Views/BlobMenuView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BlobMenuView.swift
3 | // BlobMenu
4 | //
5 | // Created by Igor K. on 27.04.2020.
6 | // Copyright © 2020 Ramotion. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 |
11 | public struct BlobMenuView: View {
12 |
13 | private let configuration: BlobMenuConfiguration
14 | @ObservedObject private var viewModel: BlobMenuModel
15 | private let hapticFeedback: UIImpactFeedbackGenerator.FeedbackStyle?
16 |
17 | public init(model: BlobMenuModel,
18 | configuration: BlobMenuConfiguration = .default,
19 | hapticFeedback: UIImpactFeedbackGenerator.FeedbackStyle? = .light) {
20 |
21 | self.viewModel = model
22 | self.configuration = configuration
23 | self.hapticFeedback = hapticFeedback
24 |
25 | UIWindow.current?.addGesture(type: .tap) {[weak model] _ in
26 | model?.closeMenu()
27 | }
28 | }
29 |
30 | public var body: some View {
31 | ZStack(alignment: .center) {
32 | HStack {
33 | Spacer()
34 | self.background.overlay(self.itemsView)
35 | Spacer().size(width: self.viewModel.isOpened ? nil : Theme.padding)
36 | }
37 | .backgroundPreferenceValue(BackgroundPreferenceKey.self) { p in
38 | GeometryReader { geometry in
39 | self.createStickyView(geometry: geometry, preferences: p)
40 | .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
41 | }
42 | }
43 | HStack {
44 | Spacer()
45 | HamburgerView(isOpened: viewModel.isOpened, color: configuration.hamburgerColor)
46 | Spacer().size(width: Theme.padding)
47 | }
48 | }
49 | .frame(height: Theme.height)
50 | }
51 |
52 | private func createStickyView(geometry: GeometryProxy, preferences: [BackgroundPreferenceData]) -> some View {
53 |
54 | guard !self.viewModel.isBlobMenuItemsVisible else {
55 | return AnyView(Color.clear)
56 | }
57 |
58 | let p = preferences.first
59 | let f = geometry.frame(in: .local)
60 | let b = p != nil ? geometry[p!.bounds] : .zero
61 |
62 | let r = Theme.closedSize.height / 2
63 | let w = f.width - b.maxX + r
64 | let base = CGRect(x: f.width, y: f.minY, width: 50, height: f.height)
65 |
66 | let effectView = StickyEffectShape(baseRect: base, figureRect: b, figureCornerRadius: r, avulsionDistance: Theme.stickyEffectAvulsionDistance)
67 |
68 | return effectView
69 | .fill(configuration.backgroundColor)
70 | .frame(size: CGSize(width: w, height: f.height))
71 | .asAnyView
72 | }
73 |
74 | private var background: some View {
75 | BackgroundView(color: configuration.backgroundColor)
76 | .cornerRadius(Theme.closedSize.height / 2)
77 | .keyframes(size: Theme.backgroundSizeKeyframes(isOpened: viewModel.isOpened, items: viewModel.items), progress: viewModel.isOpened ? 1 : 0)
78 | .animation(Animation.interpolatingSpring(mass: 1, stiffness: 170, damping: 15, initialVelocity: 1).delay(viewModel.isOpened ? 0.12 : 0))
79 | .onAnimationCompleted(condition: viewModel.isOpened) {
80 | self.viewModel.isBlobMenuItemsVisible = true
81 | }
82 | .onTapGesture {
83 | withAnimation {
84 | self.viewModel.isOpened = true
85 | }
86 | }
87 | .allowsHitTesting(!self.viewModel.isOpened)
88 | }
89 |
90 | private var itemsView: some View {
91 | Group {
92 | if Theme.isScrollable(items: viewModel.items) {
93 | ScrollView(.horizontal, showsIndicators: false) { itemsContent }
94 | } else {
95 | itemsContent
96 | }
97 | }
98 | .clipShape(Capsule(style: .circular))
99 | .animation(Animation.easeInOut(duration: 0.35).delay(0.15))
100 | .allowsHitTesting(self.viewModel.isBlobMenuItemsVisible)
101 | }
102 |
103 | private var itemsContent: some View {
104 | HStack(spacing: 20) {
105 | ForEach(viewModel.items.enumeratedArray(), id: \.element) { index, item in
106 | BlobMenuItemView(item: item,
107 | isSelected: self.viewModel.selectedIndex == index,
108 | isOpened: self.viewModel.isBlobMenuItemsVisible,
109 | selectionColor: self.configuration.selectionColor)
110 | .onTapGesture {
111 | guard self.viewModel.selectedIndex != index else { return }
112 |
113 | if let style = self.hapticFeedback {
114 | let generator = UIImpactFeedbackGenerator(style: style)
115 | generator.prepare()
116 | generator.impactOccurred()
117 | }
118 |
119 | self.viewModel.selectIndex(index)
120 | }
121 | }
122 | }
123 | }
124 | }
125 |
126 |
127 | //MARK: - Theme
128 | extension BlobMenuView {
129 | enum Theme {
130 | static let height: CGFloat = 60
131 | static let padding: CGFloat = 10
132 | static let BlobMenuItemsSpace: CGFloat = 20
133 | static let stickyEffectAvulsionDistance: CGFloat = 120
134 | static let closedSize = CGSize(width: 60, height: height)
135 | static let collapsedSize = CGSize(width: 90, height: 70)
136 | static let maxOpenSize = CGSize(width: UIScreen.main.bounds.width - 60, height: height)
137 |
138 | private static func itemsSize(items: [BlobMenuItem]) -> CGSize {
139 | let w = CGFloat(items.count) * BlobMenuItemView.Theme.size.width + CGFloat(items.count - 1) * BlobMenuItemsSpace
140 | return CGSize(width: w, height: height)
141 | }
142 |
143 | static func openedSize(items: [BlobMenuItem]) -> CGSize {
144 | if isScrollable(items: items) {
145 | return maxOpenSize
146 | } else {
147 | return itemsSize(items: items)
148 | }
149 | }
150 |
151 | static func isScrollable(items: [BlobMenuItem]) -> Bool {
152 | return itemsSize(items: items).width > maxOpenSize.width
153 | }
154 |
155 | static func backgroundSizeKeyframes(isOpened: Bool, items: [BlobMenuItem]) -> [Keyframe] {
156 | if isOpened {
157 | //will use during opening animation
158 | return [
159 | Keyframe(value: closedSize, progress: 0),
160 | Keyframe(value: openedSize(items: items), progress: 1)
161 | ]
162 | } else {
163 | //will use during closing animation
164 | return [
165 | Keyframe(value: closedSize, progress: 0),
166 | Keyframe(value: collapsedSize, progress: 0.3),
167 | Keyframe(value: openedSize(items: items), progress: 1)
168 | ]
169 | }
170 | }
171 | }
172 | }
173 |
--------------------------------------------------------------------------------
/Sources/Views/HamburgerView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HamburgerView.swift
3 | // BlobMenu
4 | //
5 | // Created by Igor K. on 29.04.2020.
6 | // Copyright © 2020 Ramotion. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 |
11 | struct HamburgerView: View {
12 |
13 | let isOpened: Bool
14 | let color: Color
15 |
16 | private var rotationAngle: Angle {
17 | return Angle(degrees: isOpened ? -90 : 0)
18 | }
19 |
20 | var body: some View {
21 | VStack(alignment: .center, spacing: Theme.lineSpacing) {
22 | line
23 | line
24 | line
25 | }
26 | .frame(size: BlobMenuView.Theme.closedSize)
27 | .opacity(isOpened ? 0 : 1)
28 | .rotationEffect(rotationAngle, anchor: UnitPoint.center)
29 | .animation(Animation.easeInOut(duration: 0.25).delay( isOpened ? 0 : 0.2))
30 | }
31 |
32 | private var line: some View {
33 | Rectangle()
34 | .frame(width: Theme.lineWidth, height: Theme.lineThickness)
35 | .foregroundColor(color)
36 | .cornerRadius(Theme.lineCornerRadius)
37 | }
38 | }
39 |
40 |
41 | //MARK: - Preview
42 | struct HamburgerView_Previews: PreviewProvider {
43 | static var previews: some View {
44 | return HamburgerView(isOpened: false, color: .hamburgerColor)
45 | }
46 | }
47 |
48 |
49 | //MARK: - Theme
50 | extension HamburgerView {
51 | enum Theme {
52 | static let lineSpacing: CGFloat = 6
53 | static let lineThickness: CGFloat = 2
54 | static let lineCornerRadius: CGFloat = 1
55 | static let lineWidth: CGFloat = 24
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/Sources/Views/MenuItemView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BlobMenuItemView.swift
3 | // BlobMenu
4 | //
5 | // Created by Igor K. on 29.04.2020.
6 | // Copyright © 2020 Ramotion. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SwiftUI
11 |
12 | struct BlobMenuItemView: View {
13 |
14 | let item: BlobMenuItem
15 | let isSelected: Bool
16 | let isOpened: Bool
17 | let selectionColor: Color
18 |
19 | public var body: some View {
20 | ZStack {
21 | selectionView
22 | ringView
23 | if isOpened { iconView }
24 | }
25 | .padding(Theme.contentInsets)
26 | .contentShape(Rectangle())
27 | .frame(size: Theme.size)
28 | }
29 |
30 | private var iconView: some View {
31 | let image = isSelected ? item.selectedIcon : item.unselectedIcon
32 | return image
33 | .offset(item.offset)
34 | .transition(AnyTransition.blobBlobMenuItem)
35 | }
36 |
37 | private var selectionView: some View {
38 | Circle()
39 | .foregroundColor(selectionColor)
40 | .frame(size: Theme.contentSize)
41 | .opacity(isSelected ? 1 : 0)
42 | .animation(nil)
43 | .scaleEffect(isOpened ? (isSelected ? 1 : 0.8) : CGFloat.minAlowedScale)
44 | .animation(.interpolatingSpring(mass: 1, stiffness: 170, damping: isOpened ? 8 : 50, initialVelocity: 1))
45 | }
46 |
47 | private var ringView: some View {
48 | let show = isOpened && isSelected
49 | return Circle()
50 | .stroke(selectionColor)
51 | .frame(size: Theme.contentSize)
52 | .opacity(show ? 0 : 1)
53 | .animation(show ? Animation.easeInOut.delay(0.2) : nil)
54 | .scaleEffect(show ? 1.3 : CGFloat.minAlowedScale)
55 | .animation(show ? .easeInOut : nil)
56 | }
57 | }
58 |
59 |
60 | //MARK: - Theme
61 | extension BlobMenuItemView {
62 | enum Theme {
63 | static let size = CGSize(uniform: 60)
64 | static let contentInsets: CGFloat = 15
65 | static let contentSize = CGSize(uniform: size.height - contentInsets)
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/Sources/Views/StickyEffectView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StickyEffectView.swift
3 | // BlobMenu
4 | //
5 | // Created by Igor K. on 27.04.2020.
6 | // Copyright © 2020 Ramotion. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SwiftUI
11 |
12 | struct StickyEffectShape: Shape {
13 |
14 | private let pathGenerator = StickyPathGenerator.default
15 | private let baseRect: CGRect
16 | private let figureRect: CGRect
17 | private let figureCornerRadius: CGFloat
18 | private let avulsionDistance: CGFloat
19 |
20 | init(baseRect: CGRect,
21 | figureRect: CGRect,
22 | figureCornerRadius: CGFloat,
23 | avulsionDistance: CGFloat) {
24 |
25 | self.baseRect = baseRect
26 | self.figureRect = figureRect
27 | self.figureCornerRadius = figureCornerRadius
28 | self.avulsionDistance = avulsionDistance
29 | }
30 |
31 | func path(in rect: CGRect) -> Path {
32 |
33 | let path = pathGenerator.generatePath(baseRect: baseRect,
34 | figureRect: figureRect,
35 | figureCornerRadius: figureCornerRadius,
36 | avulsionDistance: avulsionDistance)
37 |
38 | return Path(path)
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Sources/Views/StickyPathGenerator.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StickyPathGenerator.swift
3 | // BlobMenu
4 | //
5 | // Created by Igor K. on 29.04.2020.
6 | // Copyright © 2020 Ramotion. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SwiftUI
11 |
12 | final class StickyPathGenerator {
13 |
14 | static let `default` = StickyPathGenerator()
15 |
16 | private enum Direction {
17 | case up
18 | case left
19 | case right
20 | case down
21 | }
22 |
23 | private struct Input {
24 | let baseRect: CGRect
25 | let figureRect: CGRect
26 | let figureCornerRadius: CGFloat
27 | let avulsionDistance: CGFloat
28 |
29 | /// Calculated constants
30 | let figurePointA: CGPoint
31 | let figurePointB: CGPoint
32 | let basePointA: CGPoint
33 | let basePointB: CGPoint
34 | let initialBasePointA: CGPoint
35 | let initialBasePointB: CGPoint
36 |
37 | /// Distance between figure and base
38 | let distance: CGFloat
39 |
40 | init(baseRect: CGRect,
41 | figureRect: CGRect,
42 | figureCornerRadius: CGFloat,
43 | avulsionDistance: CGFloat) {
44 |
45 |
46 | self.baseRect = baseRect
47 | self.figureRect = figureRect
48 | self.figureCornerRadius = figureCornerRadius
49 | self.avulsionDistance = avulsionDistance
50 |
51 | figurePointA = CGPoint(x: figureRect.maxX, y: figureRect.minY)
52 | figurePointB = CGPoint(x: figureRect.maxX, y: figureRect.maxY)
53 | basePointA = CGPoint(x: baseRect.minX, y: max(baseRect.minY, figureRect.minY))
54 | basePointB = CGPoint(x: baseRect.minX, y: min(baseRect.maxY, figureRect.maxY))
55 | initialBasePointA = basePointA
56 | initialBasePointB = basePointB
57 | distance = baseRect.minX - figureRect.maxX
58 | }
59 | }
60 |
61 | ///Bool flag to control shape constriction
62 | private var stopConstriction: Bool = false
63 |
64 | private var apexControlUp: CGPoint = CGPoint.zero
65 | private var apexControlDown: CGPoint = CGPoint.zero
66 |
67 | private var storedBasePointA: CGPoint?
68 | private var storedBasePointB: CGPoint?
69 |
70 | func generatePath(baseRect: CGRect,
71 | figureRect: CGRect,
72 | figureCornerRadius: CGFloat,
73 | avulsionDistance: CGFloat) -> CGPath {
74 |
75 | let input = Input(baseRect: baseRect, figureRect: figureRect, figureCornerRadius: figureCornerRadius, avulsionDistance: avulsionDistance)
76 |
77 | return generateShapePath(input: input).cgPath
78 | }
79 |
80 |
81 | /// Calculate Base Line Points for shape
82 | /// - used movement on Xaxis
83 | /// - return (point0, point3)
84 | private func calculateBaseLinePoints(input: Input) -> (CGPoint, CGPoint) {
85 | let distance = max(0, input.distance)
86 | let pY = { (from: CGFloat, to: CGFloat, lineSegment: CGFloat) -> CGFloat in
87 | //calculate movement coeficient from start to finish while distance <= avulsion
88 | let coef = abs(to - from) / lineSegment
89 | let p = from < to ? from + coef * distance : from - coef * distance
90 | return p
91 | }
92 |
93 | let movement = (input.figurePointA.y - input.initialBasePointA.y)
94 | let cornerRadius = input.figureCornerRadius
95 |
96 | //point0
97 | let startY0 = max(input.baseRect.minY, input.initialBasePointA.y - cornerRadius + movement)
98 | let finishY0 = max(input.baseRect.minY, input.initialBasePointA.y + cornerRadius + movement)
99 | let point0 = CGPoint(x: input.initialBasePointA.x,
100 | y: pY(startY0, finishY0, input.avulsionDistance))
101 |
102 | //point3
103 | let startY3 = min(input.baseRect.maxY, input.initialBasePointB.y + cornerRadius + movement)
104 | let finishY3 = min(input.baseRect.maxY, input.initialBasePointB.y - cornerRadius + movement)
105 | let point3 = CGPoint(x: input.initialBasePointB.x,
106 | y: pY(startY3, finishY3, input.avulsionDistance))
107 |
108 | //set limit for movement
109 | let limitDistanceBetwenPoints = input.figureRect.height - cornerRadius * 2
110 | if stopConstriction || point3.y - point0.y < limitDistanceBetwenPoints,
111 | let pA = storedBasePointA, let pB = storedBasePointB {
112 | //in that case use stored values
113 | return (pA, pB)
114 | }
115 |
116 | return (point0, point3)
117 | }
118 |
119 | /// Calculate points of intercsection betwen two circles
120 | /// The solution is to find tangents by calculation of intersection of two circles
121 | /// https://www.mathsisfun.com/geometry/construct-circletangent.html
122 | /// Intersection of two circles
123 | /// Discussion http://stackoverflow.com/questions/3349125/circle-circle-intersection-points
124 | /// Description http://paulbourke.net/geometry/circlesphere/
125 |
126 | private func findIntersection(centerCircle1 c1: CGPoint, radiusCircle1 c1r: CGFloat, centerCircle2 c2: CGPoint) -> (CGPoint, CGPoint) {
127 |
128 | //Calculate distance between centres of circle
129 | let d = (c1 - c2).length
130 | let c2r = d //in our case
131 | let m = c1r + c2r
132 | var n = c1r - c2r
133 |
134 | if (n < 0) {
135 | n = n * -1
136 | }
137 |
138 | //No solns
139 | if (d > m) {
140 | return (CGPoint.zero, CGPoint.zero)
141 | }
142 | //Circle are contained within each other
143 | if (d < n) {
144 | return (CGPoint.zero, CGPoint.zero)
145 | }
146 | //Circles are the same
147 | if (d == 0 && c1r == c2r) {
148 | return (CGPoint.zero, CGPoint.zero)
149 | }
150 |
151 | let a = (c1r * c1r - c2r * c2r + d * d) / (2 * d)
152 |
153 | let h = sqrt(c1r * c1r - a * a)
154 |
155 | //Calculate point p, where the line through the circle intersection points crosses the line between the circle centers.
156 |
157 | var x = c1.x + (a / d) * (c2.x - c1.x)
158 | var y = c1.y + (a / d) * (c2.y - c1.y)
159 | let p = CGPoint(x: x, y: y)
160 |
161 | //1 Intersection , circles are touching
162 | if (d == c1r + c2r) {
163 | return (p, CGPoint.zero)
164 | }
165 |
166 | //2 Intersections
167 | //Intersection 1
168 | x = p.x + (h / d) * (c2.y - c1.y)
169 | y = p.y - (h / d) * (c2.x - c1.x)
170 | let p1 = CGPoint(x: x, y: y)
171 |
172 | //Intersection 2
173 | x = p.x - (h / d) * (c2.y - c1.y)
174 | y = p.y + (h / d) * (c2.x - c1.x)
175 | let p2 = CGPoint(x: x, y: y)
176 |
177 | return (p1, p2)
178 | }
179 |
180 |
181 | /// Calculate left top point of shape
182 | /// - used method of intersection betwen two circles
183 | /// - first circle - centerfigCircle
184 | /// - second circle - circle from controlPoint
185 | private func calculateShapePoint1(input: Input, cp:CGPoint, point0:CGPoint) -> CGPoint {
186 | let cornerRadius = input.figureCornerRadius
187 | var centerfigCircle = CGPoint(x: input.figurePointA.x - cornerRadius,
188 | y: input.figurePointA.y + cornerRadius)
189 | centerfigCircle.x = point0.x - centerfigCircle.x < cornerRadius ? point0.x - cornerRadius : centerfigCircle.x
190 | centerfigCircle.x = max(centerfigCircle.x, input.figurePointA.x + cornerRadius - input.figureRect.width)
191 | let x3 = (centerfigCircle.x + cp.x) / 2
192 | let y3 = (centerfigCircle.y + cp.y) / 2
193 | let c1 = centerfigCircle
194 | let c2 = CGPoint(x: x3, y: y3)
195 | let c1r = cornerRadius
196 | var (p1, _) = findIntersection(centerCircle1: c1, radiusCircle1: c1r, centerCircle2: c2)
197 |
198 | //when something wrong in findIntersection pass top center point
199 | if p1 == CGPoint.zero {
200 | p1 = CGPoint(x: centerfigCircle.x, y: centerfigCircle.y - cornerRadius)
201 | }
202 |
203 | return p1
204 | }
205 |
206 | /// Calculate left bottom point of shape
207 | /// - used method of intersection betwen two circles
208 | /// - first circle - centerfigCircle
209 | /// - second circle - circle from controlPoint
210 | private func calculateShapePoint2(input: Input, cp:CGPoint, point3:CGPoint) -> CGPoint {
211 | let cornerRadius = input.figureCornerRadius
212 | var circleCenter = CGPoint(x: input.figurePointB.x - cornerRadius,
213 | y: input.figurePointB.y - cornerRadius)
214 | circleCenter.x = point3.x - circleCenter.x < cornerRadius ? point3.x - cornerRadius : circleCenter.x
215 | circleCenter.x = max(circleCenter.x, input.figurePointA.x + cornerRadius - input.figureRect.width)
216 | let x3 = (circleCenter.x + cp.x) / 2
217 | let y3 = (circleCenter.y + cp.y) / 2
218 | let c1 = circleCenter
219 | let c2 = CGPoint(x: x3, y: y3)
220 | let c1r = cornerRadius
221 | var (_, p2) = findIntersection(centerCircle1: c1, radiusCircle1: c1r, centerCircle2: c2)
222 |
223 | if p2 == CGPoint.zero {
224 | p2 = CGPoint(x: circleCenter.x, y: circleCenter.y - cornerRadius)
225 | }
226 |
227 | return p2
228 | }
229 |
230 |
231 | /// Calculate 2 control points for Bezier Path
232 | /// - concavity depends of distance
233 | /// - used movement for concavity effect
234 | private func calculateControlPoints(input: Input, basePoint: CGPoint, direction: Direction) -> (CGPoint, CGPoint) {
235 |
236 | let k: CGFloat = (input.figureRect.width > input.figureRect.height) ? 1.05 : 1.3
237 | var y: CGFloat = 0
238 |
239 | if direction == .down {
240 | let controlPointСoncavity = input.distance + input.distance * 0.05
241 | let deltaY = abs(basePoint.y - input.figurePointA.y) / k
242 | y = basePoint.y + controlPointСoncavity
243 | y = max(basePoint.y + deltaY, y)
244 |
245 | } else if direction == .up {
246 | let controlPointСoncavity = max(0, input.distance + input.distance * 0.05)
247 | let deltaY = abs(basePoint.y - input.figurePointB.y) / k
248 | y = basePoint.y - controlPointСoncavity
249 | y = min(basePoint.y - deltaY, y)
250 | }
251 |
252 | let cp1 = CGPoint(x: basePoint.x, y: y)
253 | let cp2 = CGPoint(x: min(basePoint.x, input.figurePointA.x + input.distance / 4), y: cp1.y)
254 |
255 | return (cp1, cp2)
256 | }
257 |
258 | //MARK: - Generate main path
259 | /*
260 | vertical scheme:
261 | point1 |------------------------------| point2
262 | | |
263 | | -cpUp2 cpDown2- |
264 | | |
265 | | -cpUp1 cpDown1- |
266 | point0 |______________________________| point3
267 |
268 |
269 | horizontal scheme:
270 | point1 _____________________ point0
271 | | cpTop2 cpTop1 |
272 | | |
273 | | |
274 | | |
275 | | cpBottom2 cpBottom1 |
276 | point2 |_____________________| point3
277 |
278 | */
279 |
280 | /// Generate shape path
281 | /// - calculate baseLinePoints (point0, point3)
282 | /// - calculate control points
283 | /// - calculate topLinePoints (point1, point2)
284 | /// - constrinction control
285 | /// - draw the shape by points
286 | private func generateShapePath(input: Input) -> UIBezierPath {
287 | let distance = max(0, input.distance)
288 |
289 | //calculate base points
290 | let (point0, point3) = calculateBaseLinePoints(input: input)
291 |
292 | //calculete control points
293 | var (cpUp1, cpUp2) = calculateControlPoints(input: input, basePoint: point0, direction: .down)
294 | var (cpDown1, cpDown2) = calculateControlPoints(input: input, basePoint: point3, direction: .up)
295 | var point1 = calculateShapePoint1(input: input, cp: cpUp2, point0: point0)
296 | var point2 = calculateShapePoint2(input: input, cp: cpDown2, point3: point3)
297 |
298 | //constriction control
299 | let upPoints = BezierUtilities.getCubeCurvePoints(p0: point0, p1: cpUp1, p2: cpUp2, p3: point1)
300 | let downPoints = BezierUtilities.getCubeCurvePoints(p0: point2, p1: cpDown2, p2: cpDown1, p3: point3)
301 |
302 | //search for curve cross
303 | var crossed = false
304 | for up in upPoints {
305 | for dp in downPoints {
306 | let delta = dp.y - up.y
307 | if delta <= 1 {
308 | crossed = true
309 | break
310 | }
311 | }
312 | }
313 |
314 | self.stopConstriction = crossed
315 |
316 | //if true - we'll use old values
317 | if crossed {
318 | cpUp2.y = apexControlUp.y
319 | cpUp1.y = apexControlUp.y
320 |
321 | cpDown2.y = apexControlDown.y
322 | cpDown1.y = apexControlDown.y
323 |
324 | point1 = calculateShapePoint1(input: input, cp: cpUp2, point0: point0)
325 | point2 = calculateShapePoint2(input: input, cp: cpDown2, point3: point3)
326 | } else {
327 | storedBasePointA = point0
328 | storedBasePointB = point3
329 | apexControlUp = cpUp2
330 | apexControlDown = cpDown2
331 | }
332 |
333 |
334 | //Do not need to draw shape under baseLine or after avulsion
335 | if distance <= -input.figureRect.width || distance > input.avulsionDistance {
336 | stopConstriction = false
337 |
338 | point1 = CGPoint(x: point0.x, y: point0.y)
339 | point2 = CGPoint(x: point0.x, y: point0.y + input.figureRect.height)
340 | cpUp1 = point0
341 | cpUp2 = point0
342 | cpDown1 = point3
343 | cpDown2 = point3
344 | }
345 |
346 | let curvePath = UIBezierPath()
347 | curvePath.move(to: point0)
348 | curvePath.addCurve(to: point1, controlPoint1: cpUp1, controlPoint2: cpUp2)
349 | if crossed {
350 | curvePath.addLine(to: point2)
351 | } else {
352 | let center = input.figureRect.center
353 | let dc = CGPoint(x: center.x - 0.5, y: center.y) // to fix small gap between menu and arc
354 | let start = atan((point1.y - center.y) / (point1.x - center.x))
355 | let end = atan((point2.y - center.y) / (point2.x - center.x))
356 | curvePath.addArc(withCenter: dc, radius: input.figureCornerRadius, startAngle: start, endAngle: end, clockwise: true)
357 | curvePath.addLine(to: point2)
358 | }
359 | curvePath.addCurve(to: point3, controlPoint1: cpDown2, controlPoint2: cpDown1)
360 |
361 | return curvePath
362 | }
363 | }
364 |
365 |
--------------------------------------------------------------------------------
/blob-menu.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 | s.name = 'blob-menu'
3 | s.version = '1.0.0'
4 | s.summary = 'SwiftUI menu control.'
5 | s.homepage = 'https://github.com/Ramotion/garland-view'
6 | s.license = 'MIT'
7 | s.authors = { 'Igor Kolpachkov' => 'igor.k@ramotion.agency' }
8 | s.ios.deployment_target = '13.4'
9 | s.source = { :git => 'https://github.com/Ramotion/blob-menu.git', :tag => s.version.to_s }
10 | s.source_files = 'Sources/*.swift'
11 | end
12 |
--------------------------------------------------------------------------------