├── .gitignore
├── README.md
├── SUIChallenges
├── SUIChallenges.xcodeproj
│ ├── project.pbxproj
│ └── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
└── SUIChallenges
│ ├── 1.Lamp.swift
│ ├── 10.FastFoodLogo.swift
│ ├── 2.Spotify.swift
│ ├── 3.AirDrop.swift
│ ├── 4.Firework.swift
│ ├── 5.FlexCards.swift
│ ├── 6.Hamburgers.swift
│ ├── 7.WavedTabView.swift
│ ├── 8.SneakersShop.swift
│ ├── 9.DynamicIsland.swift
│ ├── Assets.xcassets
│ ├── AccentColor.colorset
│ │ └── Contents.json
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ ├── Contents.json
│ ├── Sakhalin
│ │ ├── 1.imageset
│ │ │ ├── 1.jpg
│ │ │ └── Contents.json
│ │ ├── 2.imageset
│ │ │ ├── 2.jpg
│ │ │ └── Contents.json
│ │ ├── 3.imageset
│ │ │ ├── 3.jpg
│ │ │ └── Contents.json
│ │ ├── 4.imageset
│ │ │ ├── 4.jpg
│ │ │ └── Contents.json
│ │ ├── 5.imageset
│ │ │ ├── 5.jpg
│ │ │ └── Contents.json
│ │ ├── 6.imageset
│ │ │ ├── 6.jpg
│ │ │ └── Contents.json
│ │ └── Contents.json
│ ├── Sneakers
│ │ ├── Contents.json
│ │ ├── hoka_1.imageset
│ │ │ ├── Contents.json
│ │ │ └── hoka_1.png
│ │ ├── hoka_2.imageset
│ │ │ ├── Contents.json
│ │ │ └── hoka_2.png
│ │ └── hoka_3.imageset
│ │ │ ├── Contents.json
│ │ │ └── hoka_3.png
│ ├── SwiftLogo.imageset
│ │ ├── Contents.json
│ │ └── SwiftLogo.jpg
│ ├── like-you.imageset
│ │ ├── Contents.json
│ │ └── like-you.png
│ └── me.imageset
│ │ ├── Contents.json
│ │ └── IMG_0726.jpg
│ ├── Preview Content
│ └── Preview Assets.xcassets
│ │ └── Contents.json
│ └── SUIChallengesApp.swift
└── demos
├── Airdrop.gif
├── AppAds.gif
├── ChristmasEve.gif
├── DynamicIsland.gif
├── FastFoodCommercial.gif
├── Fireworks.gif
├── FlexCards.gif
├── Hamburgers.gif
├── InsideWavedTabBar.gif
├── Loader.gif
├── NonWavedTabBar.gif
├── SneakersShop.gif
├── Spotify.gif
├── UpsideWavedTabBar.gif
├── VerticalWatches.gif
├── animatable.gif
├── imageSlider.gif
├── kavsoft.gif
├── lamp.gif
└── panerai.gif
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## User settings
6 | xcuserdata/
7 |
8 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
9 | *.xcscmblueprint
10 | *.xccheckout
11 |
12 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
13 | build/
14 | DerivedData/
15 | *.moved-aside
16 | *.pbxuser
17 | !default.pbxuser
18 | *.mode1v3
19 | !default.mode1v3
20 | *.mode2v3
21 | !default.mode2v3
22 | *.perspectivev3
23 | !default.perspectivev3
24 |
25 | ## Obj-C/Swift specific
26 | *.hmap
27 |
28 | ## App packaging
29 | *.ipa
30 | *.dSYM.zip
31 | *.dSYM
32 |
33 | ## Playgrounds
34 | timeline.xctimeline
35 | playground.xcworkspace
36 |
37 | # Swift Package Manager
38 | #
39 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
40 | # Packages/
41 | # Package.pins
42 | # Package.resolved
43 | # *.xcodeproj
44 | #
45 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
46 | # hence it is not needed unless you have added a package configuration file to your project
47 | # .swiftpm
48 |
49 | .build/
50 |
51 | # CocoaPods
52 | #
53 | # We recommend against adding the Pods directory to your .gitignore. However
54 | # you should judge for yourself, the pros and cons are mentioned at:
55 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
56 | #
57 | # Pods/
58 | #
59 | # Add this line if you want to avoid checking in source code from the Xcode workspace
60 | # *.xcworkspace
61 |
62 | # Carthage
63 | #
64 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
65 | # Carthage/Checkouts
66 |
67 | Carthage/Build/
68 |
69 | # Accio dependency management
70 | Dependencies/
71 | .accio/
72 |
73 | # fastlane
74 | #
75 | # It is recommended to not store the screenshots in the git repo.
76 | # Instead, use fastlane to re-generate the screenshots whenever they are needed.
77 | # For more information about the recommended setup visit:
78 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
79 |
80 | fastlane/report.xml
81 | fastlane/Preview.html
82 | fastlane/screenshots/**/*.png
83 | fastlane/test_output
84 |
85 | # Code Injection
86 | #
87 | # After new code Injection tools there's a generated folder /iOSInjectionProject
88 | # https://github.com/johnno1962/injectionforxcode
89 |
90 | iOSInjectionProject/
91 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # SwiftUI challenges
2 |
3 | [](https://t.me/lexkraev)
4 | [](https://telegram.dog/swiftui_dev)
5 |
6 | ## Welcome 🤙🏻✌🏻🖖🏻
7 |
8 | This project contains helpful and useful examples of animations developed with SwiftUI.
9 |
10 | You may find some inspiration 💡 for your current or next project or you may take challenge to make it yourself 💪🏻
11 |
12 | Demo videos and gifs you may find in every event.
13 |
14 | 👨🏻💻 Feel free to subscribe to channel **[SwiftUI dev](https://t.me/swiftui_dev)** in telegram.
15 |
16 | ## Communication
17 |
18 | - If you **have a feature request or the any ideas**, open an issue or submit a implementation via a pull request or hit me up on **lexkraev@gmail.com** or **[telegram](https://t.me/lexkraev)**.
19 | - If you **want to contribute**, submit a pull request onto the master branch.
20 |
21 |
22 |
23 | ## 1. [Lamp 💡](https://github.com/c-villain/SwiftUI-challenges/blob/main/SUIChallenges/SUIChallenges/1.Lamp.swift)
24 |
25 | [Lamp challenge full video demo](https://t.me/swiftui_dev/184)
26 |
27 |
28 |
29 |
30 |
31 | ## 2. [Spotify 📊](https://github.com/c-villain/SwiftUI-challenges/blob/main/SUIChallenges/SUIChallenges/2.Spotify.swift)
32 |
33 | [Spotify challenge full video demo](https://t.me/swiftui_dev/185)
34 |
35 |
36 |
37 |
38 |
39 | ## 3. [AirDrop 📱](https://github.com/c-villain/SwiftUI-challenges/blob/main/SUIChallenges/SUIChallenges/3.AirDrop.swift)
40 |
41 | [Airdrop challenge full video demo](https://t.me/swiftui_dev/186)
42 |
43 |
44 |
45 |
46 |
47 | ## 4. [Fireworks 🎉](https://github.com/c-villain/SwiftUI-challenges/blob/main/SUIChallenges/SUIChallenges/4.Firework.swift)
48 |
49 | [Fireworks full video demo](https://t.me/swiftui_dev/187)
50 |
51 |
52 |
53 |
54 |
55 | ## 5. [Flex cards 🌆🌅🌄](https://github.com/c-villain/SwiftUI-challenges/blob/main/SUIChallenges/SUIChallenges/5.FlexCards.swift)
56 |
57 | [Flex cards full video demo](https://t.me/swiftui_dev/191)
58 |
59 |
60 |
61 |
62 |
63 | ## 6. [Hamburgers 🍔](https://github.com/c-villain/SwiftUI-challenges/blob/main/SUIChallenges/SUIChallenges/6.Hamburgers.swift)
64 |
65 | [Hamburgers full video demo](https://t.me/swiftui_dev/193)
66 |
67 |
68 |
69 |
70 |
71 | ## 7. [Waved tab bar 🌊](https://github.com/c-villain/SwiftUI-challenges/blob/main/SUIChallenges/SUIChallenges/7.WavedTabView.swift)
72 |
73 | [Waved tab bar full video demo](https://t.me/swiftui_dev/194)
74 |
75 | [Separate demo project](https://github.com/c-villain/WavedTabView)
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 | ## 8. [Sneakers shop concept 👟](https://github.com/c-villain/SwiftUI-challenges/blob/main/SUIChallenges/SUIChallenges/8.SneakersShop.swift)
86 |
87 | [Sneakers shop concept full video demo](https://t.me/swiftui_dev/195)
88 |
89 |
90 |
91 |
92 |
93 | ## 9. [Dynamic Island 🏝](https://github.com/c-villain/SwiftUI-challenges/blob/main/SUIChallenges/SUIChallenges/9.DynamicIsland.swift)
94 |
95 | [Dynamic Island full video demo](https://t.me/swiftui_dev/196)
96 |
97 |
98 |
99 |
100 |
101 | ## 10. [Fast food cafe animation 🍟](https://github.com/c-villain/SwiftUI-challenges/blob/main/SUIChallenges/SUIChallenges/10.FastFoodLogo.swift)
102 |
103 | [Fast food cafe animation full video demo](https://t.me/swiftui_dev/198)
104 |
105 |
106 |
107 |
108 |
109 | ## 10. [Panerai watches ⌚️](https://boosty.to/lexkraev/posts/becd1a9a-7402-45e3-821c-8fb0ae51d42f)
110 |
111 | [Panerai watches full video demo](https://t.me/swiftui_dev/200)
112 |
113 |
114 |
115 |
116 |
117 | ## 10. [Loader 📶](https://boosty.to/lexkraev/posts/7ed82e08-0c72-4e77-99e1-61b05ef3449b)
118 |
119 | [Loader full video demo](https://t.me/swiftui_dev/211)
120 |
121 |
122 |
123 |
124 |
125 | ## 11. [Vertical watches 📶⌚️](https://boosty.to/lexkraev/posts/a2b9780a-d039-4feb-960f-0d94ff4096d8)
126 |
127 | [Full video demo](https://t.me/swiftui_dev/212)
128 |
129 |
130 |
131 |
132 |
133 | ## 12. [Kavsoft challenge 🦸🏼♂️](https://boosty.to/lexkraev/posts/f40ac449-f7d9-4c77-804e-5c22a9b3a9b1)
134 |
135 | [Full video demo](https://t.me/swiftui_dev/217)
136 |
137 |
138 |
139 |
140 |
141 | ## 13. [Button reactions 🤸🏻♂️](https://github.com/c-villain/Animatable)
142 |
143 | [Full video demo](https://t.me/swiftui_dev/222)
144 |
145 | [Separate demo project](https://github.com/c-villain/Animatable)
146 |
147 |
148 |
149 |
150 |
151 | ## 14. [App ads from AppStore 🥷](https://boosty.to/lexkraev/posts/81270528-df44-4283-a6ed-99380b02bfef)
152 |
153 | [Full video demo](https://t.me/swiftui_dev/229)
154 |
155 |
156 |
157 |
158 |
159 | ## 15. [Image slider with page view controller effect 🧞♂️](https://boosty.to/lexkraev/posts/4a15893d-7e68-48d6-9a73-e170f3036c22)
160 |
161 | [Full video demo](https://t.me/swiftui_dev/245)
162 |
163 |
164 |
165 |
166 |
--------------------------------------------------------------------------------
/SUIChallenges/SUIChallenges.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 55;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 5A0E183D28E043BF00BD4CE1 /* 5.FlexCards.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A0E183C28E043BF00BD4CE1 /* 5.FlexCards.swift */; };
11 | 5A3CFBCB28B55EE700E1D5D7 /* 4.Firework.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A3CFBCA28B55EE700E1D5D7 /* 4.Firework.swift */; };
12 | 5A576EB228E3788600F15B57 /* 6.Hamburgers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A576EB128E3788600F15B57 /* 6.Hamburgers.swift */; };
13 | 5A62639D28E8D81C00D0B9DF /* 7.WavedTabView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A62639C28E8D81C00D0B9DF /* 7.WavedTabView.swift */; };
14 | 5A72661B28A04EDD0073EBAC /* SUIChallengesApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A72661A28A04EDD0073EBAC /* SUIChallengesApp.swift */; };
15 | 5A72661F28A04EDF0073EBAC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5A72661E28A04EDF0073EBAC /* Assets.xcassets */; };
16 | 5A72662228A04EDF0073EBAC /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5A72662128A04EDF0073EBAC /* Preview Assets.xcassets */; };
17 | 5A72662A28A04F6F0073EBAC /* 1.Lamp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A72662928A04F6F0073EBAC /* 1.Lamp.swift */; };
18 | 5A72662C28A04FA00073EBAC /* 2.Spotify.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A72662B28A04FA00073EBAC /* 2.Spotify.swift */; };
19 | 5A72662E28A04FC60073EBAC /* 3.AirDrop.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A72662D28A04FC60073EBAC /* 3.AirDrop.swift */; };
20 | 5A7475362900BB2A0037DD08 /* 9.DynamicIsland.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A7475352900BB2A0037DD08 /* 9.DynamicIsland.swift */; };
21 | 5ABB122C290D90AB007AC0F8 /* 10.FastFoodLogo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5ABB122B290D90AB007AC0F8 /* 10.FastFoodLogo.swift */; };
22 | 5ACAD44828F9D49B00DE6156 /* 8.SneakersShop.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5ACAD44728F9D49B00DE6156 /* 8.SneakersShop.swift */; };
23 | /* End PBXBuildFile section */
24 |
25 | /* Begin PBXFileReference section */
26 | 5A0E183C28E043BF00BD4CE1 /* 5.FlexCards.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = 5.FlexCards.swift; sourceTree = ""; };
27 | 5A3CFBCA28B55EE700E1D5D7 /* 4.Firework.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = 4.Firework.swift; sourceTree = ""; };
28 | 5A576EB128E3788600F15B57 /* 6.Hamburgers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = 6.Hamburgers.swift; sourceTree = ""; };
29 | 5A62639C28E8D81C00D0B9DF /* 7.WavedTabView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = 7.WavedTabView.swift; sourceTree = ""; };
30 | 5A72661728A04EDD0073EBAC /* SUIChallenges.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SUIChallenges.app; sourceTree = BUILT_PRODUCTS_DIR; };
31 | 5A72661A28A04EDD0073EBAC /* SUIChallengesApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SUIChallengesApp.swift; sourceTree = ""; };
32 | 5A72661E28A04EDF0073EBAC /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
33 | 5A72662128A04EDF0073EBAC /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; };
34 | 5A72662928A04F6F0073EBAC /* 1.Lamp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = 1.Lamp.swift; sourceTree = ""; };
35 | 5A72662B28A04FA00073EBAC /* 2.Spotify.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = 2.Spotify.swift; sourceTree = ""; };
36 | 5A72662D28A04FC60073EBAC /* 3.AirDrop.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = 3.AirDrop.swift; sourceTree = ""; };
37 | 5A7475352900BB2A0037DD08 /* 9.DynamicIsland.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = 9.DynamicIsland.swift; sourceTree = ""; };
38 | 5ABB122B290D90AB007AC0F8 /* 10.FastFoodLogo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = 10.FastFoodLogo.swift; sourceTree = ""; };
39 | 5ACAD44728F9D49B00DE6156 /* 8.SneakersShop.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = 8.SneakersShop.swift; sourceTree = ""; };
40 | /* End PBXFileReference section */
41 |
42 | /* Begin PBXFrameworksBuildPhase section */
43 | 5A72661428A04EDD0073EBAC /* Frameworks */ = {
44 | isa = PBXFrameworksBuildPhase;
45 | buildActionMask = 2147483647;
46 | files = (
47 | );
48 | runOnlyForDeploymentPostprocessing = 0;
49 | };
50 | /* End PBXFrameworksBuildPhase section */
51 |
52 | /* Begin PBXGroup section */
53 | 5A72660E28A04EDD0073EBAC = {
54 | isa = PBXGroup;
55 | children = (
56 | 5A72661928A04EDD0073EBAC /* SUIChallenges */,
57 | 5A72661828A04EDD0073EBAC /* Products */,
58 | );
59 | sourceTree = "";
60 | };
61 | 5A72661828A04EDD0073EBAC /* Products */ = {
62 | isa = PBXGroup;
63 | children = (
64 | 5A72661728A04EDD0073EBAC /* SUIChallenges.app */,
65 | );
66 | name = Products;
67 | sourceTree = "";
68 | };
69 | 5A72661928A04EDD0073EBAC /* SUIChallenges */ = {
70 | isa = PBXGroup;
71 | children = (
72 | 5A72661A28A04EDD0073EBAC /* SUIChallengesApp.swift */,
73 | 5A72661E28A04EDF0073EBAC /* Assets.xcassets */,
74 | 5A72662028A04EDF0073EBAC /* Preview Content */,
75 | 5A72662928A04F6F0073EBAC /* 1.Lamp.swift */,
76 | 5A72662B28A04FA00073EBAC /* 2.Spotify.swift */,
77 | 5A72662D28A04FC60073EBAC /* 3.AirDrop.swift */,
78 | 5A3CFBCA28B55EE700E1D5D7 /* 4.Firework.swift */,
79 | 5A0E183C28E043BF00BD4CE1 /* 5.FlexCards.swift */,
80 | 5A576EB128E3788600F15B57 /* 6.Hamburgers.swift */,
81 | 5A62639C28E8D81C00D0B9DF /* 7.WavedTabView.swift */,
82 | 5ACAD44728F9D49B00DE6156 /* 8.SneakersShop.swift */,
83 | 5A7475352900BB2A0037DD08 /* 9.DynamicIsland.swift */,
84 | 5ABB122B290D90AB007AC0F8 /* 10.FastFoodLogo.swift */,
85 | );
86 | path = SUIChallenges;
87 | sourceTree = "";
88 | };
89 | 5A72662028A04EDF0073EBAC /* Preview Content */ = {
90 | isa = PBXGroup;
91 | children = (
92 | 5A72662128A04EDF0073EBAC /* Preview Assets.xcassets */,
93 | );
94 | path = "Preview Content";
95 | sourceTree = "";
96 | };
97 | /* End PBXGroup section */
98 |
99 | /* Begin PBXNativeTarget section */
100 | 5A72661628A04EDD0073EBAC /* SUIChallenges */ = {
101 | isa = PBXNativeTarget;
102 | buildConfigurationList = 5A72662528A04EDF0073EBAC /* Build configuration list for PBXNativeTarget "SUIChallenges" */;
103 | buildPhases = (
104 | 5A72661328A04EDD0073EBAC /* Sources */,
105 | 5A72661428A04EDD0073EBAC /* Frameworks */,
106 | 5A72661528A04EDD0073EBAC /* Resources */,
107 | );
108 | buildRules = (
109 | );
110 | dependencies = (
111 | );
112 | name = SUIChallenges;
113 | productName = SUIChallenges;
114 | productReference = 5A72661728A04EDD0073EBAC /* SUIChallenges.app */;
115 | productType = "com.apple.product-type.application";
116 | };
117 | /* End PBXNativeTarget section */
118 |
119 | /* Begin PBXProject section */
120 | 5A72660F28A04EDD0073EBAC /* Project object */ = {
121 | isa = PBXProject;
122 | attributes = {
123 | BuildIndependentTargetsInParallel = 1;
124 | LastSwiftUpdateCheck = 1340;
125 | LastUpgradeCheck = 1340;
126 | TargetAttributes = {
127 | 5A72661628A04EDD0073EBAC = {
128 | CreatedOnToolsVersion = 13.4.1;
129 | };
130 | };
131 | };
132 | buildConfigurationList = 5A72661228A04EDD0073EBAC /* Build configuration list for PBXProject "SUIChallenges" */;
133 | compatibilityVersion = "Xcode 13.0";
134 | developmentRegion = en;
135 | hasScannedForEncodings = 0;
136 | knownRegions = (
137 | en,
138 | Base,
139 | );
140 | mainGroup = 5A72660E28A04EDD0073EBAC;
141 | productRefGroup = 5A72661828A04EDD0073EBAC /* Products */;
142 | projectDirPath = "";
143 | projectRoot = "";
144 | targets = (
145 | 5A72661628A04EDD0073EBAC /* SUIChallenges */,
146 | );
147 | };
148 | /* End PBXProject section */
149 |
150 | /* Begin PBXResourcesBuildPhase section */
151 | 5A72661528A04EDD0073EBAC /* Resources */ = {
152 | isa = PBXResourcesBuildPhase;
153 | buildActionMask = 2147483647;
154 | files = (
155 | 5A72662228A04EDF0073EBAC /* Preview Assets.xcassets in Resources */,
156 | 5A72661F28A04EDF0073EBAC /* Assets.xcassets in Resources */,
157 | );
158 | runOnlyForDeploymentPostprocessing = 0;
159 | };
160 | /* End PBXResourcesBuildPhase section */
161 |
162 | /* Begin PBXSourcesBuildPhase section */
163 | 5A72661328A04EDD0073EBAC /* Sources */ = {
164 | isa = PBXSourcesBuildPhase;
165 | buildActionMask = 2147483647;
166 | files = (
167 | 5A72662E28A04FC60073EBAC /* 3.AirDrop.swift in Sources */,
168 | 5A576EB228E3788600F15B57 /* 6.Hamburgers.swift in Sources */,
169 | 5A72662A28A04F6F0073EBAC /* 1.Lamp.swift in Sources */,
170 | 5A72661B28A04EDD0073EBAC /* SUIChallengesApp.swift in Sources */,
171 | 5ABB122C290D90AB007AC0F8 /* 10.FastFoodLogo.swift in Sources */,
172 | 5A0E183D28E043BF00BD4CE1 /* 5.FlexCards.swift in Sources */,
173 | 5ACAD44828F9D49B00DE6156 /* 8.SneakersShop.swift in Sources */,
174 | 5A62639D28E8D81C00D0B9DF /* 7.WavedTabView.swift in Sources */,
175 | 5A7475362900BB2A0037DD08 /* 9.DynamicIsland.swift in Sources */,
176 | 5A72662C28A04FA00073EBAC /* 2.Spotify.swift in Sources */,
177 | 5A3CFBCB28B55EE700E1D5D7 /* 4.Firework.swift in Sources */,
178 | );
179 | runOnlyForDeploymentPostprocessing = 0;
180 | };
181 | /* End PBXSourcesBuildPhase section */
182 |
183 | /* Begin XCBuildConfiguration section */
184 | 5A72662328A04EDF0073EBAC /* Debug */ = {
185 | isa = XCBuildConfiguration;
186 | buildSettings = {
187 | ALWAYS_SEARCH_USER_PATHS = NO;
188 | CLANG_ANALYZER_NONNULL = YES;
189 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
190 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
191 | CLANG_ENABLE_MODULES = YES;
192 | CLANG_ENABLE_OBJC_ARC = YES;
193 | CLANG_ENABLE_OBJC_WEAK = YES;
194 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
195 | CLANG_WARN_BOOL_CONVERSION = YES;
196 | CLANG_WARN_COMMA = YES;
197 | CLANG_WARN_CONSTANT_CONVERSION = YES;
198 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
199 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
200 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
201 | CLANG_WARN_EMPTY_BODY = YES;
202 | CLANG_WARN_ENUM_CONVERSION = YES;
203 | CLANG_WARN_INFINITE_RECURSION = YES;
204 | CLANG_WARN_INT_CONVERSION = YES;
205 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
206 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
207 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
208 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
209 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
210 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
211 | CLANG_WARN_STRICT_PROTOTYPES = YES;
212 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
213 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
214 | CLANG_WARN_UNREACHABLE_CODE = YES;
215 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
216 | COPY_PHASE_STRIP = NO;
217 | DEBUG_INFORMATION_FORMAT = dwarf;
218 | ENABLE_STRICT_OBJC_MSGSEND = YES;
219 | ENABLE_TESTABILITY = YES;
220 | GCC_C_LANGUAGE_STANDARD = gnu11;
221 | GCC_DYNAMIC_NO_PIC = NO;
222 | GCC_NO_COMMON_BLOCKS = YES;
223 | GCC_OPTIMIZATION_LEVEL = 0;
224 | GCC_PREPROCESSOR_DEFINITIONS = (
225 | "DEBUG=1",
226 | "$(inherited)",
227 | );
228 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
229 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
230 | GCC_WARN_UNDECLARED_SELECTOR = YES;
231 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
232 | GCC_WARN_UNUSED_FUNCTION = YES;
233 | GCC_WARN_UNUSED_VARIABLE = YES;
234 | IPHONEOS_DEPLOYMENT_TARGET = 15.5;
235 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
236 | MTL_FAST_MATH = YES;
237 | ONLY_ACTIVE_ARCH = YES;
238 | SDKROOT = iphoneos;
239 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
240 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
241 | };
242 | name = Debug;
243 | };
244 | 5A72662428A04EDF0073EBAC /* Release */ = {
245 | isa = XCBuildConfiguration;
246 | buildSettings = {
247 | ALWAYS_SEARCH_USER_PATHS = NO;
248 | CLANG_ANALYZER_NONNULL = YES;
249 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
250 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
251 | CLANG_ENABLE_MODULES = YES;
252 | CLANG_ENABLE_OBJC_ARC = YES;
253 | CLANG_ENABLE_OBJC_WEAK = YES;
254 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
255 | CLANG_WARN_BOOL_CONVERSION = YES;
256 | CLANG_WARN_COMMA = YES;
257 | CLANG_WARN_CONSTANT_CONVERSION = YES;
258 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
259 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
260 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
261 | CLANG_WARN_EMPTY_BODY = YES;
262 | CLANG_WARN_ENUM_CONVERSION = YES;
263 | CLANG_WARN_INFINITE_RECURSION = YES;
264 | CLANG_WARN_INT_CONVERSION = YES;
265 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
266 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
267 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
268 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
269 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
270 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
271 | CLANG_WARN_STRICT_PROTOTYPES = YES;
272 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
273 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
274 | CLANG_WARN_UNREACHABLE_CODE = YES;
275 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
276 | COPY_PHASE_STRIP = NO;
277 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
278 | ENABLE_NS_ASSERTIONS = NO;
279 | ENABLE_STRICT_OBJC_MSGSEND = YES;
280 | GCC_C_LANGUAGE_STANDARD = gnu11;
281 | GCC_NO_COMMON_BLOCKS = YES;
282 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
283 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
284 | GCC_WARN_UNDECLARED_SELECTOR = YES;
285 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
286 | GCC_WARN_UNUSED_FUNCTION = YES;
287 | GCC_WARN_UNUSED_VARIABLE = YES;
288 | IPHONEOS_DEPLOYMENT_TARGET = 15.5;
289 | MTL_ENABLE_DEBUG_INFO = NO;
290 | MTL_FAST_MATH = YES;
291 | SDKROOT = iphoneos;
292 | SWIFT_COMPILATION_MODE = wholemodule;
293 | SWIFT_OPTIMIZATION_LEVEL = "-O";
294 | VALIDATE_PRODUCT = YES;
295 | };
296 | name = Release;
297 | };
298 | 5A72662628A04EDF0073EBAC /* Debug */ = {
299 | isa = XCBuildConfiguration;
300 | buildSettings = {
301 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
302 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
303 | CODE_SIGN_STYLE = Automatic;
304 | CURRENT_PROJECT_VERSION = 1;
305 | DEVELOPMENT_ASSET_PATHS = "\"SUIChallenges/Preview Content\"";
306 | DEVELOPMENT_TEAM = TP33793R45;
307 | ENABLE_PREVIEWS = YES;
308 | GENERATE_INFOPLIST_FILE = YES;
309 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
310 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
311 | INFOPLIST_KEY_UILaunchScreen_Generation = YES;
312 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
313 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
314 | IPHONEOS_DEPLOYMENT_TARGET = 15.0;
315 | LD_RUNPATH_SEARCH_PATHS = (
316 | "$(inherited)",
317 | "@executable_path/Frameworks",
318 | );
319 | MARKETING_VERSION = 1.0;
320 | PRODUCT_BUNDLE_IDENTIFIER = "com.c-villain.SUIChallenges";
321 | PRODUCT_NAME = "$(TARGET_NAME)";
322 | SWIFT_EMIT_LOC_STRINGS = YES;
323 | SWIFT_VERSION = 5.0;
324 | TARGETED_DEVICE_FAMILY = "1,2";
325 | };
326 | name = Debug;
327 | };
328 | 5A72662728A04EDF0073EBAC /* Release */ = {
329 | isa = XCBuildConfiguration;
330 | buildSettings = {
331 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
332 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
333 | CODE_SIGN_STYLE = Automatic;
334 | CURRENT_PROJECT_VERSION = 1;
335 | DEVELOPMENT_ASSET_PATHS = "\"SUIChallenges/Preview Content\"";
336 | DEVELOPMENT_TEAM = TP33793R45;
337 | ENABLE_PREVIEWS = YES;
338 | GENERATE_INFOPLIST_FILE = YES;
339 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
340 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
341 | INFOPLIST_KEY_UILaunchScreen_Generation = YES;
342 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
343 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
344 | IPHONEOS_DEPLOYMENT_TARGET = 15.0;
345 | LD_RUNPATH_SEARCH_PATHS = (
346 | "$(inherited)",
347 | "@executable_path/Frameworks",
348 | );
349 | MARKETING_VERSION = 1.0;
350 | PRODUCT_BUNDLE_IDENTIFIER = "com.c-villain.SUIChallenges";
351 | PRODUCT_NAME = "$(TARGET_NAME)";
352 | SWIFT_EMIT_LOC_STRINGS = YES;
353 | SWIFT_VERSION = 5.0;
354 | TARGETED_DEVICE_FAMILY = "1,2";
355 | };
356 | name = Release;
357 | };
358 | /* End XCBuildConfiguration section */
359 |
360 | /* Begin XCConfigurationList section */
361 | 5A72661228A04EDD0073EBAC /* Build configuration list for PBXProject "SUIChallenges" */ = {
362 | isa = XCConfigurationList;
363 | buildConfigurations = (
364 | 5A72662328A04EDF0073EBAC /* Debug */,
365 | 5A72662428A04EDF0073EBAC /* Release */,
366 | );
367 | defaultConfigurationIsVisible = 0;
368 | defaultConfigurationName = Release;
369 | };
370 | 5A72662528A04EDF0073EBAC /* Build configuration list for PBXNativeTarget "SUIChallenges" */ = {
371 | isa = XCConfigurationList;
372 | buildConfigurations = (
373 | 5A72662628A04EDF0073EBAC /* Debug */,
374 | 5A72662728A04EDF0073EBAC /* Release */,
375 | );
376 | defaultConfigurationIsVisible = 0;
377 | defaultConfigurationName = Release;
378 | };
379 | /* End XCConfigurationList section */
380 | };
381 | rootObject = 5A72660F28A04EDD0073EBAC /* Project object */;
382 | }
383 |
--------------------------------------------------------------------------------
/SUIChallenges/SUIChallenges.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/SUIChallenges/SUIChallenges.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/SUIChallenges/SUIChallenges/1.Lamp.swift:
--------------------------------------------------------------------------------
1 | //
2 | // 1.Lamp.swift
3 | // SUIChallenges
4 | //
5 | // Created by Alexander Kraev on 22.07.2022.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct Lamp: View {
11 |
12 | let text = "SWIFTUI IS JUST AMAZING!!! \nLorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nunc. \n\n Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit"
13 |
14 | static let initialOffset: CGSize = .init(width: 0, height: 70)
15 | @State private var lampOffset: CGSize = initialOffset
16 | @State private var lampColor: Color = Color.yellow
17 |
18 | @State private var lampOn: Bool = false
19 |
20 | var body: some View {
21 | ZStack {
22 | ScrollView {
23 | VStack (alignment: .leading) {
24 | Text(text)
25 | .fontWeight(.semibold)
26 | .foregroundColor(.gray)
27 | .font(Font.system(size:20))
28 | }
29 | .hLeading()
30 | .padding(.top, 130)
31 | .padding(.horizontal)
32 | }.zIndex(1)
33 |
34 | ColorPicker("", selection: $lampColor)
35 | .vTop()
36 | .hTrailing()
37 | .padding(20.0)
38 | .opacity(lampOn ? 0 : 1)
39 | .zIndex(2)
40 |
41 | ZStack(alignment: .top) {
42 |
43 | Rope(lampOffset: lampOffset)
44 | .stroke(Color(.lightGray),
45 | lineWidth: 4)
46 |
47 | Rectangle()
48 | .fill(lampOn ? lampColor : .clear)
49 | .frame(width: 150, height: 150)
50 | .offset(x: lampOffset.width, y: lampOffset.height)
51 | .blur(radius: 90)
52 |
53 | Image(systemName: "lightbulb.fill")
54 | .resizable()
55 | .frame(width: 30, height: 50)
56 | .foregroundColor(lampOn ? lampColor : Color(.lightGray) )
57 | .rotationEffect(.degrees(180))
58 | .offset(x: lampOffset.width, y: lampOffset.height)
59 | .overlay {
60 | Circle()
61 | .fill(.black)
62 | .frame(width: 13, height: 13)
63 | .offset(x: lampOffset.width, y: lampOffset.height)
64 | .blur(radius: 8)
65 | .opacity(lampOn ? 0 : 1)
66 | }
67 | .gesture(
68 | DragGesture().onChanged { value in
69 | lampOffset = .init(width: value.translation.width, height: value.translation.height + 70)
70 | } .onEnded({ value in
71 | lampOffset = Lamp
72 | .initialOffset
73 | })
74 | )
75 | .onTapGesture {
76 | withAnimation(.easeIn(duration: 0.6)) {
77 | lampOn.toggle()
78 | }
79 | }
80 | }
81 | .animation(.spring(response: 0.35,
82 | dampingFraction: 0.35,
83 | blendDuration: 0),
84 | value: lampOffset == Lamp.initialOffset )
85 | .edgesIgnoringSafeArea(.top)
86 | .zIndex(3)
87 | }
88 | }
89 | }
90 |
91 | struct Rope: Shape {
92 |
93 | var lampOffset: CGSize
94 |
95 | func path(in rect: CGRect) -> Path {
96 | Path { path in
97 | path.move(to: CGPoint(x: rect.midX, y: 0))
98 | path.addLine(to: .init(x: lampOffset.width + rect.midX, y: lampOffset.height))
99 | }
100 | }
101 |
102 | var animatableData: AnimatablePair {
103 | get { AnimatablePair(lampOffset.width, lampOffset.height) }
104 | set {
105 | lampOffset.width = newValue.first
106 | lampOffset.height = newValue.second
107 | }
108 | }
109 | }
110 |
111 | fileprivate extension View {
112 |
113 | // MARK: Vertical Center
114 | func vCenter() -> some View {
115 | self
116 | .frame(maxHeight: .infinity, alignment: .center)
117 | }
118 |
119 | // MARK: Vertical Top
120 | func vTop() -> some View {
121 | self
122 | .frame(maxHeight: .infinity, alignment: .top)
123 | }
124 |
125 | // MARK: Vertical Bottom
126 | func vBottom() -> some View {
127 | self
128 | .frame(maxHeight: .infinity, alignment: .bottom)
129 | }
130 |
131 | // MARK: Horizontal Center
132 | func hCenter() -> some View {
133 | self
134 | .frame(maxWidth: .infinity, alignment: .center)
135 | }
136 |
137 | // MARK: Horizontal Leading
138 | func hLeading() -> some View {
139 | self
140 | .frame(maxWidth: .infinity, alignment: .leading)
141 | }
142 |
143 | // MARK: Horizontal Trailing
144 | func hTrailing() -> some View {
145 | self
146 | .frame(maxWidth: .infinity, alignment: .trailing)
147 | }
148 | }
149 |
150 | struct Lamp_Previews: PreviewProvider {
151 | static var previews: some View {
152 | Lamp()
153 | .preferredColorScheme(.dark)
154 | }
155 | }
156 |
--------------------------------------------------------------------------------
/SUIChallenges/SUIChallenges/10.FastFoodLogo.swift:
--------------------------------------------------------------------------------
1 | //
2 | // 10.FastFoodLogo.swift
3 | // SUIChallenges
4 | //
5 | // Created by Alexander Kraev on 29.10.2022.
6 | //
7 |
8 | import SwiftUI
9 |
10 | enum Constants {
11 | enum Circle {
12 | enum Radius {
13 | static let scaled: CGFloat = 500
14 | static let basic: CGFloat = 25
15 | }
16 |
17 | enum Offset {
18 | static let offseted: CGFloat = 70
19 | static let basic: CGFloat = 0
20 | }
21 | }
22 | }
23 |
24 | struct FastFoodLogo: View {
25 |
26 | @State var changeMode = false
27 | @State var scaled = true
28 | @State var circleOffseted = true
29 | @State var rotate = false
30 | @State var stripesVisible = false
31 | @State var stripesOffseted = true
32 | @State var textRectVisible = false
33 | @State var textVisible = false
34 | @State var textOffseted = true
35 | @State var isFinished = true
36 |
37 | var body: some View {
38 | ZStack {
39 | Color(red: 38/255, green: 74/255, blue: 49/255).zIndex(0)
40 |
41 | ZStack(alignment: .center) {
42 | ZStack(alignment: .top) {
43 | Circle()
44 | .fill(.clear)
45 | .frame(width: 300)
46 | .zIndex(1)
47 |
48 | Circle()
49 | .fill(Color(red: 231/255, green: 65/255, blue: 15/255))
50 | .aspectRatio(contentMode: changeMode ? .fit : .fill)
51 | .edgesIgnoringSafeArea(.all)
52 | .frame(width: scaled ? Constants.Circle.Radius.scaled * 2 : Constants.Circle.Radius.basic * 2)
53 | .offset(y: circleOffseted ? Constants.Circle.Offset.basic : Constants.Circle.Offset.offseted )
54 | .zIndex(2)
55 | .onTapGesture {
56 | guard isFinished else { return }
57 | isFinished = false
58 | changeMode.toggle()
59 | withAnimation(.linear(duration: 0.5)) {
60 | scaled.toggle()
61 | }
62 | withAnimation(.linear(duration: 1.0).delay(0.5)) {
63 | rotate.toggle()
64 | }
65 | withAnimation(.spring(response: 0.6,
66 | dampingFraction: 0.4,
67 | blendDuration: 0).delay(1.5)) {
68 | circleOffseted.toggle()
69 | }
70 | withAnimation(.linear(duration: 0.2).delay(1.5)) {
71 |
72 | textRectVisible.toggle()
73 | }
74 | withAnimation(.linear(duration: 0.2).delay(1.7)) {
75 | stripesVisible.toggle()
76 | }
77 | withAnimation(.spring(response: 0.35,
78 | dampingFraction: 0.25,
79 | blendDuration: 0).delay(1.8)) {
80 | stripesOffseted.toggle()
81 | }
82 | withAnimation(.linear(duration: 0.2).delay(2.3)) {
83 | textVisible.toggle()
84 | }
85 | withAnimation(.linear(duration: 0.4).delay(2.4)) {
86 | textOffseted.toggle()
87 | }
88 | }
89 | }
90 | .rotationEffect(.degrees(rotate ? 270 : 0))
91 |
92 | HStack(spacing: 0) {
93 | Rectangle()
94 | .fill(Color(red: 238/255, green: 116/255, blue: 3/255))
95 | .clipShape(Stripe())
96 | .frame(width: 60, height: 120)
97 | .rotationEffect(.degrees(-30))
98 | .offset(x: stripesOffseted ? -12 : 0, y: -30)
99 |
100 | Rectangle()
101 | .fill(Color(red: 238/255, green: 116/255, blue: 3/255))
102 | .clipShape(Stripe())
103 | .frame(width: 60, height: 120)
104 | .rotationEffect(.degrees(-30))
105 | .offset(x: stripesOffseted ? -72 : -12, y: -30)
106 | }
107 | .opacity(stripesVisible ? 1 : 0)
108 |
109 | VStack(spacing: 0) {
110 | Text("ВКУСНО — И ТОЧКА")
111 | .kerning(0)
112 | .foregroundColor(.white)
113 | .fontWeight(.bold)
114 | .font(.title3)
115 | .offset(y: textOffseted ? 20 : 0)
116 | .opacity(textVisible ? 1 : 0)
117 | .onTapGesture {
118 | isFinished = true
119 | changeMode = false
120 | scaled = true
121 | circleOffseted = true
122 | rotate = false
123 | stripesVisible = false
124 | stripesOffseted = true
125 | textRectVisible = false
126 | textVisible = false
127 | textOffseted = true
128 | }
129 |
130 | Rectangle()
131 | .fill(Color(red: 38/255, green: 74/255, blue: 49/255))
132 | .frame(height: 60)
133 | }
134 | .fixedSize()
135 | .offset(y: 100)
136 | .opacity(textRectVisible ? 1 : 0)
137 | }
138 | .zIndex(1)
139 | }
140 | .edgesIgnoringSafeArea(.all)
141 | }
142 | }
143 |
144 | struct FastFoodLogo_Previews: PreviewProvider {
145 | static var previews: some View {
146 | FastFoodLogo()
147 | }
148 | }
149 |
150 | struct Stripe: Shape {
151 |
152 | func path(in rect: CGRect) -> Path {
153 | var path = Path()
154 | path.move(to: .init(x: rect.maxX, y: 0))
155 | path.addLine(to: .init(x: rect.maxX, y: rect.maxY - rect.width / 2))
156 | path.addQuadCurve(to: .init(x: rect.midX, y: rect.maxY), control: .init(x: rect.maxX, y: rect.maxY))
157 | path.addLine(to: .init(x: rect.width/2, y: rect.width / 2))
158 | path.addQuadCurve(to: .init(x: rect.maxX, y: 0), control: .init(x: rect.midX, y: 0))
159 | return path
160 | }
161 | }
162 |
163 | struct Logo: View {
164 | var body: some View {
165 | HStack(alignment: .bottom, spacing: 0) {
166 |
167 | Circle()
168 | .fill(Color(red: 231/255, green: 65/255, blue: 15/255))
169 | .frame(width: 50.0)
170 |
171 | Rectangle()
172 | .fill(Color(red: 238/255, green: 116/255, blue: 3/255))
173 | .clipShape(Stripe())
174 | .frame(width: 60, height: 120)
175 | .rotationEffect(.degrees(-30))
176 | .offset(x: -30.0, y: 5)
177 |
178 | Rectangle()
179 | .fill(Color(red: 238/255, green: 116/255, blue: 3/255))
180 | .clipShape(Stripe())
181 | .frame(width: 60, height: 120)
182 | .rotationEffect(.degrees(-30))
183 | .offset(x: -40.0, y: 5)
184 | }
185 | }
186 | }
187 |
--------------------------------------------------------------------------------
/SUIChallenges/SUIChallenges/2.Spotify.swift:
--------------------------------------------------------------------------------
1 | //
2 | // 2.Spotify.swift
3 | // SUIChallenges
4 | //
5 | // Created by Alexander Kraev on 04.08.2022.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct Spotify: View {
11 |
12 | @State private var color: Color = Color.green
13 | @State private var frequency: CGFloat = 0.5
14 | @State private var columns: Int = 3
15 |
16 | var body: some View {
17 | ZStack {
18 | VStack(spacing: 16.0) {
19 | ColorPicker("", selection: $color)
20 |
21 | HStack {
22 | Text("Frequency: \(String(format: "%.1f", frequency))")
23 | .font(.title3)
24 | Stepper("", onIncrement: {
25 | frequency += 0.1
26 | }, onDecrement: {
27 | if frequency > 0.2 {
28 | frequency -= 0.1
29 | }
30 | })
31 | }
32 |
33 | HStack {
34 | Text("Columns: \(columns)")
35 | .font(.title3)
36 | Stepper("", onIncrement: {
37 | columns += 1
38 | }, onDecrement: {
39 | if columns > 2 {
40 | columns -= 1
41 | }
42 | })
43 | }
44 | }
45 | .vTop()
46 | .hTrailing()
47 | .padding(20.0)
48 | .zIndex(1)
49 |
50 | HStack {
51 | ForEach(0...columns, id: \.self) { _ in
52 | Bar(color: color, interval: frequency)
53 | }
54 | }
55 | .frame(width: 170, height: 170)
56 |
57 | }
58 | }
59 | }
60 |
61 | fileprivate struct Bar: View {
62 |
63 | let color: Color
64 | let timer: Timer.TimerPublisher
65 |
66 | @State private var paddingTop: CGFloat = 1.0
67 |
68 | init(color: Color, interval: TimeInterval) {
69 | self.color = color
70 | timer = Timer.publish(every: interval, on: .main, in: .common)
71 | }
72 |
73 | var body: some View {
74 | Segment(color: color, paddingTop: paddingTop)
75 | .animation(.spring(), value: paddingTop)
76 | .onReceive(timer.autoconnect()) { _ in
77 | paddingTop = CGFloat.random(in: 0...1)
78 | }
79 | }
80 | }
81 |
82 | fileprivate struct Segment: Animatable, View {
83 |
84 | let color: Color
85 | var paddingTop: CGFloat = 1.0
86 |
87 | public var animatableData: CGFloat {
88 | get { paddingTop }
89 | set { paddingTop = newValue }
90 | }
91 |
92 | var body: some View {
93 | GeometryReader { geometry in
94 | RoundedRectangle(cornerRadius: 8.0)
95 | .fill(color)
96 | .padding(.top, geometry.size.height * paddingTop)
97 | }
98 | }
99 | }
100 |
101 | fileprivate extension View {
102 |
103 | // MARK: Vertical Center
104 | func vCenter() -> some View {
105 | self
106 | .frame(maxHeight: .infinity, alignment: .center)
107 | }
108 |
109 | // MARK: Vertical Top
110 | func vTop() -> some View {
111 | self
112 | .frame(maxHeight: .infinity, alignment: .top)
113 | }
114 |
115 | // MARK: Vertical Bottom
116 | func vBottom() -> some View {
117 | self
118 | .frame(maxHeight: .infinity, alignment: .bottom)
119 | }
120 |
121 | // MARK: Horizontal Center
122 | func hCenter() -> some View {
123 | self
124 | .frame(maxWidth: .infinity, alignment: .center)
125 | }
126 |
127 | // MARK: Horizontal Leading
128 | func hLeading() -> some View {
129 | self
130 | .frame(maxWidth: .infinity, alignment: .leading)
131 | }
132 |
133 | // MARK: Horizontal Trailing
134 | func hTrailing() -> some View {
135 | self
136 | .frame(maxWidth: .infinity, alignment: .trailing)
137 | }
138 | }
139 |
140 | struct Spotify_Previews: PreviewProvider {
141 | static var previews: some View {
142 | Spotify()
143 | .preferredColorScheme(.dark)
144 | }
145 | }
146 |
147 |
--------------------------------------------------------------------------------
/SUIChallenges/SUIChallenges/3.AirDrop.swift:
--------------------------------------------------------------------------------
1 | //
2 | // 3.AirDrop.swift
3 | // SUIChallenges
4 | //
5 | // Created by Alexander Kraev on 07.08.2022.
6 | //
7 |
8 | import SwiftUI
9 |
10 | @MainActor
11 | final class AirDropViewModel: ObservableObject {
12 | enum State {
13 | case `init`
14 | case waiting
15 | case sending
16 | case sent
17 | }
18 |
19 | @Published var state: State = .`init`
20 | @Published var progress: Double = 0
21 |
22 | func dropFile() async {
23 | do {
24 | progress = 0
25 | state = .waiting
26 | try await Task.sleep(nanoseconds: 3_500_000_000) // to simulate waiting
27 | state = .sending
28 | for _ in 0...9 {
29 | let time = Double.random(in: 0.1...0.5) * 1_000_000_000
30 | try await Task.sleep(nanoseconds: UInt64.init(time)) // to simulate sending
31 | progress += 0.1
32 | }
33 | try await Task.sleep(nanoseconds: 1_500_000_000)
34 | state = .sent
35 | } catch { }
36 | }
37 | }
38 |
39 | struct AirDrop: View {
40 |
41 | @StateObject private var vm: AirDropViewModel = .init()
42 |
43 | @State private var isProgressVisible = true
44 | @State private var blinking: Bool = false
45 | @State private var showShimmer: Bool = false
46 | @State private var id = UUID()
47 |
48 | var restart: some View {
49 | Button {
50 | vm.state = .`init`
51 | vm.progress = 0
52 | isProgressVisible = true
53 | blinking = false
54 | showShimmer = false
55 | id = UUID()
56 | } label: {
57 | Text("Restart")
58 | .font(.largeTitle)
59 | .opacity(vm.state == .sent ? 1.0 : 0)
60 | }
61 | }
62 |
63 | var photoSkeleton: some View {
64 | Circle()
65 | .fill(Color(.systemGray))
66 | .overlay {
67 | Person()
68 | .fill(Color(.systemGray6))
69 | }
70 | .frame(width: 190, height: 190)
71 | }
72 |
73 | var airDropButton: some View {
74 | VStack {
75 | ZStack {
76 | CircularProgressView(progress: vm.progress)
77 | .frame(width: 210, height: 210)
78 | .padding()
79 | .opacity(isProgressVisible ? 1.0 : 0.0)
80 | .animation(.linear, value: vm.progress)
81 |
82 | photoSkeleton
83 | .opacity(0.8)
84 |
85 | photoSkeleton
86 | .mask {
87 | Capsule()
88 | .fill(LinearGradient(gradient: .init(colors: [.clear, .white, .clear]), startPoint: .top, endPoint: .bottom))
89 | .rotationEffect(.degrees(-120))
90 | .offset(x: showShimmer ? 200 : -200)
91 | }
92 |
93 | }
94 | .clipShape(Circle())
95 | .onTapGesture {
96 | Task {
97 | await vm.dropFile()
98 | withAnimation(.easeIn(duration: 0.7)) {
99 | isProgressVisible.toggle()
100 | }
101 | }
102 | }
103 | .disabled(vm.state == .sent)
104 |
105 | Text("iPhone")
106 | .foregroundColor(.gray)
107 | .font(.title2)
108 |
109 | switch vm.state {
110 | case .`init`:
111 | Text("from Alex")
112 | .foregroundColor(.gray)
113 | .font(.title2)
114 | case .waiting:
115 | Text("Waiting...")
116 | .foregroundColor(Color(.systemGray3))
117 | .font(.title2)
118 | .opacity(blinking ? 0 : 1)
119 | .animation(.easeIn(duration: 0.6).repeatForever(), value: blinking)
120 | .onAppear {
121 | withAnimation {
122 | blinking = true
123 | }
124 | }
125 | case .sending:
126 | Text("Sending...")
127 | .foregroundColor(Color(.systemGray3))
128 | .font(.title2)
129 | case .sent:
130 | Text("Sent")
131 | .foregroundColor(.blue)
132 | .font(.title2)
133 | .onAppear {
134 | withAnimation(.linear.delay(1.5)) {
135 | showShimmer.toggle()
136 | }
137 | }
138 | }
139 | }
140 | }
141 |
142 | var body: some View {
143 | VStack(spacing: 40) {
144 | restart
145 | airDropButton
146 | .id(id)
147 | }
148 | }
149 |
150 | }
151 |
152 | struct CircularProgressView: View {
153 | var progress: Double
154 |
155 | var body: some View {
156 | ZStack {
157 | Circle()
158 | .stroke(Color(.systemGray3), lineWidth: 10)
159 | .zIndex(1)
160 | Circle()
161 | .trim(from: 0, to: CGFloat(self.progress))
162 | .stroke(
163 | Color.blue,
164 | style: StrokeStyle(lineWidth: 10, lineCap: .square))
165 | .zIndex(2)
166 | }
167 | .rotationEffect(Angle(degrees: -90))
168 | }
169 | }
170 |
171 | struct Person: Shape {
172 |
173 | func path(in rect: CGRect) -> Path {
174 | Path { path in
175 |
176 | // head:
177 | path.addEllipse(in: .init(x: rect.midX - rect.maxX / (3.5 * 2), y: rect.maxY / 5 , width: rect.maxX / 3.5, height: rect.maxY / 3))
178 |
179 | // body:
180 | path.move(to: .init(x: rect.maxX / 5, y: rect.maxY * 4 / 5))
181 |
182 | path.addQuadCurve(to: CGPoint(x: rect.maxX * 4 / 5, y: rect.maxY * 4 / 5),
183 | control: CGPoint(x: rect.midX, y: rect.midY))
184 |
185 | path.move(to: .init(x: rect.maxX * 4 / 5, y: rect.maxY * 4 / 5))
186 |
187 | path.addQuadCurve(to: CGPoint(x: rect.maxX / 5, y: rect.maxY * 4 / 5),
188 | control: CGPoint(x: rect.midX, y: rect.maxY))
189 |
190 | path.closeSubpath()
191 | }
192 | }
193 |
194 | }
195 |
196 | struct AirDrop_Previews: PreviewProvider {
197 | static var previews: some View {
198 | AirDrop()
199 | .preferredColorScheme(.dark)
200 | }
201 | }
202 |
--------------------------------------------------------------------------------
/SUIChallenges/SUIChallenges/4.Firework.swift:
--------------------------------------------------------------------------------
1 | //
2 | // 4.Firework.swift
3 | // SUIChallenges
4 | //
5 | // Created by Alexander Kraev on 23.08.2022.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct FireworkParticlesGeometryEffect : GeometryEffect {
11 | var time : Double
12 | var speed = Double.random(in: 20 ... 200)
13 | var direction = Double.random(in: -Double.pi ... Double.pi)
14 |
15 | var animatableData: Double {
16 | get { time }
17 | set { time = newValue }
18 | }
19 | func effectValue(size: CGSize) -> ProjectionTransform {
20 | let xTranslation = speed * cos(direction) * time
21 | let yTranslation = speed * sin(direction) * time
22 | let affineTranslation = CGAffineTransform(translationX: xTranslation, y: yTranslation)
23 | return ProjectionTransform(affineTranslation)
24 | }
25 | }
26 |
27 | struct ParticlesModifier: ViewModifier {
28 | @State var time = 0.0
29 | @State var scale = 0.1
30 | let duration = Double.random(in: 3.0 ... 7.0)
31 |
32 | func body(content: Content) -> some View {
33 | ZStack {
34 | ForEach(0..<80, id: \.self) { index in
35 | content
36 | .hueRotation(Angle(degrees: time * 80))
37 | .scaleEffect(scale)
38 | .modifier(FireworkParticlesGeometryEffect(time: time))
39 | .opacity((duration - time) / duration)
40 | .animation(.easeOut(duration: duration).repeatForever(autoreverses: false), value: scale)
41 | }
42 | }
43 | .onAppear {
44 | time = duration
45 | scale = 1.0
46 | }
47 | }
48 | }
49 |
50 | struct Fireworks: View {
51 |
52 | @State var scaling: Bool = false
53 |
54 | var body: some View {
55 | ZStack {
56 | VStack {
57 | VStack {
58 | Text("Thanks so much!")
59 | .font(.largeTitle)
60 | Text("200")
61 | .font(.system(size: 60))
62 | .scaleEffect(scaling ? 1.1 : 1.0)
63 | .animation(.easeOut(duration: 1.0).repeatForever(), value: scaling)
64 | Text("I really appreciate you!")
65 | .font(.largeTitle)
66 | }
67 | .padding()
68 | .vTop()
69 |
70 | }
71 | .zIndex(1)
72 |
73 | ForEach(0.. some View {
101 | self
102 | .frame(maxHeight: .infinity, alignment: .center)
103 | }
104 |
105 | // MARK: Vertical Top
106 | func vTop() -> some View {
107 | self
108 | .frame(maxHeight: .infinity, alignment: .top)
109 | }
110 |
111 | // MARK: Vertical Bottom
112 | func vBottom() -> some View {
113 | self
114 | .frame(maxHeight: .infinity, alignment: .bottom)
115 | }
116 |
117 | // MARK: Horizontal Center
118 | func hCenter() -> some View {
119 | self
120 | .frame(maxWidth: .infinity, alignment: .center)
121 | }
122 |
123 | // MARK: Horizontal Leading
124 | func hLeading() -> some View {
125 | self
126 | .frame(maxWidth: .infinity, alignment: .leading)
127 | }
128 |
129 | // MARK: Horizontal Trailing
130 | func hTrailing() -> some View {
131 | self
132 | .frame(maxWidth: .infinity, alignment: .trailing)
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/SUIChallenges/SUIChallenges/5.FlexCards.swift:
--------------------------------------------------------------------------------
1 | //
2 | // 5.FlexCards.swift
3 | // SUIChallenges
4 | //
5 | // Created by Alexander Kraev on 25.09.2022.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct Card: Identifiable, Hashable {
11 | var id: Int
12 | var imageName: String
13 | var place: String
14 | var description: String
15 | }
16 |
17 | let cards: [Card] = [.init(id: 1, imageName: "1", place: "Kurilsk", description: "Yetorup island"),
18 | .init(id: 2, imageName: "2", place: "Shallow seabed", description: "Yetorup island"),
19 | .init(id: 3, imageName: "3", place: "Tug Korund", description: "Yetorup island"),
20 | .init(id: 4, imageName: "4", place: "Lighthouse", description: "The Aniva cape"),
21 | .init(id: 5, imageName: "5", place: "Rocks", description: "Okhotsckoe sea"),
22 | .init(id: 6, imageName: "6", place: "Volcanic beach", description: "Yetorup island")]
23 |
24 | struct FlexCards: View {
25 |
26 | @State var isSelected = cards[1]
27 | @State var selectedSize: CGFloat = 0
28 | @State var size: CGFloat = 0
29 |
30 | var body: some View {
31 | GeometryReader { geometry in
32 | VStack(alignment: .leading) {
33 | Text("Sakhalin island, Russia")
34 | .fontWeight(.medium)
35 | .font(.largeTitle)
36 |
37 | ScrollView(.horizontal) {
38 | HStack(spacing: 4.0) {
39 | ForEach(cards) { card in
40 | CardView(image: card.imageName,
41 | place: card.place,
42 | description: card.description,
43 | buttonImage: isSelected == card ? "sun.min.fill" : "sun.min",
44 | size: .init(width: isSelected == card ? selectedSize : size, height: 220), isSelected: isSelected == card) {
45 | withAnimation(.easeIn(duration: 0.2)) {
46 | isSelected = card
47 | }
48 | }
49 | }
50 | }
51 | }
52 | .onAppear {
53 | let basicSize: CGFloat = geometry.size.width - 16.0 - (CGFloat)(8 * (cards.count - 1))
54 | selectedSize = basicSize / 2
55 | size = (basicSize * 0.5) / (CGFloat)(cards.count - 1)
56 | print("selectedSize: \(selectedSize) size: \(size)")
57 | }
58 | }
59 | }
60 | .padding(8)
61 | }
62 | }
63 |
64 | struct FlexCards_Previews: PreviewProvider {
65 | static var previews: some View {
66 | FlexCards()
67 | .previewInterfaceOrientation(.landscapeLeft)
68 | }
69 | }
70 |
71 | struct CardView: View {
72 |
73 | let image: String
74 | let place: String
75 | let description: String
76 | let buttonImage: String
77 |
78 | let size: CGSize
79 | let isSelected: Bool
80 | var onTap: (() -> ())?
81 |
82 | var body: some View {
83 | Image("\(image)")
84 | .resizable()
85 | .scaledToFill()
86 | .frame(width: size.width, height: size.height)
87 | .cornerRadius(8)
88 | .clipped()
89 | .contentShape(Rectangle())
90 | .overlay(alignment: .bottomLeading) {
91 | HStack(spacing: 8.0) {
92 | Image(systemName: buttonImage)
93 | .foregroundColor(.white)
94 | .frame(width: 24.0, height: 24.0)
95 | if isSelected {
96 | VStack(alignment: .leading) {
97 | Text(place)
98 | .font(.headline)
99 | .fontWeight(.medium)
100 | .foregroundColor(.white)
101 | Text(description)
102 | .font(.caption2)
103 | .foregroundColor(.white)
104 | }
105 | .fixedSize()
106 | }
107 | }
108 | .frame(height: 40)
109 | .padding(4.0)
110 | }
111 | .onTapGesture {
112 | onTap?()
113 | }
114 | }
115 | }
116 |
117 | struct CardView_Previews: PreviewProvider {
118 | static var previews: some View {
119 | CardView(image: "1",
120 | place: "Kurilsk",
121 | description: "Yetorup island",
122 | buttonImage: "sun.min.fill",
123 | size: .init(width: 159.0, height: 220.0),
124 | isSelected: true)
125 | .previewInterfaceOrientation(.landscapeLeft)
126 | }
127 | }
128 |
--------------------------------------------------------------------------------
/SUIChallenges/SUIChallenges/6.Hamburgers.swift:
--------------------------------------------------------------------------------
1 | //
2 | // 6.Hamburgers.swift
3 | // SUIChallenges
4 | //
5 | // Created by Alexander Kraev on 27.09.2022.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct Hamburgers: View {
11 | var body: some View {
12 | VStack(spacing: 100) {
13 | Hamburger()
14 | Hamburger2()
15 | Hamburger3()
16 | Hamburger4()
17 | }
18 | }
19 | }
20 |
21 | struct Hamburger: View {
22 |
23 | @State private var isRotating = false
24 | @State private var isHidden = false
25 |
26 | var body: some View {
27 | VStack(spacing: 14){
28 | Rectangle()
29 | .frame(width: 88, height: 8)
30 | .cornerRadius(4)
31 | .opacity(isHidden ? 0 : 1)
32 |
33 | ZStack {
34 | Rectangle()
35 | .frame(width: 88, height: 8)
36 | .cornerRadius(4)
37 | .rotationEffect(.degrees(isRotating ? -45 : 0), anchor: .center)
38 |
39 | Rectangle()
40 | .frame(width: 88, height: 8)
41 | .cornerRadius(4)
42 | .rotationEffect(.degrees(isRotating ? 45 : 0), anchor: .center)
43 | }
44 |
45 | Rectangle()
46 | .frame(width: 88, height: 8)
47 | .cornerRadius(4)
48 | .opacity(isHidden ? 0 : 1)
49 | }
50 | .onTapGesture {
51 | withAnimation(.interpolatingSpring(stiffness: 500, damping: 35)){
52 | isRotating.toggle()
53 | isHidden.toggle()
54 | }
55 | }
56 | }
57 | }
58 |
59 | struct Hamburger2: View {
60 |
61 | @State private var isOffset = false
62 | @State private var isFirstRotating = false
63 | @State private var isSecondRotating = false
64 | @State private var isHidden = false
65 | @State private var isOpened = false
66 |
67 | var body: some View {
68 | VStack(spacing: 14) {
69 | Rectangle()
70 | .frame(width: 88, height: 8)
71 | .cornerRadius(4)
72 | .offset(x: 0, y: isOffset ? 22 : 0)
73 | .opacity(isHidden ? 0 : 1)
74 |
75 | ZStack {
76 | Rectangle()
77 | .frame(width: 88, height: 8)
78 | .cornerRadius(4)
79 | .rotationEffect(.degrees(isFirstRotating ? (isSecondRotating ? -135 : -90) : 0), anchor: .center)
80 |
81 | Rectangle()
82 | .frame(width: 88, height: 8)
83 | .cornerRadius(4)
84 | .rotationEffect(.degrees(isFirstRotating ? (isSecondRotating ? -45 : -90) : 0), anchor: .center)
85 | }
86 |
87 | Rectangle()
88 | .frame(width: 88, height: 8)
89 | .cornerRadius(4)
90 | .offset(x: 0, y: isOffset ? -22 : 0)
91 | .opacity(isHidden ? 0 : 1)
92 | }
93 | .onTapGesture {
94 | if !isOpened {
95 | withAnimation(.interpolatingSpring(stiffness: 500, damping: 35)){
96 | isOffset.toggle()
97 | }
98 |
99 | withAnimation(.interpolatingSpring(stiffness: 500, damping: 35).delay(0.15)){
100 | isHidden.toggle()
101 | }
102 |
103 | withAnimation(.interpolatingSpring(stiffness: 500, damping: 35).delay(0.3)){
104 | isFirstRotating.toggle()
105 | }
106 |
107 | withAnimation(.interpolatingSpring(stiffness: 500, damping: 35).delay(0.45)){
108 | isSecondRotating.toggle()
109 | isOpened.toggle()
110 | }
111 | } else {
112 | withAnimation(.interpolatingSpring(stiffness: 500, damping: 35)){
113 | isSecondRotating.toggle()
114 | }
115 |
116 | withAnimation(.interpolatingSpring(stiffness: 500, damping: 35).delay(0.15)){
117 | isFirstRotating.toggle()
118 | }
119 |
120 | withAnimation(.interpolatingSpring(stiffness: 500, damping: 35).delay(0.3)){
121 | isHidden.toggle()
122 | }
123 |
124 | withAnimation(.interpolatingSpring(stiffness: 500, damping: 35).delay(0.45)){
125 | isOffset.toggle()
126 | isOpened.toggle()
127 | }
128 | }
129 | }
130 | }
131 | }
132 |
133 | struct Hamburger3: View {
134 |
135 | @State private var isOffset = false
136 | @State private var isFirstRotating = false
137 | @State private var isSecondRotating = false
138 | @State private var isHidden = false
139 | @State private var isOpened = false
140 |
141 | var body: some View {
142 | VStack(spacing: 14) {
143 | Rectangle()
144 | .frame(width: 88, height: 8)
145 | .cornerRadius(4)
146 | .offset(x: 0, y: isOffset ? 22 : 0)
147 | .opacity(isHidden ? 0 : 1)
148 |
149 | ZStack {
150 | Rectangle()
151 | .frame(width: 88, height: 8)
152 | .cornerRadius(4)
153 | .rotationEffect(.degrees(isFirstRotating ? -90 : 0), anchor: .center)
154 | .rotationEffect(.degrees(isSecondRotating ? -135 : 0), anchor: .center)
155 |
156 | Rectangle()
157 | .frame(width: 88, height: 8)
158 | .cornerRadius(4)
159 | .rotationEffect(.degrees(isFirstRotating ? -90 : 0), anchor: .center)
160 | .rotationEffect(.degrees(isSecondRotating ? -45 : 0), anchor: .center)
161 | }
162 |
163 | Rectangle()
164 | .frame(width: 88, height: 8)
165 | .cornerRadius(4)
166 | .offset(x: 0, y: isOffset ? -22 : 0)
167 | .opacity(isHidden ? 0 : 1)
168 | }
169 | .onTapGesture {
170 | if !isOpened {
171 | withAnimation(.interpolatingSpring(stiffness: 500, damping: 35)){
172 | isOffset.toggle()
173 | }
174 |
175 | withAnimation(.interpolatingSpring(stiffness: 500, damping: 35).delay(0.15)){
176 | isHidden.toggle()
177 | }
178 |
179 | withAnimation(.interpolatingSpring(stiffness: 500, damping: 35).delay(0.3)){
180 | isFirstRotating.toggle()
181 | }
182 |
183 | withAnimation(.interpolatingSpring(stiffness: 500, damping: 35).delay(0.45)){
184 | isSecondRotating.toggle()
185 | isOpened.toggle()
186 | }
187 | } else {
188 | withAnimation(.interpolatingSpring(stiffness: 500, damping: 35)){
189 | isSecondRotating.toggle()
190 | }
191 |
192 | withAnimation(.interpolatingSpring(stiffness: 500, damping: 35).delay(0.15)){
193 | isFirstRotating.toggle()
194 | }
195 |
196 | withAnimation(.interpolatingSpring(stiffness: 500, damping: 35).delay(0.3)){
197 | isHidden.toggle()
198 | }
199 |
200 | withAnimation(.interpolatingSpring(stiffness: 500, damping: 35).delay(0.45)){
201 | isOffset.toggle()
202 | isOpened.toggle()
203 | }
204 | }
205 | }
206 | }
207 | }
208 |
209 | struct Hamburger4: View {
210 |
211 | @State private var isOffset = false
212 | @State private var isHidden = false
213 | @State private var isOpened = false
214 | @State private var angle1: Double = 0.0
215 | @State private var angle2: Double = 0.0
216 |
217 | var body: some View {
218 | VStack(spacing: 14) {
219 | Rectangle()
220 | .frame(width: 88, height: 8)
221 | .cornerRadius(4)
222 | .offset(x: 0, y: isOffset ? 22 : 0)
223 | .opacity(isHidden ? 0 : 1)
224 |
225 | ZStack {
226 | Rectangle()
227 | .frame(width: 88, height: 8)
228 | .cornerRadius(4)
229 | .rotationEffect(.degrees(angle1), anchor: .center)
230 |
231 | Rectangle()
232 | .frame(width: 88, height: 8)
233 | .cornerRadius(4)
234 | .rotationEffect(.degrees(angle2), anchor: .center)
235 | }
236 |
237 | Rectangle()
238 | .frame(width: 88, height: 8)
239 | .cornerRadius(4)
240 | .offset(x: 0, y: isOffset ? -22 : 0)
241 | .opacity(isHidden ? 0 : 1)
242 | }
243 | .onTapGesture {
244 | if !isOpened {
245 | withAnimation(.interpolatingSpring(stiffness: 500, damping: 35)){
246 | isOffset.toggle()
247 | }
248 |
249 | withAnimation(.interpolatingSpring(stiffness: 500, damping: 35).delay(0.15)){
250 | isHidden.toggle()
251 | }
252 |
253 | withAnimation(.interpolatingSpring(stiffness: 500, damping: 35).delay(0.3)){
254 | angle1 = angle1 - 90
255 | angle2 = angle2 - 90
256 | }
257 |
258 | withAnimation(.interpolatingSpring(stiffness: 500, damping: 35).delay(0.45)){
259 | angle1 = angle1 - 45
260 | angle2 = angle2 + 45
261 | isOpened.toggle()
262 | }
263 | } else {
264 | withAnimation(.interpolatingSpring(stiffness: 500, damping: 35)){
265 | angle1 = angle1 + 45
266 | angle2 = angle2 - 45
267 | }
268 |
269 | withAnimation(.interpolatingSpring(stiffness: 500, damping: 35).delay(0.15)){
270 | angle1 = angle1 - 90
271 | angle2 = angle2 - 90
272 | }
273 |
274 | withAnimation(.interpolatingSpring(stiffness: 500, damping: 35).delay(0.3)){
275 | isHidden.toggle()
276 | }
277 |
278 | withAnimation(.interpolatingSpring(stiffness: 500, damping: 35).delay(0.45)){
279 | isOffset.toggle()
280 | isOpened.toggle()
281 | }
282 | }
283 | }
284 | }
285 | }
286 |
287 | struct Hamburgers_Previews: PreviewProvider {
288 | static var previews: some View {
289 | Hamburgers()
290 | }
291 | }
292 |
--------------------------------------------------------------------------------
/SUIChallenges/SUIChallenges/7.WavedTabView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WavedTabView.swift
3 | // Waved tab view
4 | //
5 | // Created by Alexander Kraev on 01.10.2022.
6 | //
7 |
8 | import SwiftUI
9 |
10 | enum TabType: Int {
11 | case person = 0
12 | case search = 1
13 | case chat = 2
14 | case phone = 3
15 | case settings = 4
16 | }
17 |
18 | struct Tab: Identifiable, Equatable {
19 | let id: Int
20 | let type: TabType
21 | let imageName: String
22 | let color: Color
23 | }
24 |
25 | extension UIDevice {
26 | static var isIPad: Bool {
27 | UIDevice.current.userInterfaceIdiom == .pad
28 | }
29 |
30 | static var isIPhone: Bool {
31 | UIDevice.current.userInterfaceIdiom == .phone
32 | }
33 | }
34 |
35 | struct WavedTabView: View {
36 |
37 | @State var tabs: [Tab] = [.init(id: 1, type: .person, imageName: "person", color: Color.blue.opacity(0.6)),
38 | .init(id: 2, type: .search, imageName: "magnifyingglass", color: Color.orange.opacity(0.6)),
39 | .init(id: 3, type: .chat, imageName: "bubble.left", color: Color.brown.opacity(0.6)),
40 | .init(id: 4, type: .phone, imageName: "phone", color: Color.pink.opacity(0.6)),
41 | .init(id: 5, type: .settings, imageName: "gear", color: Color.mint.opacity(0.6))]
42 |
43 |
44 | @State var selectedTab: TabType = .person
45 |
46 | @State var waveX: CGFloat = 0
47 |
48 | //Constants:
49 |
50 | @State var waveLength: CGFloat = 120
51 | @State var waveHeight: CGFloat = 25
52 | @State var tabBarHeight: CGFloat = 50
53 | @State var hPaddingIn: CGFloat = 35 // horizontal padding inside tab view
54 | @State var cornerRadius: CGFloat = 0
55 | @State var hPaddingOut: CGFloat = 0 // horizontal padding outside tab view
56 | @State var offset: CGFloat = 10
57 | @State var selectedOffset: CGFloat = -15
58 |
59 | var body: some View {
60 |
61 | ZStack(alignment: .bottom) {
62 | TabView(selection: $selectedTab) {
63 | Color.blue
64 | .ignoresSafeArea(.all, edges: .all)
65 | .tag(TabType.person)
66 |
67 | Color.orange
68 | .ignoresSafeArea(.all, edges: .all)
69 | .tag(TabType.search)
70 |
71 | Color.brown
72 | .ignoresSafeArea(.all, edges: .all)
73 | .tag(TabType.chat)
74 |
75 | Color.pink
76 | .ignoresSafeArea(.all, edges: .all)
77 | .tag(TabType.phone)
78 |
79 | Color.mint
80 | .ignoresSafeArea(.all, edges: .all)
81 | .tag(TabType.settings)
82 | }
83 | .zIndex(0)
84 |
85 | ScrollView {
86 | VStack {
87 | HStack {
88 | Text("Wave length: \(Int(waveLength))")
89 | .font(.largeTitle)
90 | Slider(value: $waveLength, in: 80...(UIDevice.isIPad ? 200 : 140))
91 | .tint(.yellow)
92 | }
93 |
94 | HStack {
95 | Text("Wave height: \(Int(waveHeight))")
96 | .font(.largeTitle)
97 | Slider(value: $waveHeight, in: -50...50)
98 | .tint(.yellow)
99 | }
100 |
101 | HStack {
102 | Text("Tab bar height: \(Int(tabBarHeight))")
103 | .font(.largeTitle)
104 | Slider(value: $tabBarHeight, in: 10...90)
105 | .tint(.yellow)
106 | }
107 |
108 | HStack {
109 | Text("Padding inside: \(Int(hPaddingIn))")
110 | .font(.largeTitle)
111 | Slider(value: $hPaddingIn, in: 14...(UIDevice.isIPad ? 100 : 35))
112 | .tint(.yellow)
113 | }
114 |
115 | HStack {
116 | Text("Padding outside: \(Int(hPaddingOut))")
117 | .font(.largeTitle)
118 | Slider(value: $hPaddingOut, in: 0...(UIDevice.isIPad ? 100 : 20))
119 | .tint(.yellow)
120 | }
121 |
122 | HStack {
123 | Text("Corner radius: \(Int(cornerRadius))")
124 | .font(.largeTitle)
125 | Slider(value: $cornerRadius, in: 0...40)
126 | .tint(.yellow)
127 | }
128 |
129 | HStack {
130 | Text("Buttons offset: \(Int(offset))")
131 | .font(.largeTitle)
132 | Slider(value: $offset, in: -40...40)
133 | .tint(.yellow)
134 | }
135 |
136 | HStack {
137 | Text("Selected offset: \(Int(selectedOffset))")
138 | .font(.largeTitle)
139 | Slider(value: $selectedOffset, in: -40...40)
140 | .tint(.yellow)
141 | }
142 |
143 | Button("Refresh values to defaults") {
144 | waveLength = 120
145 | waveHeight = 25
146 | tabBarHeight = 50
147 | hPaddingIn = 35
148 | cornerRadius = 0
149 | hPaddingOut = 0
150 | offset = 10
151 | selectedOffset = -15
152 | }
153 | .frame(maxWidth: .infinity)
154 | .frame(height: 50.0)
155 | .padding(8)
156 | .background(Color.black.opacity(0.8))
157 | .cornerRadius(8)
158 | .foregroundColor(.white)
159 | }
160 | .padding()
161 | .frame(maxHeight: .infinity)
162 | }
163 | .zIndex(1)
164 |
165 | HStack(spacing: 0) {
166 | ForEach(tabs, id: \.id) { tab in
167 | GeometryReader { proxy in
168 | Button(action: {
169 | withAnimation(.linear) {
170 | selectedTab = tab.type
171 | waveX = proxy.frame(in: .global).midX - hPaddingOut
172 | }
173 | }, label: {
174 | ZStack {
175 | Circle()
176 | .fill( selectedTab == tab.type ? tab.color : .clear)
177 | .frame(width: 50.0, height: 50.0)
178 | .zIndex(1)
179 | Image(systemName: tab.imageName)
180 | .resizable()
181 | .aspectRatio(contentMode: .fit)
182 | .foregroundColor(.white)
183 | .frame(width: 25.0, height: 25.0)
184 | .zIndex(2)
185 | }
186 | })
187 | .frame(height: 50.0)
188 | .frame(idealWidth: 50.0)
189 | .offset(x: 0, y: selectedTab == tab.type ? selectedOffset : 0)
190 | .onAppear {
191 | waveX = proxy.frame(in: .global).midX - hPaddingOut
192 | }
193 | }
194 | .frame(width: 50.0, height: 50.0)
195 | if tab != tabs.last { Spacer() }
196 | }
197 | .offset(y: offset)
198 | }
199 | .frame(height: tabBarHeight)
200 | .padding(.horizontal, hPaddingIn)
201 | .padding(.vertical, 24)
202 | .background(Color.black.opacity(0.8)
203 | .clipShape(WavedTabViewBg(corner: cornerRadius,
204 | height: waveHeight,
205 | startX: waveX,
206 | length: waveLength))
207 | )
208 | .padding(.bottom, 51)
209 | .padding(.horizontal, hPaddingOut)
210 | .zIndex(2)
211 | }
212 | .ignoresSafeArea(.all, edges: .bottom)
213 | }
214 | }
215 |
216 | struct WavedTabViewBg: Shape {
217 |
218 | var corner: CGFloat = 0 // for corner radius
219 | var height: CGFloat = 25 // height of wave
220 |
221 | //for wave:
222 | var startX: CGFloat = 180
223 | var length: CGFloat = 120
224 |
225 | var animatableData: CGFloat {
226 | get { startX }
227 | set { startX = newValue }
228 | }
229 |
230 | func path(in rect: CGRect) -> Path {
231 | var path = Path()
232 |
233 | // points:
234 | let startPoint: CGFloat = (startX - (length / 2) <= corner) ? corner : startX - (length / 2)
235 | let midPoint: CGFloat = startX
236 |
237 | let apex1: CGFloat = midPoint - (length / 4)
238 | let apex2: CGFloat = midPoint + (length / 4)
239 |
240 | // init point
241 | path.move(to: .init(x: corner, y: height))
242 |
243 | // line to wave start
244 | path.addLine(to: .init(x: startPoint, y: height))
245 |
246 | // add wave on top:
247 |
248 | path.addCurve(to: CGPoint(x: midPoint, y: 0), control1: CGPoint(x: apex1, y: height), control2: CGPoint(x: apex1, y: 0))
249 |
250 | let finalPoint: CGFloat
251 | if midPoint + (length / 2) >= (rect.width - corner) {
252 | finalPoint = rect.width - corner
253 | } else {
254 | finalPoint = midPoint + (length / 2)
255 | }
256 |
257 | path.addCurve(to: CGPoint(x: finalPoint, y: height), control1: CGPoint(x: apex2, y: 0), control2: CGPoint(x: apex2, y: height))
258 |
259 | // line from end wave to end
260 | path.addLine(to: .init(x: rect.width - corner, y: height))
261 |
262 | //add round corner on top right:
263 | path.addQuadCurve(to: CGPoint(x:rect.width, y: height + corner), control: CGPoint(x: rect.width, y: height))
264 |
265 | // right line:
266 | path.addLine(to: .init(x: rect.width, y: rect.height - corner))
267 |
268 | //add round corner bottom right:
269 | path.addQuadCurve(to: CGPoint(x: rect.width - corner, y: rect.height), control: CGPoint(x: rect.width, y: rect.height))
270 |
271 | // bottom line
272 | path.addLine(to: .init(x: corner, y: rect.height))
273 |
274 | //add round corner bottom left:
275 | path.addQuadCurve(to: CGPoint(x: 0, y: rect.height - corner), control: CGPoint(x: 0, y: rect.height))
276 |
277 | //left line
278 | path.addLine(to: .init(x: 0, y: height + corner))
279 |
280 | //add round corner top left:
281 | path.addQuadCurve(to: CGPoint(x: corner, y: height), control: CGPoint(x: 0, y: height))
282 |
283 | return path
284 | }
285 |
286 | }
287 |
288 | struct WavedTabView_Previews: PreviewProvider {
289 | static var previews: some View {
290 | WavedTabView()
291 | }
292 | }
293 |
--------------------------------------------------------------------------------
/SUIChallenges/SUIChallenges/8.SneakersShop.swift:
--------------------------------------------------------------------------------
1 | //
2 | // 8.SneakersShop.swift
3 | // SUIChallenges
4 | //
5 | // Created by Alexander Kraev on 15.10.2022.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct Hoka: Identifiable {
11 | let id: Int
12 | let image: String
13 | let name: String
14 | let sizes: [String]
15 | let colors: [Color]
16 | }
17 |
18 | struct SneakersShop: View {
19 |
20 | @State var sneakers: [Hoka] = [.init(id: 1, image: "hoka_1", name: "Hoka Clifton 3", sizes: ["41", "42", "42.5", "43", "44"], colors: [.black, .yellow, .white]),
21 | .init(id: 2, image: "hoka_2", name: "Hoka Rincon", sizes: ["40", "42.5"], colors: [.black, .red]),
22 | .init(id: 3, image: "hoka_3", name: "Hoka Clifton 4", sizes: ["41", "42", "42.5", "43"], colors: [.yellow, .black, .blue])]
23 | @State var selectedId: Int = 0
24 | @State var searchText: String = ""
25 | @State var inCartCount: Int = 0
26 |
27 | var body: some View {
28 | GeometryReader { proxy in
29 | ZStack {
30 | Color.gray.opacity(0.3)
31 | .ignoresSafeArea(.all, edges: .all)
32 |
33 | Cloud(alignment: .topTrailing, proxy: proxy, offset: .init(width: 230, height: -100), frameHeightRatio: 1.6, color: .red)
34 | .opacity(0.8)
35 |
36 | Cloud(alignment: .bottomLeading, proxy: proxy, offset: .init(width: -230, height: 120), frameHeightRatio: 1.4, color: .blue)
37 | .opacity(0.7)
38 |
39 | ScrollView {
40 | VStack {
41 |
42 | HStack {
43 | Text("Welcome 🤙🏻")
44 | .font(.largeTitle)
45 |
46 | Spacer()
47 |
48 | Button {
49 |
50 | } label: {
51 |
52 | ZStack {
53 | Image(systemName: "cart.fill")
54 | .resizable()
55 | .padding()
56 | .foregroundColor(.white)
57 | .background(
58 | .black.opacity(0.7)
59 | )
60 | .cornerRadius(8.0)
61 | Text("\(inCartCount > 99 ? "99+" : "\(inCartCount)")")
62 | .kerning(0.3)
63 | .lineLimit(1)
64 | .font(.subheadline)
65 | .truncationMode(.tail)
66 | .foregroundColor(.yellow.opacity(0.9))
67 | .padding(.horizontal, 4)
68 | .cornerRadius(50)
69 | .opacity(inCartCount == 0 ? 0.0 : 1.0)
70 | .offset(x: 16.0, y: -8.0)
71 | }
72 | }.frame(width: 50)
73 | }
74 |
75 | HStack {
76 | SearchBar(text: $searchText) {
77 | withAnimation(.linear) {
78 | selectedId = 0
79 | }
80 | }
81 |
82 | Button {
83 |
84 | } label: {
85 | Image(systemName: "slider.horizontal.3")
86 | .resizable()
87 | .padding()
88 | .foregroundColor(.white)
89 | .background(
90 | .black.opacity(0.7)
91 | )
92 | .cornerRadius(8.0)
93 |
94 | }.frame(width: 50)
95 | }
96 | .frame(height: 50)
97 |
98 | HStack {
99 | Button {
100 |
101 | } label: {
102 | Image(systemName: "square.grid.2x2.fill")
103 | .padding()
104 | .foregroundColor(.white)
105 | .opacity(0.8)
106 | }
107 | .frame(width: 30, height: 30)
108 |
109 | Text("MOST POPULAR")
110 | .font(.caption)
111 | .fontWeight(.heavy)
112 | Spacer()
113 | Button {
114 |
115 | } label: {
116 | Text("Show all")
117 | .foregroundColor(.white)
118 | .font(.caption2)
119 | .fontWeight(.bold)
120 | .opacity(0.7)
121 | }
122 | }
123 | .offset(y: 30)
124 |
125 | ScrollView(.horizontal, showsIndicators: false) {
126 | HStack {
127 | ForEach(sneakers, id: \.id) { sneaker in
128 | Sneaker(isSelected: sneaker.id == selectedId,
129 | imageName: sneaker.image,
130 | name: sneaker.name,
131 | availableSizes: sneaker.sizes,
132 | colors: sneaker.colors) {
133 | inCartCount = inCartCount + 1
134 | }
135 | .onTapGesture {
136 | withAnimation(.linear) {
137 | selectedId = sneaker.id
138 | }
139 | }
140 | .frame(height: 350.0)
141 | }
142 | }
143 | }
144 | }
145 | }
146 | .padding(.horizontal, 12)
147 | }
148 | }
149 | .onTapGesture {
150 | withAnimation(.linear) {
151 | selectedId = 0
152 | }
153 | }
154 | }
155 | }
156 |
157 | struct Cloud: View {
158 |
159 | let alignment: Alignment
160 | let proxy: GeometryProxy
161 | let offset: CGSize
162 | let frameHeightRatio: CGFloat
163 | let color: Color
164 |
165 | var body: some View {
166 | Circle()
167 | .fill(color)
168 | .frame(height: proxy.size.height / frameHeightRatio)
169 | .offset(offset)
170 | .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: alignment)
171 | }
172 | }
173 |
174 | struct Sneaker: View {
175 |
176 | let isSelected: Bool
177 | let imageName: String
178 | let name: String
179 | let availableSizes: [String]
180 | let colors: [Color]
181 | let action: (() -> ())?
182 |
183 | private let imageSize: CGSize = .init(width: 150, height: 70.0)
184 |
185 | var body: some View {
186 | VStack {
187 | VStack(spacing: 10.0) {
188 | Image(imageName)
189 | .resizable()
190 | .aspectRatio(contentMode: .fit)
191 | .frame(width: isSelected ? imageSize.width * 2.5: imageSize.width,
192 | height: isSelected ? imageSize.height * 2.5 : imageSize.height)
193 | .offset(x: isSelected ? 30 : 0.0, y: isSelected ? -60.0 : 0.0)
194 | .rotationEffect(isSelected ? .degrees(-30.0) : .degrees(0.0))
195 | Text(name)
196 | .fontWeight(.heavy)
197 | .offset(y: isSelected ? -60.0 : 0.0)
198 | }
199 |
200 | if isSelected {
201 | Group {
202 | HStack {
203 | Text("SIZE:")
204 | .fontWeight(.medium)
205 | ScrollView(.horizontal, showsIndicators: false) {
206 | HStack {
207 | ForEach(availableSizes, id: \.self) {
208 | Text($0)
209 | .foregroundColor(.black)
210 | .background(
211 | RoundedRectangle(cornerRadius: 4)
212 | .fill(.white)
213 | )
214 | }
215 | }
216 | }
217 | }
218 |
219 | HStack {
220 | Text("COLOR:")
221 | .fontWeight(.medium)
222 | ScrollView(.horizontal, showsIndicators: false) {
223 | HStack {
224 | ForEach(colors, id: \.self) {
225 | Circle()
226 | .stroke(.black, lineWidth: 2)
227 | .background(Circle().fill($0))
228 | .frame(width: 20, height: 22)
229 | }
230 | }
231 | .padding(.horizontal, 8.0)
232 | }
233 | }
234 | .frame(height: 20.0)
235 |
236 | Button {
237 | action?()
238 | } label: {
239 | Capsule()
240 | .fill(.white)
241 | .overlay(
242 | Text("BUY NOW")
243 | .fontWeight(.medium)
244 | .foregroundColor(.black)
245 | )
246 | .frame(height: 30)
247 | }
248 | .buttonStyle(ScaleButtonStyle())
249 | }
250 | .offset(y: isSelected ? -50.0 : 0.0)
251 | .opacity(isSelected ? 1.0 : 0.0)
252 | }
253 | }
254 | .padding(8.0)
255 | .frame(width: 170.0, height: 250.0)
256 | .background(
257 | .ultraThinMaterial,
258 | in: RoundedRectangle(cornerRadius: 8, style: .continuous)
259 | )
260 | }
261 | }
262 |
263 | struct SneakersShop_Previews: PreviewProvider {
264 | static var previews: some View {
265 | SneakersShop()
266 | }
267 | }
268 |
269 | struct Sneaker_Previews: PreviewProvider {
270 | static var previews: some View {
271 | Sneaker(isSelected: true,
272 | imageName: "hoka_1",
273 | name: "Hoka Clifton",
274 | availableSizes: ["41", "42", "42.5", "43"], colors: [.blue, .red, .green]) { }
275 | }
276 | }
277 |
278 | struct SearchBar: View {
279 | @Binding var text: String
280 | @State var placeholder: String = "Search for the sneakers"
281 | let action: (() -> ())?
282 |
283 | var body: some View {
284 | Group{
285 | HStack {
286 | Image(systemName: "magnifyingglass")
287 | .renderingMode(.template)
288 | .frame(width: 20.0, height: 20.0)
289 | .foregroundColor(.black)
290 | .padding(.leading, 16)
291 | .padding(.trailing, 11)
292 | .opacity(0.3)
293 |
294 | Rectangle()
295 | .frame(width: 2.0)
296 | .padding(.horizontal, 3.0)
297 | .padding(.vertical, 8.0)
298 | .opacity(0.3)
299 |
300 | TextField(placeholder, text: $text)
301 | .onTapGesture {
302 | action?()
303 | }
304 | }
305 | .buttonStyle(PlainButtonStyle())
306 | .background(
307 | .ultraThinMaterial,
308 | in: RoundedRectangle(cornerRadius: 8, style: .continuous)
309 | )
310 | }
311 | .cornerRadius(8)
312 | }
313 | }
314 |
315 | struct SearchBar_Previews: PreviewProvider {
316 | static var previews: some View {
317 | SearchBar(text: .constant("")) {}
318 | }
319 | }
320 |
321 | private struct ScaleButtonStyle: ButtonStyle {
322 | public init() {}
323 |
324 | public func makeBody(configuration: Self.Configuration) -> some View {
325 | configuration.label
326 | .scaleEffect(configuration.isPressed ? 0.95 : 1)
327 | .animation(.linear(duration: 0.2), value: configuration.isPressed)
328 | .brightness(configuration.isPressed ? -0.05 : 0)
329 | }
330 | }
331 |
332 | private extension ButtonStyle where Self == ScaleButtonStyle {
333 | static var scale: ScaleButtonStyle {
334 | ScaleButtonStyle()
335 | }
336 | }
337 |
--------------------------------------------------------------------------------
/SUIChallenges/SUIChallenges/9.DynamicIsland.swift:
--------------------------------------------------------------------------------
1 | //
2 | // 9.DynamicIsland.swift
3 | // SUIChallenges
4 | //
5 | // Created by Alexander Kraev on 20.10.2022.
6 | //
7 |
8 | import SwiftUI
9 | import Combine
10 |
11 | struct DynamicIsland: View {
12 |
13 | @State var showIncomingCall = false
14 | @State var showNewPost = false
15 | @State var showOngoingCall = false
16 |
17 | var body: some View {
18 | GeometryReader { reader in
19 | ZStack(alignment: .top) {
20 | if UIApplication.shared.withDynamicIsland {
21 | IncomingCallView(size: reader.size,
22 | isShown: $showIncomingCall)
23 |
24 | SwiftUIDevPost(size: reader.size,
25 | isShown: $showNewPost)
26 |
27 | OngoingCallView(size: reader.size,
28 | isShown: $showOngoingCall)
29 |
30 | VStack(spacing: 24) {
31 | Button {
32 | withAnimation(.linear(duration: 0.3)) {
33 | showIncomingCall.toggle()
34 | }
35 | } label: {
36 | Text(showIncomingCall ? "Dismiss" : "Incoming call")
37 | }.opacity(showNewPost || showOngoingCall ? 0 : 1)
38 |
39 | Button {
40 | withAnimation(.linear(duration: 0.3)) {
41 | showNewPost.toggle()
42 | }
43 | } label: {
44 | Text(showNewPost ? "Dismiss" : "@SwiftUI_Dev: new post")
45 | }.opacity(showIncomingCall || showOngoingCall ? 0 : 1)
46 |
47 | Button {
48 | withAnimation(.linear(duration: 0.3)) {
49 | showOngoingCall.toggle()
50 | }
51 | } label: {
52 | Text(showOngoingCall ? "Dismiss" : "Ongoing phone call")
53 | }.opacity(showIncomingCall || showNewPost ? 0 : 1)
54 |
55 | Text("@LexKraev")
56 | .fontWeight(.regular)
57 | .opacity(showIncomingCall || showNewPost || showOngoingCall ? 0 : 0.5)
58 | }
59 | .position(x: reader.frame(in: .local).midX, y: reader.frame(in: .local).midY)
60 | } else {
61 | Text("Please launch app on the iPhone 14 Pro or Pro Max")
62 | .padding()
63 | .position(x: reader.frame(in: .local).midX, y: reader.frame(in: .local).midY)
64 | }
65 | }
66 | }
67 | .ignoresSafeArea()
68 | .statusBarHidden(showIncomingCall || showNewPost || showOngoingCall)
69 | }
70 | }
71 |
72 | struct IncomingCallView: View {
73 | var size: CGSize
74 | @Binding var isShown: Bool
75 |
76 | var body: some View {
77 | HStack {
78 | Group {
79 | Image("me")
80 | .resizable()
81 | .aspectRatio(contentMode: .fit)
82 | .frame(width: 40, height: 40)
83 | .clipShape(Circle())
84 |
85 | VStack(alignment: .leading) {
86 | Text("iPhone")
87 | .font(.caption2)
88 | .fontWeight(.medium)
89 | .foregroundColor(.gray)
90 | Text("Alexander Kraev")
91 | .font(.subheadline)
92 | .fontWeight(.heavy)
93 | .foregroundColor(.white)
94 | }
95 |
96 | Spacer()
97 |
98 | Button {
99 | withAnimation(.linear(duration: 0.3)) {
100 | isShown.toggle()
101 | }
102 | } label: {
103 | Image(systemName: "phone.down.fill")
104 | .resizable()
105 | .aspectRatio(contentMode: .fit)
106 | .frame(width: 14, height: 14)
107 | .foregroundColor(.white)
108 | .padding(14)
109 | .background(.red)
110 | .clipShape(Circle())
111 | }
112 |
113 | Button {
114 |
115 | } label: {
116 | Image(systemName: "phone.fill")
117 | .resizable()
118 | .aspectRatio(contentMode: .fit)
119 | .frame(width: 14, height: 14)
120 | .foregroundColor(.white)
121 | .padding(14)
122 | .background(Color.green)
123 | .clipShape(Circle())
124 | }
125 | }
126 | .opacity(isShown ? 1.0 : 0.0)
127 | }
128 | .padding()
129 | .frame(width: isShown ? size.width - 22 : 126, height: isShown ? 80 : 37.33)
130 | .background(
131 | RoundedRectangle(cornerRadius: isShown ? 50 : 63, style: .continuous)
132 | .fill(.black)
133 | )
134 | .offset(y: 11)
135 | }
136 | }
137 |
138 | struct SwiftUIDevPost: View {
139 | var size: CGSize
140 | @Binding var isShown: Bool
141 | @State private var isSubscribed = false
142 |
143 | var body: some View {
144 | HStack {
145 | Group {
146 | Image("SwiftLogo")
147 | .resizable()
148 | .aspectRatio(contentMode: .fit)
149 | .frame(width: 40, height: 40)
150 | .clipShape(Circle())
151 |
152 | VStack(alignment: .leading) {
153 | Text("SwiftUI dev")
154 | .font(.caption2)
155 | .fontWeight(.medium)
156 | .foregroundColor(.gray)
157 |
158 | Text("New post: Dynamic island")
159 | .font(.subheadline)
160 | .fontWeight(.heavy)
161 | .foregroundColor(.white)
162 | }
163 |
164 | Spacer()
165 |
166 | Button {
167 | withAnimation {
168 | isSubscribed.toggle()
169 | }
170 | } label: {
171 | Image(systemName: isSubscribed ? "bell.fill" : "bell")
172 | .resizable()
173 | .aspectRatio(contentMode: .fit)
174 | .frame(width: 14, height: 14)
175 | .foregroundColor(.white)
176 | .padding(14)
177 | .background(.red.opacity(0.8))
178 | .clipShape(Circle())
179 | }
180 |
181 | Button {
182 | withAnimation(.linear(duration: 0.3)) {
183 | isShown.toggle()
184 | }
185 | } label: {
186 | Image(systemName: "hand.thumbsup.fill")
187 | .resizable()
188 | .aspectRatio(contentMode: .fit)
189 | .frame(width: 14, height: 14)
190 | .foregroundColor(.white)
191 | .padding(14)
192 | .background(.cyan)
193 | .clipShape(Circle())
194 | }
195 | }
196 | .opacity(isShown ? 1.0 : 0.0)
197 | }
198 | .padding()
199 | .frame(width: isShown ? size.width - 22 : 126, height: isShown ? 80 : 37.33)
200 | .background(
201 | RoundedRectangle(cornerRadius: isShown ? 50 : 63, style: .continuous)
202 | .fill(.black)
203 | )
204 | .offset(y: 11)
205 | }
206 | }
207 |
208 | struct OngoingCallView: View {
209 | var size: CGSize
210 | @Binding var isShown: Bool
211 |
212 | @State var hours: Int = 0
213 | @State var minutes: Int = 0
214 | @State var seconds: Int = 0
215 |
216 | @State var timer: Timer? = nil
217 |
218 | var body: some View {
219 | HStack {
220 | Group {
221 | HStack (spacing: 8) {
222 | Image(systemName: "phone.fill")
223 | .resizable()
224 | .aspectRatio(contentMode: .fit)
225 | .frame(width: 14, height: 14)
226 | .foregroundColor(.green)
227 |
228 | Text("\(minutes):" + (seconds < 10 ? "0" + String(seconds): "\(seconds)"))
229 | .foregroundColor(.green)
230 | }
231 | Spacer()
232 |
233 | // Voice bar
234 | HStack(spacing: 1.0) {
235 | ForEach(0...15, id: \.self) { _ in
236 | Bar(color: .white, interval: 0.1)
237 | }
238 | }
239 | .frame(width: 60, height: 20)
240 | }
241 | .opacity(isShown ? 1.0 : 0.0)
242 | }
243 | .padding()
244 | .frame(width: isShown ? 280 : 126, height: 37.33)
245 | .background(
246 | RoundedRectangle(cornerRadius: isShown ? 50 : 63, style: .continuous)
247 | .fill(.black)
248 | )
249 | .offset(y: 11)
250 | .onChange(of: isShown) { value in
251 | value ? startTimer() : stopTimer()
252 | print("value: \(value)")
253 | }
254 | }
255 |
256 | func startTimer(){
257 | timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true){ tempTimer in
258 | if seconds == 59 {
259 | seconds = 0
260 | if minutes == 59 {
261 | minutes = 0
262 | hours = hours + 1
263 | } else {
264 | minutes = minutes + 1
265 | }
266 | } else {
267 | seconds = seconds + 1
268 | }
269 | }
270 | }
271 |
272 | func stopTimer(){
273 | timer?.invalidate()
274 | timer = nil
275 | hours = 0
276 | minutes = 0
277 | seconds = 0
278 | }
279 | }
280 |
281 | fileprivate struct Bar: View {
282 |
283 | let color: Color
284 | let timer: Timer.TimerPublisher
285 |
286 | @State private var paddingTop: CGFloat = 1.0
287 |
288 | init(color: Color, interval: TimeInterval) {
289 | self.color = color
290 | timer = Timer.publish(every: interval, on: .main, in: .common)
291 | }
292 |
293 | var body: some View {
294 | Segment(color: color, paddingTop: paddingTop)
295 | .animation(.spring(), value: paddingTop)
296 | .onReceive(timer.autoconnect()) { _ in
297 | paddingTop = CGFloat.random(in: 0...0.4)
298 | }
299 | }
300 | }
301 |
302 | fileprivate struct Segment: Animatable, View {
303 |
304 | let color: Color
305 | var paddingTop: CGFloat = 1.0
306 |
307 | public var animatableData: CGFloat {
308 | get { paddingTop }
309 | set { paddingTop = newValue }
310 | }
311 |
312 | var body: some View {
313 | GeometryReader { geometry in
314 | RoundedRectangle(cornerRadius: 12.0)
315 | .fill(color)
316 | .frame(width: 3)
317 | .padding(.vertical, geometry.size.height * paddingTop)
318 | }
319 | }
320 | }
321 |
322 | fileprivate extension UIApplication {
323 | var withDynamicIsland: Bool {
324 | return UIDevice.current.name == "iPhone 14 Pro" || UIDevice.current.name == "iPhone 14 Pro Max"
325 | }
326 | }
327 |
--------------------------------------------------------------------------------
/SUIChallenges/SUIChallenges/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/SUIChallenges/SUIChallenges/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "scale" : "2x",
6 | "size" : "20x20"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "scale" : "3x",
11 | "size" : "20x20"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "scale" : "2x",
16 | "size" : "29x29"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "scale" : "3x",
21 | "size" : "29x29"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "scale" : "2x",
26 | "size" : "40x40"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "scale" : "3x",
31 | "size" : "40x40"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "scale" : "2x",
36 | "size" : "60x60"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "scale" : "3x",
41 | "size" : "60x60"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "scale" : "1x",
46 | "size" : "20x20"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "scale" : "2x",
51 | "size" : "20x20"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "scale" : "1x",
56 | "size" : "29x29"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "scale" : "2x",
61 | "size" : "29x29"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "scale" : "1x",
66 | "size" : "40x40"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "scale" : "2x",
71 | "size" : "40x40"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "scale" : "1x",
76 | "size" : "76x76"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "scale" : "2x",
81 | "size" : "76x76"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "scale" : "2x",
86 | "size" : "83.5x83.5"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "scale" : "1x",
91 | "size" : "1024x1024"
92 | }
93 | ],
94 | "info" : {
95 | "author" : "xcode",
96 | "version" : 1
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/SUIChallenges/SUIChallenges/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/SUIChallenges/SUIChallenges/Assets.xcassets/Sakhalin/1.imageset/1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/c-villain/SwiftUI-challenges/47ff3dfe387b31e8702838fc241022682cd203fb/SUIChallenges/SUIChallenges/Assets.xcassets/Sakhalin/1.imageset/1.jpg
--------------------------------------------------------------------------------
/SUIChallenges/SUIChallenges/Assets.xcassets/Sakhalin/1.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "1.jpg",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/SUIChallenges/SUIChallenges/Assets.xcassets/Sakhalin/2.imageset/2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/c-villain/SwiftUI-challenges/47ff3dfe387b31e8702838fc241022682cd203fb/SUIChallenges/SUIChallenges/Assets.xcassets/Sakhalin/2.imageset/2.jpg
--------------------------------------------------------------------------------
/SUIChallenges/SUIChallenges/Assets.xcassets/Sakhalin/2.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "2.jpg",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/SUIChallenges/SUIChallenges/Assets.xcassets/Sakhalin/3.imageset/3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/c-villain/SwiftUI-challenges/47ff3dfe387b31e8702838fc241022682cd203fb/SUIChallenges/SUIChallenges/Assets.xcassets/Sakhalin/3.imageset/3.jpg
--------------------------------------------------------------------------------
/SUIChallenges/SUIChallenges/Assets.xcassets/Sakhalin/3.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "3.jpg",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/SUIChallenges/SUIChallenges/Assets.xcassets/Sakhalin/4.imageset/4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/c-villain/SwiftUI-challenges/47ff3dfe387b31e8702838fc241022682cd203fb/SUIChallenges/SUIChallenges/Assets.xcassets/Sakhalin/4.imageset/4.jpg
--------------------------------------------------------------------------------
/SUIChallenges/SUIChallenges/Assets.xcassets/Sakhalin/4.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "4.jpg",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/SUIChallenges/SUIChallenges/Assets.xcassets/Sakhalin/5.imageset/5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/c-villain/SwiftUI-challenges/47ff3dfe387b31e8702838fc241022682cd203fb/SUIChallenges/SUIChallenges/Assets.xcassets/Sakhalin/5.imageset/5.jpg
--------------------------------------------------------------------------------
/SUIChallenges/SUIChallenges/Assets.xcassets/Sakhalin/5.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "5.jpg",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/SUIChallenges/SUIChallenges/Assets.xcassets/Sakhalin/6.imageset/6.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/c-villain/SwiftUI-challenges/47ff3dfe387b31e8702838fc241022682cd203fb/SUIChallenges/SUIChallenges/Assets.xcassets/Sakhalin/6.imageset/6.jpg
--------------------------------------------------------------------------------
/SUIChallenges/SUIChallenges/Assets.xcassets/Sakhalin/6.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "6.jpg",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/SUIChallenges/SUIChallenges/Assets.xcassets/Sakhalin/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/SUIChallenges/SUIChallenges/Assets.xcassets/Sneakers/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/SUIChallenges/SUIChallenges/Assets.xcassets/Sneakers/hoka_1.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "hoka_1.png",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/SUIChallenges/SUIChallenges/Assets.xcassets/Sneakers/hoka_1.imageset/hoka_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/c-villain/SwiftUI-challenges/47ff3dfe387b31e8702838fc241022682cd203fb/SUIChallenges/SUIChallenges/Assets.xcassets/Sneakers/hoka_1.imageset/hoka_1.png
--------------------------------------------------------------------------------
/SUIChallenges/SUIChallenges/Assets.xcassets/Sneakers/hoka_2.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "hoka_2.png",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/SUIChallenges/SUIChallenges/Assets.xcassets/Sneakers/hoka_2.imageset/hoka_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/c-villain/SwiftUI-challenges/47ff3dfe387b31e8702838fc241022682cd203fb/SUIChallenges/SUIChallenges/Assets.xcassets/Sneakers/hoka_2.imageset/hoka_2.png
--------------------------------------------------------------------------------
/SUIChallenges/SUIChallenges/Assets.xcassets/Sneakers/hoka_3.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "hoka_3.png",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/SUIChallenges/SUIChallenges/Assets.xcassets/Sneakers/hoka_3.imageset/hoka_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/c-villain/SwiftUI-challenges/47ff3dfe387b31e8702838fc241022682cd203fb/SUIChallenges/SUIChallenges/Assets.xcassets/Sneakers/hoka_3.imageset/hoka_3.png
--------------------------------------------------------------------------------
/SUIChallenges/SUIChallenges/Assets.xcassets/SwiftLogo.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "SwiftLogo.jpg",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/SUIChallenges/SUIChallenges/Assets.xcassets/SwiftLogo.imageset/SwiftLogo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/c-villain/SwiftUI-challenges/47ff3dfe387b31e8702838fc241022682cd203fb/SUIChallenges/SUIChallenges/Assets.xcassets/SwiftLogo.imageset/SwiftLogo.jpg
--------------------------------------------------------------------------------
/SUIChallenges/SUIChallenges/Assets.xcassets/like-you.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "like-you.png",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/SUIChallenges/SUIChallenges/Assets.xcassets/like-you.imageset/like-you.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/c-villain/SwiftUI-challenges/47ff3dfe387b31e8702838fc241022682cd203fb/SUIChallenges/SUIChallenges/Assets.xcassets/like-you.imageset/like-you.png
--------------------------------------------------------------------------------
/SUIChallenges/SUIChallenges/Assets.xcassets/me.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "IMG_0726.jpg",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/SUIChallenges/SUIChallenges/Assets.xcassets/me.imageset/IMG_0726.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/c-villain/SwiftUI-challenges/47ff3dfe387b31e8702838fc241022682cd203fb/SUIChallenges/SUIChallenges/Assets.xcassets/me.imageset/IMG_0726.jpg
--------------------------------------------------------------------------------
/SUIChallenges/SUIChallenges/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/SUIChallenges/SUIChallenges/SUIChallengesApp.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SUIChallengesApp.swift
3 | // SUIChallenges
4 | //
5 | // Created by Alexander Kraev on 07.08.2022.
6 | //
7 |
8 | import SwiftUI
9 |
10 | @main
11 | struct SUIChallengesApp: App {
12 | var body: some Scene {
13 | WindowGroup {
14 |
15 | /// Uncomment choosed challenge 👇🏻
16 | // Lamp()
17 | // Spotify()
18 | // AirDrop()
19 | // Fireworks()
20 | // FlexCards()
21 | // Hamburgers()
22 | // WavedTabView()
23 | // SneakersShop()
24 | // DynamicIsland()
25 | FastFoodLogo()
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/demos/Airdrop.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/c-villain/SwiftUI-challenges/47ff3dfe387b31e8702838fc241022682cd203fb/demos/Airdrop.gif
--------------------------------------------------------------------------------
/demos/AppAds.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/c-villain/SwiftUI-challenges/47ff3dfe387b31e8702838fc241022682cd203fb/demos/AppAds.gif
--------------------------------------------------------------------------------
/demos/ChristmasEve.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/c-villain/SwiftUI-challenges/47ff3dfe387b31e8702838fc241022682cd203fb/demos/ChristmasEve.gif
--------------------------------------------------------------------------------
/demos/DynamicIsland.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/c-villain/SwiftUI-challenges/47ff3dfe387b31e8702838fc241022682cd203fb/demos/DynamicIsland.gif
--------------------------------------------------------------------------------
/demos/FastFoodCommercial.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/c-villain/SwiftUI-challenges/47ff3dfe387b31e8702838fc241022682cd203fb/demos/FastFoodCommercial.gif
--------------------------------------------------------------------------------
/demos/Fireworks.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/c-villain/SwiftUI-challenges/47ff3dfe387b31e8702838fc241022682cd203fb/demos/Fireworks.gif
--------------------------------------------------------------------------------
/demos/FlexCards.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/c-villain/SwiftUI-challenges/47ff3dfe387b31e8702838fc241022682cd203fb/demos/FlexCards.gif
--------------------------------------------------------------------------------
/demos/Hamburgers.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/c-villain/SwiftUI-challenges/47ff3dfe387b31e8702838fc241022682cd203fb/demos/Hamburgers.gif
--------------------------------------------------------------------------------
/demos/InsideWavedTabBar.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/c-villain/SwiftUI-challenges/47ff3dfe387b31e8702838fc241022682cd203fb/demos/InsideWavedTabBar.gif
--------------------------------------------------------------------------------
/demos/Loader.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/c-villain/SwiftUI-challenges/47ff3dfe387b31e8702838fc241022682cd203fb/demos/Loader.gif
--------------------------------------------------------------------------------
/demos/NonWavedTabBar.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/c-villain/SwiftUI-challenges/47ff3dfe387b31e8702838fc241022682cd203fb/demos/NonWavedTabBar.gif
--------------------------------------------------------------------------------
/demos/SneakersShop.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/c-villain/SwiftUI-challenges/47ff3dfe387b31e8702838fc241022682cd203fb/demos/SneakersShop.gif
--------------------------------------------------------------------------------
/demos/Spotify.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/c-villain/SwiftUI-challenges/47ff3dfe387b31e8702838fc241022682cd203fb/demos/Spotify.gif
--------------------------------------------------------------------------------
/demos/UpsideWavedTabBar.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/c-villain/SwiftUI-challenges/47ff3dfe387b31e8702838fc241022682cd203fb/demos/UpsideWavedTabBar.gif
--------------------------------------------------------------------------------
/demos/VerticalWatches.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/c-villain/SwiftUI-challenges/47ff3dfe387b31e8702838fc241022682cd203fb/demos/VerticalWatches.gif
--------------------------------------------------------------------------------
/demos/animatable.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/c-villain/SwiftUI-challenges/47ff3dfe387b31e8702838fc241022682cd203fb/demos/animatable.gif
--------------------------------------------------------------------------------
/demos/imageSlider.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/c-villain/SwiftUI-challenges/47ff3dfe387b31e8702838fc241022682cd203fb/demos/imageSlider.gif
--------------------------------------------------------------------------------
/demos/kavsoft.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/c-villain/SwiftUI-challenges/47ff3dfe387b31e8702838fc241022682cd203fb/demos/kavsoft.gif
--------------------------------------------------------------------------------
/demos/lamp.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/c-villain/SwiftUI-challenges/47ff3dfe387b31e8702838fc241022682cd203fb/demos/lamp.gif
--------------------------------------------------------------------------------
/demos/panerai.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/c-villain/SwiftUI-challenges/47ff3dfe387b31e8702838fc241022682cd203fb/demos/panerai.gif
--------------------------------------------------------------------------------