├── .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 | [![contact: @lexkraev](https://img.shields.io/badge/contact-%40lexkraev-blue.svg?style=flat)](https://t.me/lexkraev) 4 | [![Telegram Group](https://img.shields.io/endpoint?color=neon&style=flat-square&url=https%3A%2F%2Ftg.sumanjay.workers.dev%2Fswiftui_dev)](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 --------------------------------------------------------------------------------