├── IOS17-Swift.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── swiftpm │ │ │ └── Package.resolved │ └── xcuserdata │ │ └── darktech4.xcuserdatad │ │ ├── IDEFindNavigatorScopes.plist │ │ └── UserInterfaceState.xcuserstate ├── xcshareddata │ └── xcschemes │ │ └── IOS17-Swift.xcscheme └── xcuserdata │ └── darktech4.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ └── xcschememanagement.plist ├── IOS17-Swift ├── Assets.xcassets │ ├── AccentColor.colorset │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Contents.json │ ├── DLMode │ │ ├── Contents.json │ │ ├── bg.colorset │ │ │ └── Contents.json │ │ ├── lb.colorset │ │ │ └── Contents.json │ │ ├── lbi.colorset │ │ │ └── Contents.json │ │ ├── lock.imageset │ │ │ ├── Contents.json │ │ │ └── DALL·E 2024-09-05 14.40.13 - A modern, clean security lock icon with a shield symbol in white, in PNG format with no background. The design should be minimalistic, sleek, and bold.png │ │ └── rec.colorset │ │ │ └── Contents.json │ ├── DarkModeSwitch │ │ ├── Contents.json │ │ ├── Moon.colorset │ │ │ └── Contents.json │ │ ├── Sun.colorset │ │ │ └── Contents.json │ │ └── ThemeBG.colorset │ │ │ └── Contents.json │ ├── DeleteCard │ │ ├── Contents.json │ │ ├── deleteCardBG.colorset │ │ │ └── Contents.json │ │ └── deleteCardDB.colorset │ │ │ └── Contents.json │ ├── ImageParalax │ │ ├── Contents.json │ │ ├── planet.imageset │ │ │ ├── Contents.json │ │ │ └── —Pngtree—astronaut in the galaxy cartoon_6469121.png │ │ └── stars.imageset │ │ │ ├── Contents.json │ │ │ └── cHJpdmF0ZS9sci9pbWFnZXMvd2Vic2l0ZS8yMDIzLTEwL3Jhd3BpeGVsX29mZmljZV8zN19waG90b19vZl9hX2xhcmdlX2Ftb3VudF9vZl9zdGFyc19hbmRfbmVidWxhZV8yMGIzMzcyYS01MTUwLTRkODUtODM4My00NjU4Yzc5MTk0ZDdfMS5qcGc.png │ ├── OnBoardingAnimation │ │ ├── Contents.json │ │ ├── pic1.imageset │ │ │ ├── Contents.json │ │ │ └── filip-zrnzevic-_EMkxLdko9k-unsplash.jpg │ │ ├── pic2.imageset │ │ │ ├── Contents.json │ │ │ └── michael-mouritz-WXX_DhjlmD4-unsplash.jpg │ │ ├── pic3.imageset │ │ │ ├── Contents.json │ │ │ └── branimir-balogovic-NeX4gheYs3Q-unsplash.jpg │ │ └── pic4.imageset │ │ │ ├── Contents.json │ │ │ └── Rectangle 1513.png │ ├── Parallax Carousel Scroll │ │ ├── Contents.json │ │ ├── ImageDefault.imageset │ │ │ ├── Contents.json │ │ │ └── icon-image-not-found-free-vector.jpg │ │ ├── Pic 1.imageset │ │ │ ├── Contents.json │ │ │ └── clement-fusil-Fpqx6GGXfXs-unsplash.jpg │ │ ├── Pic 2.imageset │ │ │ ├── Contents.json │ │ │ └── paul-pastourmatzis-ysA6qL8j-OI-unsplash.jpg │ │ └── Pic 3.imageset │ │ │ ├── Contents.json │ │ │ └── laurel-balyeat-ExarETx4xNA-unsplash.jpg │ ├── ParallaxScrollEffect │ │ ├── Contents.json │ │ ├── p1.imageset │ │ │ ├── Contents.json │ │ │ └── luca-bravo-XJXWbfSo2f0-unsplash.jpg │ │ └── p2.imageset │ │ │ ├── Contents.json │ │ │ └── austin-poon-JO_S6ewBqAk-unsplash.jpg │ ├── Text.colorset │ │ └── Contents.json │ ├── Text2.colorset │ │ └── Contents.json │ ├── YTBMiniPlayer │ │ ├── Contents.json │ │ ├── jj1.imageset │ │ │ ├── Contents.json │ │ │ └── maxresdefault (1).jpg │ │ ├── jj2.imageset │ │ │ ├── Contents.json │ │ │ └── maxresdefault (2).jpg │ │ ├── jj3.imageset │ │ │ ├── Contents.json │ │ │ └── maxresdefault (3).jpg │ │ └── jj4.imageset │ │ │ ├── Contents.json │ │ │ └── maxresdefault.jpg │ ├── rev.colorset │ │ └── Contents.json │ ├── sideMenu.colorset │ │ └── Contents.json │ └── test-img.imageset │ │ ├── Contents.json │ │ └── wallpaperflare.com_wallpaper (4).jpg ├── ContentView.swift ├── Helpers │ ├── BackToTopScrollView.swift │ ├── CoverFlow.swift │ ├── FloatingButton.swift │ ├── LimitedTextField.swift │ └── Toast.swift ├── IOS17_SwiftApp.swift ├── Models │ ├── CoinModel.swift │ ├── PlayerItem.swift │ ├── Tab.swift │ └── TripCard.swift ├── Preview Content │ └── Preview Assets.xcassets │ │ └── Contents.json ├── Services │ └── CoinDataService.swift ├── Utilities │ ├── Color.swift │ └── NetworkingManager.swift ├── View │ ├── CustomViewTransition │ │ └── CustomViewTransition.swift │ ├── CoverFlow │ │ └── CoverFlowShowView.swift │ ├── CustomLayout │ │ └── CustomLayoutView.swift │ ├── CustomSegmentAnimated │ │ └── CustomSegmentAnimated.swift │ ├── DarkLightMode │ │ └── DarkLightModeView.swift │ ├── DarkModeSwitch │ │ └── ThemeChangeSwitch.swift │ ├── DeleteAccountView │ │ └── DeleteAccountView.swift │ ├── DropDownView │ │ └── DropDownView.swift │ ├── DynamicSlider │ │ └── CustomSlider2.swift │ ├── DynamicTabbar │ │ └── DynamicTabbar.swift │ ├── FlipClockTextEffect │ │ └── FlipClockTextEffectView.swift │ ├── FloatingActionButton │ │ └── FloatingActionButtonView.swift │ ├── FloatingBottomSheets │ │ ├── FloatingBottomSheetsView.swift │ │ └── Helper │ │ │ └── FloatingSheet.swift │ ├── FloatingTabBar │ │ ├── Model │ │ │ └── TabFloating.swift │ │ └── View │ │ │ ├── CustomTabbar.swift │ │ │ └── FloatingTabBarView.swift │ ├── HackerGlitchTextEffect │ │ ├── GlitchTextEffectView.swift │ │ └── HackerTextEffectView.swift │ ├── ImageParalax │ │ └── ImageParalaxView.swift │ ├── InlineToasts │ │ ├── Helpers │ │ │ └── InlineToast.swift │ │ └── HomeInlineToastView.swift │ ├── LimitedTextField │ │ └── LimitedTextFieldHome .swift │ ├── OnBoardingAnimation │ │ ├── Intro.swift │ │ ├── LiquidShapeAnimation.swift │ │ ├── OnBoardingScreen.swift │ │ └── ViewExtension.swift │ ├── Other │ │ ├── AnimatedSFSymbol.swift │ │ ├── Animation.swift │ │ ├── CustomTextField.swift │ │ ├── MapInteraction.swift │ │ ├── PalettePickerView.swift │ │ └── ScrollTransition.swift │ ├── ParallaxCarouselScroll │ │ └── ParallaxCarouselScrollView.swift │ ├── ParallaxScrollEffect │ │ └── ParallaxScrollEffectView.swift │ ├── Passcode │ │ ├── NumberPadView.swift │ │ ├── PasscodeIndicatorView.swift │ │ └── PasscodeView.swift │ ├── PatternLock │ │ └── PatternLockScreen.swift │ ├── ScrollProgressTracker │ │ └── ScrollProgressTrackerView.swift │ ├── SideMenu │ │ ├── AnimatedSideBar.swift │ │ └── HomeAnimatedSideBarView.swift │ ├── SlideToUnlock │ │ └── SlideToUnlockView.swift │ ├── StackAnimation │ │ ├── StackAnimation.swift │ │ └── StackView.swift │ ├── StretchySlider │ │ ├── CustomSlider.swift │ │ └── StretchySliderView.swift │ ├── SwipeActionsScrollView │ │ └── HomeSwipeActions .swift │ └── YouTubeMiniPlayer │ │ ├── MiniPlayerView.swift │ │ └── YouTubeMiniPlayerView.swift └── ViewModels │ └── ParallaxCarouselScrollViewModel.swift ├── Patterns ├── Adapter │ └── Adapter.swift ├── CombineData │ └── CombineSearch.swift ├── Dependency-Injection │ ├── DIHomeView.swift │ ├── Model │ │ └── CoinDIModel.swift │ ├── Protocol │ │ └── ProtocolManager.swift │ ├── Services │ │ ├── NetworkingManagerService.swift │ │ └── NumberGeneratorService.swift │ ├── ViewModels │ │ ├── CoinViewModel.swift │ │ └── NumberGeneratorViewModel.swift │ └── Views │ │ ├── CoinView.swift │ │ └── NumberGeneratorView.swift ├── Factory-Pattern │ └── FactoryView.swift └── Swift-Data │ ├── Model │ ├── Event.swift │ └── Person.swift │ └── Views │ ├── EditEventView.swift │ ├── EditJobView.swift │ ├── EditPersonView.swift │ ├── FaceFactsView.swift │ └── PeopleView.swift └── README.md /IOS17-Swift.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /IOS17-Swift.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /IOS17-Swift.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "pins" : [ 3 | { 4 | "identity" : "kingfisher", 5 | "kind" : "remoteSourceControl", 6 | "location" : "https://github.com/onevcat/Kingfisher.git", 7 | "state" : { 8 | "revision" : "277f1ab2c6664b19b4a412e32b094b201e2d5757", 9 | "version" : "7.10.0" 10 | } 11 | } 12 | ], 13 | "version" : 2 14 | } 15 | -------------------------------------------------------------------------------- /IOS17-Swift.xcodeproj/project.xcworkspace/xcuserdata/darktech4.xcuserdatad/IDEFindNavigatorScopes.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /IOS17-Swift.xcodeproj/project.xcworkspace/xcuserdata/darktech4.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xqsadness/IOS17-SwiftUI/21aa78fb436e3dd021143d13c82656291c5b8b8e/IOS17-Swift.xcodeproj/project.xcworkspace/xcuserdata/darktech4.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /IOS17-Swift.xcodeproj/xcshareddata/xcschemes/IOS17-Swift.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 9 | 10 | 16 | 22 | 23 | 24 | 25 | 26 | 32 | 33 | 43 | 45 | 51 | 52 | 53 | 54 | 60 | 62 | 68 | 69 | 70 | 71 | 73 | 74 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /IOS17-Swift.xcodeproj/xcuserdata/darktech4.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 9 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /IOS17-Swift.xcodeproj/xcuserdata/darktech4.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | IOS17-Swift.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | DD2F499D2B18769900E8C7FE 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /IOS17-Swift/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0xF7", 9 | "green" : "0x8A", 10 | "red" : "0x2A" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | }, 20 | "properties" : { 21 | "localizable" : true 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /IOS17-Swift/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "platform" : "ios", 6 | "size" : "1024x1024" 7 | } 8 | ], 9 | "info" : { 10 | "author" : "xcode", 11 | "version" : 1 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /IOS17-Swift/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /IOS17-Swift/Assets.xcassets/DLMode/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /IOS17-Swift/Assets.xcassets/DLMode/bg.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0xFF", 9 | "green" : "0xFF", 10 | "red" : "0xFE" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "light" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "srgb", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "0xDF", 27 | "green" : "0xDF", 28 | "red" : "0xDF" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | }, 33 | { 34 | "appearances" : [ 35 | { 36 | "appearance" : "luminosity", 37 | "value" : "dark" 38 | } 39 | ], 40 | "color" : { 41 | "color-space" : "srgb", 42 | "components" : { 43 | "alpha" : "1.000", 44 | "blue" : "0x1A", 45 | "green" : "0x18", 46 | "red" : "0x1A" 47 | } 48 | }, 49 | "idiom" : "universal" 50 | } 51 | ], 52 | "info" : { 53 | "author" : "xcode", 54 | "version" : 1 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /IOS17-Swift/Assets.xcassets/DLMode/lb.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0xC4", 9 | "green" : "0xC1", 10 | "red" : "0xC3" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "light" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "srgb", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "0xC4", 27 | "green" : "0xC1", 28 | "red" : "0xC3" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | }, 33 | { 34 | "appearances" : [ 35 | { 36 | "appearance" : "luminosity", 37 | "value" : "dark" 38 | } 39 | ], 40 | "color" : { 41 | "color-space" : "srgb", 42 | "components" : { 43 | "alpha" : "1.000", 44 | "blue" : "0x89", 45 | "green" : "0x89", 46 | "red" : "0x89" 47 | } 48 | }, 49 | "idiom" : "universal" 50 | } 51 | ], 52 | "info" : { 53 | "author" : "xcode", 54 | "version" : 1 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /IOS17-Swift/Assets.xcassets/DLMode/lbi.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0xE7", 9 | "green" : "0xE6", 10 | "red" : "0xE7" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "light" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "srgb", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "0xE7", 27 | "green" : "0xE6", 28 | "red" : "0xE7" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | }, 33 | { 34 | "appearances" : [ 35 | { 36 | "appearance" : "luminosity", 37 | "value" : "dark" 38 | } 39 | ], 40 | "color" : { 41 | "color-space" : "srgb", 42 | "components" : { 43 | "alpha" : "1.000", 44 | "blue" : "0xE3", 45 | "green" : "0xE3", 46 | "red" : "0xE3" 47 | } 48 | }, 49 | "idiom" : "universal" 50 | } 51 | ], 52 | "info" : { 53 | "author" : "xcode", 54 | "version" : 1 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /IOS17-Swift/Assets.xcassets/DLMode/lock.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "DALL·E 2024-09-05 14.40.13 - A modern, clean security lock icon with a shield symbol in white, in PNG format with no background. The design should be minimalistic, sleek, and bold.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 | -------------------------------------------------------------------------------- /IOS17-Swift/Assets.xcassets/DLMode/lock.imageset/DALL·E 2024-09-05 14.40.13 - A modern, clean security lock icon with a shield symbol in white, in PNG format with no background. The design should be minimalistic, sleek, and bold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xqsadness/IOS17-SwiftUI/21aa78fb436e3dd021143d13c82656291c5b8b8e/IOS17-Swift/Assets.xcassets/DLMode/lock.imageset/DALL·E 2024-09-05 14.40.13 - A modern, clean security lock icon with a shield symbol in white, in PNG format with no background. The design should be minimalistic, sleek, and bold.png -------------------------------------------------------------------------------- /IOS17-Swift/Assets.xcassets/DLMode/rec.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0xFF", 9 | "green" : "0xFF", 10 | "red" : "0xFE" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "dark" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "srgb", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "0xFF", 27 | "green" : "0xFF", 28 | "red" : "0xFE" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /IOS17-Swift/Assets.xcassets/DarkModeSwitch/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /IOS17-Swift/Assets.xcassets/DarkModeSwitch/Moon.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0xFE", 9 | "green" : "0x7A", 10 | "red" : "0x84" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /IOS17-Swift/Assets.xcassets/DarkModeSwitch/Sun.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0x18", 9 | "green" : "0x78", 10 | "red" : "0xFF" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /IOS17-Swift/Assets.xcassets/DarkModeSwitch/ThemeBG.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0xFF", 9 | "green" : "0xFF", 10 | "red" : "0xFE" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "dark" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "srgb", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "0x2A", 27 | "green" : "0x20", 28 | "red" : "0x21" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /IOS17-Swift/Assets.xcassets/DeleteCard/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /IOS17-Swift/Assets.xcassets/DeleteCard/deleteCardBG.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0xFF", 9 | "green" : "0xFF", 10 | "red" : "0xFE" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "dark" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "srgb", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "0x1C", 27 | "green" : "0x1D", 28 | "red" : "0x1A" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /IOS17-Swift/Assets.xcassets/DeleteCard/deleteCardDB.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0x1C", 9 | "green" : "0x1D", 10 | "red" : "0x1A" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "dark" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "srgb", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "0xA0", 27 | "green" : "0xA0", 28 | "red" : "0xA0" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /IOS17-Swift/Assets.xcassets/ImageParalax/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /IOS17-Swift/Assets.xcassets/ImageParalax/planet.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "—Pngtree—astronaut in the galaxy cartoon_6469121.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 | -------------------------------------------------------------------------------- /IOS17-Swift/Assets.xcassets/ImageParalax/planet.imageset/—Pngtree—astronaut in the galaxy cartoon_6469121.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xqsadness/IOS17-SwiftUI/21aa78fb436e3dd021143d13c82656291c5b8b8e/IOS17-Swift/Assets.xcassets/ImageParalax/planet.imageset/—Pngtree—astronaut in the galaxy cartoon_6469121.png -------------------------------------------------------------------------------- /IOS17-Swift/Assets.xcassets/ImageParalax/stars.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "cHJpdmF0ZS9sci9pbWFnZXMvd2Vic2l0ZS8yMDIzLTEwL3Jhd3BpeGVsX29mZmljZV8zN19waG90b19vZl9hX2xhcmdlX2Ftb3VudF9vZl9zdGFyc19hbmRfbmVidWxhZV8yMGIzMzcyYS01MTUwLTRkODUtODM4My00NjU4Yzc5MTk0ZDdfMS5qcGc.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 | -------------------------------------------------------------------------------- /IOS17-Swift/Assets.xcassets/ImageParalax/stars.imageset/cHJpdmF0ZS9sci9pbWFnZXMvd2Vic2l0ZS8yMDIzLTEwL3Jhd3BpeGVsX29mZmljZV8zN19waG90b19vZl9hX2xhcmdlX2Ftb3VudF9vZl9zdGFyc19hbmRfbmVidWxhZV8yMGIzMzcyYS01MTUwLTRkODUtODM4My00NjU4Yzc5MTk0ZDdfMS5qcGc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xqsadness/IOS17-SwiftUI/21aa78fb436e3dd021143d13c82656291c5b8b8e/IOS17-Swift/Assets.xcassets/ImageParalax/stars.imageset/cHJpdmF0ZS9sci9pbWFnZXMvd2Vic2l0ZS8yMDIzLTEwL3Jhd3BpeGVsX29mZmljZV8zN19waG90b19vZl9hX2xhcmdlX2Ftb3VudF9vZl9zdGFyc19hbmRfbmVidWxhZV8yMGIzMzcyYS01MTUwLTRkODUtODM4My00NjU4Yzc5MTk0ZDdfMS5qcGc.png -------------------------------------------------------------------------------- /IOS17-Swift/Assets.xcassets/OnBoardingAnimation/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /IOS17-Swift/Assets.xcassets/OnBoardingAnimation/pic1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "filip-zrnzevic-_EMkxLdko9k-unsplash.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 | -------------------------------------------------------------------------------- /IOS17-Swift/Assets.xcassets/OnBoardingAnimation/pic1.imageset/filip-zrnzevic-_EMkxLdko9k-unsplash.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xqsadness/IOS17-SwiftUI/21aa78fb436e3dd021143d13c82656291c5b8b8e/IOS17-Swift/Assets.xcassets/OnBoardingAnimation/pic1.imageset/filip-zrnzevic-_EMkxLdko9k-unsplash.jpg -------------------------------------------------------------------------------- /IOS17-Swift/Assets.xcassets/OnBoardingAnimation/pic2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "michael-mouritz-WXX_DhjlmD4-unsplash.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 | -------------------------------------------------------------------------------- /IOS17-Swift/Assets.xcassets/OnBoardingAnimation/pic2.imageset/michael-mouritz-WXX_DhjlmD4-unsplash.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xqsadness/IOS17-SwiftUI/21aa78fb436e3dd021143d13c82656291c5b8b8e/IOS17-Swift/Assets.xcassets/OnBoardingAnimation/pic2.imageset/michael-mouritz-WXX_DhjlmD4-unsplash.jpg -------------------------------------------------------------------------------- /IOS17-Swift/Assets.xcassets/OnBoardingAnimation/pic3.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "branimir-balogovic-NeX4gheYs3Q-unsplash.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 | -------------------------------------------------------------------------------- /IOS17-Swift/Assets.xcassets/OnBoardingAnimation/pic3.imageset/branimir-balogovic-NeX4gheYs3Q-unsplash.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xqsadness/IOS17-SwiftUI/21aa78fb436e3dd021143d13c82656291c5b8b8e/IOS17-Swift/Assets.xcassets/OnBoardingAnimation/pic3.imageset/branimir-balogovic-NeX4gheYs3Q-unsplash.jpg -------------------------------------------------------------------------------- /IOS17-Swift/Assets.xcassets/OnBoardingAnimation/pic4.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "Rectangle 1513.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 | -------------------------------------------------------------------------------- /IOS17-Swift/Assets.xcassets/OnBoardingAnimation/pic4.imageset/Rectangle 1513.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xqsadness/IOS17-SwiftUI/21aa78fb436e3dd021143d13c82656291c5b8b8e/IOS17-Swift/Assets.xcassets/OnBoardingAnimation/pic4.imageset/Rectangle 1513.png -------------------------------------------------------------------------------- /IOS17-Swift/Assets.xcassets/Parallax Carousel Scroll/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /IOS17-Swift/Assets.xcassets/Parallax Carousel Scroll/ImageDefault.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "icon-image-not-found-free-vector.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 | -------------------------------------------------------------------------------- /IOS17-Swift/Assets.xcassets/Parallax Carousel Scroll/ImageDefault.imageset/icon-image-not-found-free-vector.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xqsadness/IOS17-SwiftUI/21aa78fb436e3dd021143d13c82656291c5b8b8e/IOS17-Swift/Assets.xcassets/Parallax Carousel Scroll/ImageDefault.imageset/icon-image-not-found-free-vector.jpg -------------------------------------------------------------------------------- /IOS17-Swift/Assets.xcassets/Parallax Carousel Scroll/Pic 1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "clement-fusil-Fpqx6GGXfXs-unsplash.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 | -------------------------------------------------------------------------------- /IOS17-Swift/Assets.xcassets/Parallax Carousel Scroll/Pic 1.imageset/clement-fusil-Fpqx6GGXfXs-unsplash.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xqsadness/IOS17-SwiftUI/21aa78fb436e3dd021143d13c82656291c5b8b8e/IOS17-Swift/Assets.xcassets/Parallax Carousel Scroll/Pic 1.imageset/clement-fusil-Fpqx6GGXfXs-unsplash.jpg -------------------------------------------------------------------------------- /IOS17-Swift/Assets.xcassets/Parallax Carousel Scroll/Pic 2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "paul-pastourmatzis-ysA6qL8j-OI-unsplash.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 | -------------------------------------------------------------------------------- /IOS17-Swift/Assets.xcassets/Parallax Carousel Scroll/Pic 2.imageset/paul-pastourmatzis-ysA6qL8j-OI-unsplash.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xqsadness/IOS17-SwiftUI/21aa78fb436e3dd021143d13c82656291c5b8b8e/IOS17-Swift/Assets.xcassets/Parallax Carousel Scroll/Pic 2.imageset/paul-pastourmatzis-ysA6qL8j-OI-unsplash.jpg -------------------------------------------------------------------------------- /IOS17-Swift/Assets.xcassets/Parallax Carousel Scroll/Pic 3.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "laurel-balyeat-ExarETx4xNA-unsplash.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 | -------------------------------------------------------------------------------- /IOS17-Swift/Assets.xcassets/Parallax Carousel Scroll/Pic 3.imageset/laurel-balyeat-ExarETx4xNA-unsplash.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xqsadness/IOS17-SwiftUI/21aa78fb436e3dd021143d13c82656291c5b8b8e/IOS17-Swift/Assets.xcassets/Parallax Carousel Scroll/Pic 3.imageset/laurel-balyeat-ExarETx4xNA-unsplash.jpg -------------------------------------------------------------------------------- /IOS17-Swift/Assets.xcassets/ParallaxScrollEffect/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /IOS17-Swift/Assets.xcassets/ParallaxScrollEffect/p1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "luca-bravo-XJXWbfSo2f0-unsplash.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 | -------------------------------------------------------------------------------- /IOS17-Swift/Assets.xcassets/ParallaxScrollEffect/p1.imageset/luca-bravo-XJXWbfSo2f0-unsplash.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xqsadness/IOS17-SwiftUI/21aa78fb436e3dd021143d13c82656291c5b8b8e/IOS17-Swift/Assets.xcassets/ParallaxScrollEffect/p1.imageset/luca-bravo-XJXWbfSo2f0-unsplash.jpg -------------------------------------------------------------------------------- /IOS17-Swift/Assets.xcassets/ParallaxScrollEffect/p2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "austin-poon-JO_S6ewBqAk-unsplash.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 | -------------------------------------------------------------------------------- /IOS17-Swift/Assets.xcassets/ParallaxScrollEffect/p2.imageset/austin-poon-JO_S6ewBqAk-unsplash.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xqsadness/IOS17-SwiftUI/21aa78fb436e3dd021143d13c82656291c5b8b8e/IOS17-Swift/Assets.xcassets/ParallaxScrollEffect/p2.imageset/austin-poon-JO_S6ewBqAk-unsplash.jpg -------------------------------------------------------------------------------- /IOS17-Swift/Assets.xcassets/Text.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.000", 9 | "green" : "0.000", 10 | "red" : "0.000" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "light" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "srgb", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "0.000", 27 | "green" : "0.000", 28 | "red" : "0.000" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | }, 33 | { 34 | "appearances" : [ 35 | { 36 | "appearance" : "luminosity", 37 | "value" : "dark" 38 | } 39 | ], 40 | "color" : { 41 | "color-space" : "srgb", 42 | "components" : { 43 | "alpha" : "1.000", 44 | "blue" : "1.000", 45 | "green" : "1.000", 46 | "red" : "1.000" 47 | } 48 | }, 49 | "idiom" : "universal" 50 | } 51 | ], 52 | "info" : { 53 | "author" : "xcode", 54 | "version" : 1 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /IOS17-Swift/Assets.xcassets/Text2.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.925", 9 | "green" : "0.925", 10 | "red" : "0.925" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "light" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "srgb", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "0.925", 27 | "green" : "0.925", 28 | "red" : "0.925" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | }, 33 | { 34 | "appearances" : [ 35 | { 36 | "appearance" : "luminosity", 37 | "value" : "dark" 38 | } 39 | ], 40 | "color" : { 41 | "color-space" : "srgb", 42 | "components" : { 43 | "alpha" : "1.000", 44 | "blue" : "0.651", 45 | "green" : "0.651", 46 | "red" : "0.651" 47 | } 48 | }, 49 | "idiom" : "universal" 50 | } 51 | ], 52 | "info" : { 53 | "author" : "xcode", 54 | "version" : 1 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /IOS17-Swift/Assets.xcassets/YTBMiniPlayer/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /IOS17-Swift/Assets.xcassets/YTBMiniPlayer/jj1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "maxresdefault (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 | -------------------------------------------------------------------------------- /IOS17-Swift/Assets.xcassets/YTBMiniPlayer/jj1.imageset/maxresdefault (1).jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xqsadness/IOS17-SwiftUI/21aa78fb436e3dd021143d13c82656291c5b8b8e/IOS17-Swift/Assets.xcassets/YTBMiniPlayer/jj1.imageset/maxresdefault (1).jpg -------------------------------------------------------------------------------- /IOS17-Swift/Assets.xcassets/YTBMiniPlayer/jj2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "maxresdefault (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 | -------------------------------------------------------------------------------- /IOS17-Swift/Assets.xcassets/YTBMiniPlayer/jj2.imageset/maxresdefault (2).jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xqsadness/IOS17-SwiftUI/21aa78fb436e3dd021143d13c82656291c5b8b8e/IOS17-Swift/Assets.xcassets/YTBMiniPlayer/jj2.imageset/maxresdefault (2).jpg -------------------------------------------------------------------------------- /IOS17-Swift/Assets.xcassets/YTBMiniPlayer/jj3.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "maxresdefault (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 | -------------------------------------------------------------------------------- /IOS17-Swift/Assets.xcassets/YTBMiniPlayer/jj3.imageset/maxresdefault (3).jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xqsadness/IOS17-SwiftUI/21aa78fb436e3dd021143d13c82656291c5b8b8e/IOS17-Swift/Assets.xcassets/YTBMiniPlayer/jj3.imageset/maxresdefault (3).jpg -------------------------------------------------------------------------------- /IOS17-Swift/Assets.xcassets/YTBMiniPlayer/jj4.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "maxresdefault.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 | -------------------------------------------------------------------------------- /IOS17-Swift/Assets.xcassets/YTBMiniPlayer/jj4.imageset/maxresdefault.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xqsadness/IOS17-SwiftUI/21aa78fb436e3dd021143d13c82656291c5b8b8e/IOS17-Swift/Assets.xcassets/YTBMiniPlayer/jj4.imageset/maxresdefault.jpg -------------------------------------------------------------------------------- /IOS17-Swift/Assets.xcassets/rev.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "platform" : "watchos", 6 | "reference" : "labelColor" 7 | }, 8 | "idiom" : "universal" 9 | }, 10 | { 11 | "appearances" : [ 12 | { 13 | "appearance" : "luminosity", 14 | "value" : "dark" 15 | } 16 | ], 17 | "color" : { 18 | "color-space" : "srgb", 19 | "components" : { 20 | "alpha" : "1.000", 21 | "blue" : "0.000", 22 | "green" : "0.000", 23 | "red" : "0.000" 24 | } 25 | }, 26 | "idiom" : "universal" 27 | } 28 | ], 29 | "info" : { 30 | "author" : "xcode", 31 | "version" : 1 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /IOS17-Swift/Assets.xcassets/sideMenu.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "extended-linear-srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0x22", 9 | "green" : "0x22", 10 | "red" : "0x22" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /IOS17-Swift/Assets.xcassets/test-img.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "wallpaperflare.com_wallpaper (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 | -------------------------------------------------------------------------------- /IOS17-Swift/Assets.xcassets/test-img.imageset/wallpaperflare.com_wallpaper (4).jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xqsadness/IOS17-SwiftUI/21aa78fb436e3dd021143d13c82656291c5b8b8e/IOS17-Swift/Assets.xcassets/test-img.imageset/wallpaperflare.com_wallpaper (4).jpg -------------------------------------------------------------------------------- /IOS17-Swift/ContentView.swift: -------------------------------------------------------------------------------- 1 | 2 | import SwiftUI 3 | 4 | struct ContentView: View { 5 | var body: some View { 6 | VStack { 7 | //Jump to definition 8 | HomeAnimatedSideBarView() 9 | } 10 | } 11 | } 12 | 13 | #Preview { 14 | RootView{ 15 | ContentView() 16 | } 17 | } 18 | 19 | //struct FloatingActionButton2: View { 20 | // @State private var isExpanded = false 21 | // 22 | // var body: some View { 23 | // ZStack(alignment: .bottomTrailing) { 24 | // Color.black.ignoresSafeArea() 25 | // 26 | // VStack(spacing: 20) { 27 | // ForEach(0..<3) { index in 28 | // Button{ 29 | // 30 | // }label: { 31 | // Image(systemName: ["pencil", "photo", "trash"][index]) 32 | // .symbolEffect(.scale.up.wholeSymbol, options: .nonRepeating) 33 | // .foregroundColor(.white) 34 | // .padding() 35 | // .background(Circle().fill(.blue)) 36 | // } 37 | // .buttonStyle(.plain) 38 | // .offset(y: isExpanded ? 0 : 100) 39 | // .opacity(isExpanded ? 1 : 0) 40 | // } 41 | // 42 | // Button { 43 | // withAnimation(.spring(response: 0.3, dampingFraction: 0.6)) { 44 | // isExpanded.toggle() 45 | // } 46 | // } label: { 47 | // Image(systemName: "plus") 48 | // .rotationEffect(.degrees(isExpanded ? 45 : 0)) 49 | // .foregroundColor(.white) 50 | // .imageScale(.large) 51 | // .padding(24) 52 | // .background(Circle().fill(.blue)) 53 | // } 54 | // .buttonStyle(.plain) 55 | // } 56 | // } 57 | // } 58 | //} 59 | -------------------------------------------------------------------------------- /IOS17-Swift/Helpers/BackToTopScrollView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BackToTopScrollView.swift 3 | // IOS17-Swift 4 | // 5 | // Created by xqsadness on 16/5/25. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct BackToTopScrollView: View { 11 | private let contentView: (ScrollViewProxy) -> Content 12 | private let buttonView: () -> ButtonView 13 | private var buttonPosition: Alignment = .top 14 | private var invisibleTopViewId: String = "TOP_ID" 15 | private var coordinateSpaceName: String = "BACK_TO_TOP_SCROLLVIEW" 16 | private var minimumScrollOffset: CGFloat = 20 17 | private var buttonTransition: AnyTransition = .identity 18 | @State private var offset: CGPoint = .zero 19 | @State private var showBackButton: Bool = false 20 | 21 | init( 22 | @ViewBuilder contentView: @escaping(ScrollViewProxy) -> Content, 23 | @ViewBuilder buttonView: @escaping () -> ButtonView 24 | ) { 25 | self.contentView = contentView 26 | self.buttonView = buttonView 27 | } 28 | 29 | var body: some View { 30 | ScrollViewReader { scrollProxy in 31 | ScrollView { 32 | Color.clear 33 | .frame(height: 0) 34 | .id(invisibleTopViewId) 35 | 36 | PositionObservingView( 37 | coordinateSpace: .named(coordinateSpaceName), 38 | position: Binding( 39 | get: { offset }, 40 | set: { newOffset in 41 | offset = CGPoint( 42 | x: -newOffset.x, 43 | y: -newOffset.y 44 | ) 45 | } 46 | ), 47 | content: { 48 | contentView(scrollProxy) 49 | } 50 | ) 51 | } 52 | .coordinateSpace(name: coordinateSpaceName) 53 | .overlay(alignment: buttonPosition) { 54 | if showBackButton { 55 | Button { 56 | withAnimation { scrollProxy.scrollTo(invisibleTopViewId) } 57 | } label: { 58 | buttonView() 59 | } 60 | .transition(buttonTransition) 61 | .buttonStyle(ButtonPressStyle()) 62 | } 63 | } 64 | } 65 | .onChange(of: offset.y) { _,newValue in 66 | withAnimation(.easeInOut) { 67 | if newValue > minimumScrollOffset && !showBackButton { 68 | showBackButton = true 69 | } else if newValue < minimumScrollOffset && showBackButton { 70 | showBackButton = false 71 | } 72 | } 73 | } 74 | } 75 | 76 | struct ButtonPressStyle: ButtonStyle { 77 | func makeBody(configuration: Configuration) -> some View { 78 | configuration.label 79 | .scaleEffect(configuration.isPressed ? 0.8 : 1) 80 | .animation(.linear, value: configuration.isPressed) 81 | } 82 | } 83 | } 84 | 85 | extension BackToTopScrollView { 86 | func buttonPosition(_ position: Alignment) -> Self { 87 | var view = self 88 | view.buttonPosition = position 89 | return view 90 | } 91 | 92 | func minimumScrollOffset(_ value: CGFloat) -> Self { 93 | var view = self 94 | view.minimumScrollOffset = value 95 | return view 96 | } 97 | 98 | func buttonTransition(_ value: AnyTransition) -> Self { 99 | var view = self 100 | view.buttonTransition = value 101 | return view 102 | } 103 | } 104 | 105 | struct PositionObservingView: View { 106 | var coordinateSpace: CoordinateSpace 107 | @Binding var position: CGPoint 108 | @ViewBuilder var content: () -> Content 109 | 110 | var body: some View { 111 | content() 112 | .background(GeometryReader { geometry in 113 | Color.clear.preference( 114 | key: PreferenceKey.self, 115 | value: geometry.frame(in: coordinateSpace).origin 116 | ) 117 | }) 118 | .onPreferenceChange(PreferenceKey.self) { position in 119 | self.position = position 120 | } 121 | } 122 | } 123 | 124 | private extension PositionObservingView { 125 | struct PreferenceKey: SwiftUI.PreferenceKey { 126 | static var defaultValue: CGPoint { .zero } 127 | 128 | static func reduce(value: inout CGPoint, nextValue: () -> CGPoint) { 129 | // No-op 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /IOS17-Swift/Helpers/CoverFlow.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CoverFlow.swift 3 | // IOS17-Swift 4 | // 5 | // Created by iamblue on 18/01/2024. 6 | // 7 | 8 | import SwiftUI 9 | 10 | //Custom view 11 | struct CoverFlowView: View where Item.Element: Identifiable{ 12 | //Customization Props 13 | var itemWidth: CGFloat 14 | var enableReflection: Bool 15 | var spacing: CGFloat = 0 16 | var rotation: Double 17 | var items: Item 18 | var content: (Item.Element) -> Content 19 | 20 | var body: some View { 21 | GeometryReader { 22 | let size = $0.size 23 | 24 | ScrollView(.horizontal){ 25 | LazyHStack(spacing: 0){ 26 | ForEach(items){ item in 27 | content(item) 28 | .frame(width: itemWidth) 29 | .reflection(enableReflection) 30 | .visualEffect { content, geometryProxy in 31 | content 32 | .rotation3DEffect(.init(degrees: rotation(geometryProxy)), axis: (x: 0, y: 1, z: 0), anchor: .center) 33 | } 34 | .padding(.trailing, item.id == items.last?.id ? 0 : spacing) 35 | } 36 | } 37 | .padding(.horizontal, (size.width - itemWidth) / 2) 38 | .scrollTargetLayout() 39 | } 40 | .scrollTargetBehavior(.viewAligned) 41 | .scrollIndicators(.hidden) 42 | .scrollClipDisabled() 43 | } 44 | } 45 | 46 | func rotation(_ proxy: GeometryProxy) -> Double{ 47 | let scrollViewWidth = proxy.bounds (of: .scrollView (axis: .horizontal))?.width ?? 0 48 | let midX = proxy.frame (in: .scrollView (axis: .horizontal)).midX 49 | /// Converting into progress 50 | let progress = midX / scrollViewWidth 51 | /// Limiting Progress between 0-1 52 | let cappedProgress = max(min (progress, 1), 0) 53 | /// Limiting Rotation between 0-90 54 | let cappedRotation = max(min(rotation,90),0) 55 | let degree = cappedProgress * (cappedRotation * 2) 56 | 57 | return cappedRotation - degree 58 | } 59 | } 60 | 61 | //Cover flow item model 62 | struct CoverFlowItem: Identifiable{ 63 | let id: UUID = .init() 64 | var color: Color 65 | } 66 | 67 | //View extension 68 | fileprivate extension View{ 69 | @ViewBuilder 70 | func reflection(_ added: Bool) -> some View{ 71 | self 72 | .overlay { 73 | if added{ 74 | GeometryReader { 75 | let size = $0.size 76 | 77 | self 78 | //Flipping upside down 79 | .scaleEffect(y: -1) 80 | .mask{ 81 | Rectangle() 82 | .fill( 83 | .linearGradient(colors: [ 84 | .white, 85 | .white.opacity(0.7), 86 | .white.opacity(0.5), 87 | .white.opacity(0.3), 88 | .white.opacity(0.1), 89 | .white.opacity(0) 90 | ] + Array(repeating: Color.clear, count: 5), startPoint: .top, endPoint: .bottom) 91 | ) 92 | } 93 | //Moving to bottom 94 | .offset(y: size.height + 5) 95 | .opacity(0.5) 96 | } 97 | } 98 | } 99 | } 100 | } 101 | 102 | #Preview{ 103 | CoverFlowShowView() 104 | } 105 | -------------------------------------------------------------------------------- /IOS17-Swift/Helpers/FloatingButton.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FloatingButton.swift 3 | // IOS17-Swift 4 | // 5 | // Created by iamblue on 08/04/2024. 6 | // 7 | 8 | import SwiftUI 9 | 10 | /// Custom Button 11 | struct FloatingButton: View { 12 | /// Actions 13 | var buttonSize: CGFloat 14 | var actions: [FloatingAction] 15 | var label: (Bool) -> Label 16 | 17 | init(buttonSize: CGFloat = 50 ,@FloationActionBuilder actions: @escaping () -> [FloatingAction],@ViewBuilder label: @escaping (Bool) -> Label) { 18 | self.buttonSize = buttonSize 19 | self.actions = actions() 20 | self.label = label 21 | } 22 | // view props 23 | @State private var isExpanded: Bool = false 24 | @State private var dragLocation: CGPoint = .zero 25 | @State private var selectedAction: FloatingAction? 26 | @GestureState private var isDragging: Bool = false 27 | var body: some View { 28 | Button{ 29 | isExpanded.toggle() 30 | } label: { 31 | label(isExpanded) 32 | .frame(width: buttonSize, height: buttonSize) 33 | .contentShape(.rect) 34 | } 35 | .buttonStyle(NoAnimationButtonStyle()) 36 | .simultaneousGesture( 37 | LongPressGesture(minimumDuration: 0.3) 38 | .onEnded{ _ in 39 | isExpanded = true 40 | }.sequenced(before: DragGesture().updating($isDragging, body: { _, out, _ in 41 | out = true 42 | }).onChanged{ value in 43 | guard isExpanded else { return } 44 | dragLocation = value.location 45 | }.onEnded{ _ in 46 | Task{ 47 | if let selectedAction{ 48 | isExpanded = false 49 | selectedAction.action() 50 | } 51 | 52 | selectedAction = nil 53 | dragLocation = .zero 54 | } 55 | }) 56 | ) 57 | .background{ 58 | ZStack{ 59 | ForEach(actions){ action in 60 | ActionView(action) 61 | } 62 | } 63 | } 64 | .coordinateSpace(.named("FLOATING VIEW")) 65 | .animation(.snappy(duration: 0.4, extraBounce: 0), value: isExpanded) 66 | } 67 | 68 | //action view 69 | @ViewBuilder 70 | func ActionView(_ action: FloatingAction) -> some View{ 71 | Button{ 72 | action.action() 73 | isExpanded = false 74 | }label: { 75 | Image(systemName: action.symbol) 76 | .font(action.font) 77 | .foregroundStyle(action.tint) 78 | .frame(width: buttonSize, height: buttonSize) 79 | .background(action.background,in: .circle) 80 | .contentShape(.circle) 81 | } 82 | .buttonStyle(PressableButtonStyle()) 83 | .disabled(!isExpanded) 84 | .animation(.snappy(duration: 0.3, extraBounce: 0)) { content in 85 | content 86 | .scaleEffect(selectedAction?.id == action.id ? 1.15 : 1) 87 | } 88 | .background{ 89 | GeometryReader{ 90 | let rect = $0.frame(in: .named("FLOATING VIEW")) 91 | 92 | Color.clear 93 | .onChange(of: dragLocation) { oldValue, newValue in 94 | if isExpanded && isDragging{ 95 | //checking if the drag location is inside any action's rect 96 | if rect.contains(newValue){ 97 | //User is pressing on this action 98 | selectedAction = action 99 | }else{ 100 | // checking if it's gone out of the rect 101 | if selectedAction?.id == action.id && !rect.contains(newValue){ 102 | selectedAction = nil 103 | } 104 | } 105 | } 106 | } 107 | } 108 | } 109 | .rotationEffect(.init(degrees: progress(action) * -90)) 110 | .offset(x: isExpanded ? -offset / 2 : 0) 111 | .rotationEffect(.init(degrees: progress(action) * 90)) 112 | } 113 | 114 | private var offset: CGFloat{ 115 | let buttonsize = buttonSize + 10 116 | return Double(actions.count) * (actions.count == 1 ? buttonsize * 2 : (actions.count == 2 ? buttonsize * 1.25 : buttonsize)) 117 | } 118 | 119 | private func progress(_ action: FloatingAction) -> CGFloat{ 120 | let index = CGFloat(actions.firstIndex(where: { $0.id == action.id }) ?? 0) 121 | return actions.count == 1 ? 1 : (index / CGFloat(actions.count - 1)) 122 | } 123 | } 124 | 125 | //custom button styles 126 | fileprivate struct NoAnimationButtonStyle: ButtonStyle{ 127 | func makeBody(configuration: Configuration) -> some View { 128 | configuration.label 129 | } 130 | } 131 | 132 | fileprivate struct PressableButtonStyle: ButtonStyle{ 133 | func makeBody(configuration: Configuration) -> some View { 134 | configuration.label 135 | .scaleEffect(configuration.isPressed ? 0.9 : 1) 136 | .animation(.smooth(duration: 0.3, extraBounce: 0), value: configuration.isPressed) 137 | } 138 | } 139 | 140 | struct FloatingAction: Identifiable { 141 | private (set) var id: UUID = .init() 142 | var symbol: String 143 | var font: Font = .title3 144 | var tint: Color = .white 145 | var background: Color = .black 146 | var action: () -> () 147 | } 148 | 149 | /// SwiftUI View like Builder to get array of actions using ResultBuilder 150 | @resultBuilder 151 | struct FloationActionBuilder { 152 | static func buildBlock(_ components: FloatingAction...) -> [FloatingAction] { 153 | components.compactMap({ $0 }) 154 | } 155 | } 156 | #Preview { 157 | FloatingActionButton() 158 | } 159 | -------------------------------------------------------------------------------- /IOS17-Swift/Helpers/LimitedTextField.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LimitedTextField.swift 3 | // IOS17-Swift 4 | // 5 | // Created by xqsadness on 01/04/2024. 6 | // 7 | 8 | import SwiftUI 9 | 10 | /// Custom View 11 | struct LimitedTextField: View { 12 | /// Configuration 13 | var config: Config 14 | var hint: String 15 | @Binding var value: String 16 | //View props 17 | @FocusState private var isKeyboardShowing: Bool 18 | var body: some View { 19 | VStack(alignment: config.progressConfig.alignment, spacing: 12){ 20 | ZStack(alignment: .top){ 21 | RoundedRectangle(cornerRadius: config.borderConfig.radius) 22 | .fill(.clear) 23 | .frame(height: config.autoResizes ? 0 : nil) 24 | .contentShape(.rect(cornerRadius: config.borderConfig.radius)) 25 | .onTapGesture { 26 | //Show key board 27 | isKeyboardShowing = true 28 | } 29 | 30 | TextField(hint, text: $value, axis: .vertical) 31 | .focused($isKeyboardShowing) 32 | .onChange(of: value, initial: true) { oldValue, newValue in 33 | guard !config.allowsExcessTyping else { return } 34 | value = String(value.prefix(config.limit)) 35 | } 36 | } 37 | .padding(.horizontal, 15) 38 | .padding(.vertical,10) 39 | .background( 40 | RoundedRectangle(cornerRadius: config.borderConfig.radius) 41 | .stroke(progressColor.gradient, lineWidth: config.borderConfig.width) 42 | ) 43 | 44 | //Progress bar / text indicator 45 | HStack(alignment: .top, spacing: 12){ 46 | if config.progressConfig.showsRing{ 47 | ZStack{ 48 | Circle() 49 | .stroke(.ultraThinMaterial, lineWidth: 5) 50 | 51 | Circle() 52 | .trim(from: 0, to: progress) 53 | .stroke(progressColor.gradient, lineWidth: 5) 54 | .rotationEffect(.degrees(-90)) 55 | } 56 | .frame(width: 20, height: 20) 57 | } 58 | 59 | if config.progressConfig.showsText{ 60 | Text("\(value.count)/\(config.limit)") 61 | .foregroundStyle(progressColor.gradient) 62 | } 63 | } 64 | } 65 | } 66 | 67 | var progress: CGFloat{ 68 | return max(min(CGFloat(value.count) / CGFloat(config.limit), 1), 0) 69 | } 70 | 71 | var progressColor: Color{ 72 | return progress < 0.6 ? config.tint : progress == 1.0 ? .red : .orange 73 | } 74 | 75 | struct Config { 76 | var limit: Int 77 | var tint: Color = .blue 78 | var autoResizes: Bool = false 79 | var allowsExcessTyping: Bool = false 80 | var progressConfig: ProgressConfig = .init() 81 | var borderConfig: BorderConfig = .init() 82 | } 83 | 84 | struct ProgressConfig { 85 | var showsRing: Bool = true 86 | var showsText: Bool = true 87 | var alignment: HorizontalAlignment = . trailing 88 | } 89 | 90 | struct BorderConfig { 91 | var show: Bool = true 92 | var radius: CGFloat = 12 93 | var width: CGFloat = 0.8 94 | } 95 | } 96 | 97 | #Preview { 98 | LimitedTextFieldHome() 99 | } 100 | -------------------------------------------------------------------------------- /IOS17-Swift/IOS17_SwiftApp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // IOS17_SwiftApp.swift 3 | // IOS17-Swift 4 | // 5 | // Created by xqsadness on 30/11/2023. 6 | // 7 | 8 | import SwiftUI 9 | import SwiftData 10 | 11 | @main 12 | struct IOS17_SwiftApp: App { 13 | var body: some Scene { 14 | WindowGroup { 15 | RootView{ 16 | ContentView() 17 | } 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /IOS17-Swift/Models/CoinModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CoinModel.swift 3 | // IOS17-Swift 4 | // 5 | // Created by xqsadness on 01/12/2023. 6 | // 7 | 8 | import Foundation 9 | 10 | struct CoinModel: Identifiable, Codable { 11 | let id, symbol, name: String 12 | let image: String 13 | let currentPrice: Double 14 | let marketCap, marketCapRank, fullyDilutedValuation: Double? 15 | let totalVolume, high24H, low24H: Double? 16 | let priceChange24H: Double? 17 | let priceChangePercentage24H: Double? 18 | let marketCapChange24H: Double? 19 | let marketCapChangePercentage24H: Double? 20 | let circulatingSupply, totalSupply, maxSupply, ath: Double? 21 | let athChangePercentage: Double? 22 | let athDate: String? 23 | let atl, atlChangePercentage: Double? 24 | let atlDate: String? 25 | let lastUpdated: String? 26 | let sparklineIn7D: SparklineIn7D? 27 | let priceChangePercentage24HInCurrency: Double? 28 | let currentHoldings: Double? 29 | 30 | enum CodingKeys: String, CodingKey { 31 | case id, symbol, name, image 32 | case currentPrice = "current_price" 33 | case marketCap = "market_cap" 34 | case marketCapRank = "market_cap_rank" 35 | case fullyDilutedValuation = "fully_diluted_valuation" 36 | case totalVolume = "total_volume" 37 | case high24H = "high_24h" 38 | case low24H = "low_24h" 39 | case priceChange24H = "price_change_24h" 40 | case priceChangePercentage24H = "price_change_percentage_24h" 41 | case marketCapChange24H = "market_cap_change_24h" 42 | case marketCapChangePercentage24H = "market_cap_change_percentage_24h" 43 | case circulatingSupply = "circulating_supply" 44 | case totalSupply = "total_supply" 45 | case maxSupply = "max_supply" 46 | case ath 47 | case athChangePercentage = "ath_change_percentage" 48 | case athDate = "ath_date" 49 | case atl 50 | case atlChangePercentage = "atl_change_percentage" 51 | case atlDate = "atl_date" 52 | case lastUpdated = "last_updated" 53 | case sparklineIn7D = "sparkline_in_7d" 54 | case priceChangePercentage24HInCurrency = "price_change_percentage_24h_in_currency" 55 | case currentHoldings 56 | } 57 | 58 | func updateHoldings(amount: Double) -> CoinModel { 59 | return CoinModel(id: id, symbol: symbol, name: name, image: image, currentPrice: currentPrice, marketCap: marketCap, marketCapRank: marketCapRank, fullyDilutedValuation: fullyDilutedValuation, totalVolume: totalVolume, high24H: high24H, low24H: low24H, priceChange24H: priceChange24H, priceChangePercentage24H: priceChangePercentage24H, marketCapChange24H: marketCapChange24H, marketCapChangePercentage24H: marketCapChangePercentage24H, circulatingSupply: circulatingSupply, totalSupply: totalSupply, maxSupply: maxSupply, ath: ath, athChangePercentage: athChangePercentage, athDate: athDate, atl: atl, atlChangePercentage: atlChangePercentage, atlDate: atlDate, lastUpdated: lastUpdated, sparklineIn7D: sparklineIn7D, priceChangePercentage24HInCurrency: priceChangePercentage24HInCurrency, currentHoldings: amount) 60 | } 61 | 62 | var currentHoldingsValue: Double { 63 | return (currentHoldings ?? 0) * currentPrice 64 | } 65 | 66 | var rank: Int { 67 | return Int(marketCapRank ?? 0) 68 | } 69 | 70 | } 71 | 72 | struct SparklineIn7D: Codable { 73 | let price: [Double]? 74 | } 75 | -------------------------------------------------------------------------------- /IOS17-Swift/Models/PlayerItem.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PlayerItem.swift 3 | // IOS17-Swift 4 | // 5 | // Created by xqsadness on 24/02/2024. 6 | // 7 | 8 | import SwiftUI 9 | 10 | // Player Item Model for YouTube Mini Player Animation 11 | 12 | let dummyDescription: String = " Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book." 13 | 14 | struct PlayerItem: Identifiable, Equatable { 15 | let id: UUID = .init() 16 | var title: String 17 | var author: String 18 | var image: String 19 | var description: String = dummyDescription 20 | } 21 | 22 | //Simple data 23 | var items: [PlayerItem] = [ 24 | .init( 25 | title: "Joji - Die For You", 26 | author: "Joji", 27 | image: "jj1" 28 | ), 29 | .init( 30 | title: "Joji - SLOW DANCING IN THE DARK", 31 | author: "88rising", 32 | image: "jj2" 33 | ), 34 | .init( 35 | title: "Joji - Gimme Love (Official Video)", 36 | author: "88rising", 37 | image: "jj3" 38 | ), 39 | .init( 40 | title: "Joji - Sanctuary (Official Video)", 41 | author: "88rising", 42 | image: "jj4" 43 | ) 44 | ] 45 | 46 | //PlayerConfig 47 | struct PlayerConfig: Equatable { 48 | var position: CGFloat = .zero 49 | var lastPosition: CGFloat = .zero 50 | var progress: CGFloat = .zero 51 | var selectedPlayerItem: PlayerItem? 52 | var showMiniPlayer: Bool = false 53 | mutating func resetPosition(){ 54 | position = .zero 55 | lastPosition = .zero 56 | progress = .zero 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /IOS17-Swift/Models/Tab.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Tab.swift 3 | // IOS17-Swift 4 | // 5 | // Created by xqsadness on 24/02/2024. 6 | // 7 | 8 | import SwiftUI 9 | 10 | //Tab's for YouTube Mini Player Animation 11 | enum Tab: String, CaseIterable{ 12 | case home = "Home" 13 | case shorts = "Shorts" 14 | case subscriptions = "subscriptions" 15 | case you = "You" 16 | 17 | var symbol: String{ 18 | switch self{ 19 | case .home: 20 | "house.fill" 21 | case .shorts: 22 | "video.badge.waveform.fill" 23 | case .subscriptions: 24 | "play.square.stack.fill" 25 | case .you: 26 | "person.circle.fill" 27 | } 28 | } 29 | } 30 | struct CubeOffset: View { 31 | @State private var yellowSquareAnimationX = CGFloat(-25) 32 | @State private var yellowSquareAnimationY = CGFloat(25) 33 | 34 | @State private var purpleSquareAnimationX = CGFloat(25) 35 | @State private var purpleSquareAnimationY = CGFloat(-25) 36 | 37 | @State private var greenSquareAnimationX = CGFloat(25) 38 | @State private var greenSquareAnimationY = CGFloat(25) 39 | 40 | @State private var orangeSquareAnimationX = CGFloat(-25) 41 | @State private var orangeSquareAnimationY = CGFloat(-25) 42 | var four_colors: [Color] 43 | 44 | var body: some View { 45 | ZStack{ 46 | four_colors[0] 47 | .frame(width: 40, height: 40, alignment: .center) 48 | .offset(x: yellowSquareAnimationX, y: yellowSquareAnimationY) 49 | .animation(.timingCurve(0.33, 1, 0.68, 1, duration: 0.5)) 50 | 51 | four_colors[1] 52 | .frame(width: 40, height: 40, alignment: .center) 53 | .offset(x: purpleSquareAnimationX, y: purpleSquareAnimationY) 54 | .animation(.timingCurve(0.33, 1, 0.68, 1, duration: 0.5)) 55 | 56 | four_colors[2] 57 | .frame(width: 40, height: 40, alignment: .center) 58 | .offset(x: greenSquareAnimationX, y: greenSquareAnimationY) 59 | .animation(.timingCurve(0.33, 1, 0.68, 1, duration: 0.5)) 60 | 61 | four_colors[3] 62 | .frame(width: 40, height: 40, alignment: .center) 63 | .offset(x: orangeSquareAnimationX, y: orangeSquareAnimationY) 64 | .animation(.timingCurve(0.33, 1, 0.68, 1, duration: 0.5)) 65 | 66 | }//End of ZStack 67 | .onAppear(){ 68 | yellowSquareAnimationX = -20 69 | yellowSquareAnimationY = 20 70 | purpleSquareAnimationX = 20 71 | purpleSquareAnimationY = -20 72 | greenSquareAnimationX = 20 73 | greenSquareAnimationY = 20 74 | orangeSquareAnimationX = -20 75 | orangeSquareAnimationY = -20 76 | 77 | DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { 78 | orangeSquareAnimationY = 20 79 | greenSquareAnimationY = -20 80 | yellowSquareAnimationX = 20 81 | purpleSquareAnimationX = -20 82 | } 83 | 84 | DispatchQueue.main.asyncAfter(deadline: .now() + 1) { 85 | orangeSquareAnimationX = 20 86 | greenSquareAnimationX = -20 87 | yellowSquareAnimationY = -20 88 | purpleSquareAnimationY = 20 89 | } 90 | 91 | DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) { 92 | orangeSquareAnimationY = -20 93 | greenSquareAnimationY = 20 94 | yellowSquareAnimationX = -20 95 | purpleSquareAnimationX = 20 96 | } 97 | 98 | DispatchQueue.main.asyncAfter(deadline: .now() + 2) { 99 | orangeSquareAnimationX = -20 100 | greenSquareAnimationX = 20 101 | yellowSquareAnimationY = 20 102 | purpleSquareAnimationY = -20 103 | } 104 | 105 | Timer.scheduledTimer(withTimeInterval: 2, repeats: true) { _ in 106 | yellowSquareAnimationX = -20 107 | yellowSquareAnimationY = 20 108 | purpleSquareAnimationX = 20 109 | purpleSquareAnimationY = -20 110 | greenSquareAnimationX = 20 111 | greenSquareAnimationY = 20 112 | orangeSquareAnimationX = -20 113 | orangeSquareAnimationY = -20 114 | 115 | DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { 116 | orangeSquareAnimationY = 20 117 | greenSquareAnimationY = -20 118 | yellowSquareAnimationX = 20 119 | purpleSquareAnimationX = -20 120 | } 121 | 122 | DispatchQueue.main.asyncAfter(deadline: .now() + 1) { 123 | orangeSquareAnimationX = 20 124 | greenSquareAnimationX = -20 125 | yellowSquareAnimationY = -20 126 | purpleSquareAnimationY = 20 127 | } 128 | 129 | DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) { 130 | orangeSquareAnimationY = -20 131 | greenSquareAnimationY = 20 132 | yellowSquareAnimationX = -20 133 | purpleSquareAnimationX = 20 134 | } 135 | 136 | DispatchQueue.main.asyncAfter(deadline: .now() + 2) { 137 | orangeSquareAnimationX = -20 138 | greenSquareAnimationX = 20 139 | yellowSquareAnimationY = 20 140 | purpleSquareAnimationY = -20 141 | } 142 | } 143 | //MARK: - To fire Timer immediately 144 | // .fire() 145 | } 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /IOS17-Swift/Models/TripCard.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TripCard.swift 3 | // IOS17-Swift 4 | // 5 | // Created by xqsadness on 01/12/2023. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct TripCard: Identifiable, Hashable { 11 | var id: UUID = .init() 12 | var title: String 13 | var subTitle: String 14 | var image: String 15 | } 16 | 17 | var tripCards: [TripCard] = [ 18 | .init(title: "London", subTitle: "England", image: "Pic 1"), 19 | .init(title: "New York", subTitle: "USA", image: "Pic 2"), 20 | .init(title: "France", subTitle: "Moutain", image: "Pic 3") 21 | ] 22 | -------------------------------------------------------------------------------- /IOS17-Swift/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /IOS17-Swift/Services/CoinDataService.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CoinDataService.swift 3 | // IOS17-Swift 4 | // 5 | // Created by xqsadness on 01/12/2023. 6 | // 7 | 8 | import SwiftUI 9 | import Combine 10 | 11 | class CoinDataService{ 12 | @Published var allCoins: [CoinModel] = [] 13 | 14 | var coinSubscription: AnyCancellable? 15 | 16 | init(){ 17 | getCoins() 18 | } 19 | 20 | func getCoins(){ 21 | guard let url = URL(string: "https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&order=market_cap_desc&per_page=250&page=1&sparkline=true&price_change_percentage=24h&x_cg_demo_api_key=CG-6n4cetNqwzC1oJU7t3Xe4ebJ") else { 22 | return 23 | } 24 | 25 | coinSubscription = NetworkingManager.download(url: url) 26 | .decode(type: [CoinModel].self, decoder: JSONDecoder()) 27 | .sink(receiveCompletion: NetworkingManager.handleCompeltion, receiveValue: { [weak self] coins in 28 | self?.allCoins = coins 29 | self?.coinSubscription?.cancel() 30 | }) 31 | } 32 | } 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /IOS17-Swift/Utilities/NetworkingManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NetworkingManager.swift 3 | // DefualtSource 4 | // 5 | // Created by xqsadness on 14/11/2023. 6 | // 7 | 8 | import Foundation 9 | import Combine 10 | 11 | class NetworkingManager{ 12 | 13 | enum NetworkingError: LocalizedError{ 14 | case badURLResponse(url: URL) 15 | case unowned 16 | 17 | var errorDescription: String?{ 18 | switch self{ 19 | case .badURLResponse(url: let url): return "Bad response from url: \(url)" 20 | case .unowned: return "unowned error occured" 21 | } 22 | } 23 | } 24 | 25 | static func download(url: URL) -> AnyPublisher{ 26 | return URLSession.shared.dataTaskPublisher(for: url) 27 | .subscribe(on: DispatchQueue.global(qos: .default)) 28 | .tryMap({ try handleURLResponse(output: $0, url: url) }) 29 | .receive(on: DispatchQueue.main) 30 | .eraseToAnyPublisher() 31 | } 32 | 33 | static func handleURLResponse(output: URLSession.DataTaskPublisher.Output, url: URL) throws -> Data{ 34 | guard let response = output.response as? HTTPURLResponse, 35 | response.statusCode >= 200 && response.statusCode < 300 else{ 36 | throw NetworkingError.badURLResponse(url: url) 37 | } 38 | return output.data 39 | } 40 | 41 | static func handleCompeltion(completion: Subscribers.Completion){ 42 | switch completion{ 43 | case .finished: 44 | break 45 | case .failure(let err): 46 | print(err.localizedDescription) 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /IOS17-Swift/View/CoverFlow/CoverFlowShowView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CoverFlowView.swift 3 | // IOS17-Swift 4 | // 5 | // Created by iamblue on 18/01/2024. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct CoverFlowShowView: View { 11 | 12 | @State private var items: [CoverFlowItem] = [.red, .blue, .green, .yellow, .primary, .cyan] 13 | .compactMap{ return .init(color: $0) } 14 | // view props 15 | @State private var spacing: CGFloat = 0 16 | @State private var rotation: CGFloat = .zero 17 | @State private var enableReflection: Bool = false 18 | 19 | var body: some View { 20 | VStack{ 21 | Spacer(minLength: 0) 22 | 23 | CoverFlowView( 24 | itemWidth: 280, 25 | enableReflection: enableReflection, 26 | spacing: spacing, 27 | rotation: rotation, 28 | items: items) { item in 29 | RoundedRectangle(cornerRadius: 20) 30 | .fill(item.color.gradient) 31 | } 32 | .frame(height: 180) 33 | 34 | Spacer(minLength: 0) 35 | 36 | //Custom view 37 | 38 | VStack(alignment: .leading, spacing: 10){ 39 | Toggle("Toggle Reflection", isOn: $enableReflection) 40 | 41 | Text("Card Spacing") 42 | .font(.caption2) 43 | .foregroundStyle(.gray) 44 | 45 | Slider(value: $spacing, in: -120...20) 46 | 47 | Text("Card Rotation") 48 | .font(.caption2) 49 | .foregroundStyle(.gray) 50 | 51 | Slider(value: $rotation, in: 0...180) 52 | } 53 | .padding(15) 54 | .background(.ultraThinMaterial, in: .rect(cornerRadius: 20)) 55 | .padding(15) 56 | } 57 | } 58 | } 59 | 60 | #Preview{ 61 | CoverFlowShowView() 62 | } 63 | -------------------------------------------------------------------------------- /IOS17-Swift/View/CustomLayout/CustomLayoutView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CustomLayoutView.swift 3 | // IOS17-Swift 4 | // 5 | // Created by xqsadness on 04/07/2024. 6 | // 7 | 8 | import SwiftUI 9 | import Kingfisher 10 | 11 | protocol SelfCreateingView: View{ 12 | init() 13 | } 14 | 15 | extension LayoutCaching { 16 | struct MasonryLayout: Layout { 17 | struct Cache { 18 | var frames: [CGRect] 19 | var width = 0.0 20 | } 21 | 22 | var columns: Int 23 | var spacing: Double 24 | 25 | init(columns: Int = 3, spacing: Double = 5) { 26 | self.columns = max(1, columns) 27 | self.spacing = spacing 28 | } 29 | 30 | func makeCache(subviews: Subviews) -> Cache { 31 | Cache(frames: []) 32 | } 33 | 34 | func frames(for subviews: Subviews, in totalWidth: Double) -> [CGRect] { 35 | let totalSpacing = spacing * Double(columns - 1) 36 | let columnWidth = (totalWidth - totalSpacing) / Double(columns) 37 | let columnWidthwithSpacing = columnWidth + spacing 38 | let proposedSize = ProposedViewSize(width: columnWidth, height: nil) 39 | var viewFrames = [CGRect]() 40 | var columnHeights = Array(repeating: 0.0, count: columns) 41 | for subview in subviews { 42 | var selectColumn = 0 43 | var selectedHeight = Double.greatestFiniteMagnitude 44 | for (columnIndex, height) in columnHeights.enumerated() { 45 | if height < selectedHeight { 46 | selectColumn = columnIndex 47 | selectedHeight = height 48 | } 49 | } 50 | let x = Double(selectColumn) * columnWidthwithSpacing 51 | let y = columnHeights [selectColumn] 52 | let size = subview.sizeThatFits(proposedSize) 53 | let frame = CGRect(x: x, y: y, width: size.width, height: size.height) 54 | 55 | columnHeights [selectColumn] += size.height + spacing 56 | viewFrames.append(frame) 57 | } 58 | 59 | return viewFrames 60 | } 61 | 62 | func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout Cache) -> CGSize { 63 | let width = proposal.replacingUnspecifiedDimensions().width 64 | let viewFrames = frames(for: subviews, in: width) 65 | let height = viewFrames.max{ $0.maxY < $1.maxY } ?? .zero 66 | 67 | cache.frames = viewFrames 68 | cache.width = width 69 | 70 | return CGSize(width: width, height: height.maxY) 71 | } 72 | 73 | func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout Cache) { 74 | if cache.width != bounds.width{ 75 | cache.frames = frames(for: subviews, in: bounds.width) 76 | cache.width = bounds.width 77 | } 78 | 79 | for index in subviews.indices{ 80 | let frame = cache.frames[index] 81 | let position = CGPoint(x: bounds.minX + frame.minX, y: bounds.minY + frame.minY) 82 | subviews[index].place(at: position, proposal: ProposedViewSize(frame.size)) 83 | } 84 | } 85 | } 86 | 87 | struct placeholderView: View { 88 | let color: Color = [.blue, .cyan, .green, .indigo, .mint, .orange, .pink, .purple, .red,.yellow].randomElement()! 89 | let size: CGSize 90 | var imageUrl: String 91 | 92 | var body: some View { 93 | ZStack{ 94 | KFImage(URL(string: imageUrl)) 95 | .placeholder { 96 | Image("ImageDefault") 97 | } 98 | .loadDiskFileSynchronously() 99 | .cacheMemoryOnly() 100 | .fade(duration: 0.25) 101 | .resizable() 102 | // .aspectRatio(contentMode: .fill) 103 | .scaledToFill() 104 | // .frame(maxWidth: size.width, maxHeight: size.height) 105 | // .aspectRatio(contentMode: .fill) 106 | 107 | // RoundedRectangle(cornerRadius: 10) 108 | // .fill(color) 109 | 110 | Text("\(Int(size.width))x\(Int(size.height))") 111 | .foregroundStyle(.white) 112 | .font(.headline) 113 | } 114 | // .aspectRatio(size, contentMode: .fill) 115 | } 116 | } 117 | } 118 | 119 | struct LayoutCaching: SelfCreateingView{ 120 | //get image from api 121 | @StateObject var parallaxCarouselScrollViewModel = ParallaxCarouselScrollViewModel() 122 | 123 | @State private var columns = 3 124 | @State private var views = (0..<40).map { _ in 125 | CGSize(width: .random(in: 100...500), height: .random(in: 100...500)) 126 | } 127 | var body: some View{ 128 | ScrollView(.vertical){ 129 | MasonryLayout(columns: columns){ 130 | ForEach(parallaxCarouselScrollViewModel.imageData, id: \.id){ img in 131 | placeholderView(size: views[Int.random(in: 0..<40)], imageUrl: img.urls.raw) 132 | } 133 | } 134 | .padding(.horizontal, 5) 135 | .animation(.spring, value: columns) 136 | } 137 | .scrollIndicators(.hidden) 138 | .safeAreaInset(edge: .bottom) { 139 | Stepper("Column \(columns)",value: $columns) 140 | .padding() 141 | .background(.ultraThinMaterial) 142 | } 143 | } 144 | } 145 | 146 | #Preview { 147 | LayoutCaching() 148 | } 149 | -------------------------------------------------------------------------------- /IOS17-Swift/View/CustomSegmentAnimated/CustomSegmentAnimated.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CustomSegment.swift 3 | // IOS17-Swift 4 | // 5 | // Created by xqsadness on 11/06/2024. 6 | // 7 | 8 | import SwiftUI 9 | 10 | //MARK: This view is for testing purposes only, do not use it !!! 11 | struct SegmentAnimatedView: View { 12 | @State private var isVideo: Bool = false 13 | var body: some View { 14 | CustomSegmentAnimated(isSelected: $isVideo, text1: "Song", text2: "Video") 15 | .frame(width: 180) 16 | } 17 | } 18 | 19 | struct CustomSegmentAnimated: View { 20 | @Binding var isSelected: Bool 21 | @State private var shakeValue: CGFloat = 0 22 | 23 | var text1: String = "text1" 24 | var text2: String = "text2" 25 | 26 | var body: some View { 27 | HStack(spacing: 0) { 28 | TabableText(title: "\(text1)") 29 | .foregroundColor(.accentColor) 30 | .overlay { 31 | Rectangle() 32 | .fill(Color.accentColor) 33 | .clipShape( 34 | .rect( 35 | topLeadingRadius: 30, 36 | bottomLeadingRadius: 30, 37 | bottomTrailingRadius: 0, 38 | topTrailingRadius: 0 39 | ) 40 | ) 41 | .overlay { 42 | TabableText(title: "\(text2)") 43 | .foregroundColor(isSelected ? .white : .clear) 44 | .scaleEffect(x: -1) 45 | } 46 | .rotation3DEffect(.init(degrees: isSelected ? 180 : 0), axis: (x: 0, y: 1, z: 0), anchor: .trailing, perspective: 0.4) 47 | } 48 | .overlay(content: { 49 | TabableText(title: "\(text1)") 50 | .foregroundColor(isSelected ? .clear : .white) 51 | }) 52 | .zIndex(1) 53 | .contentShape(Rectangle()) 54 | .shadow(color: Color.white.opacity(0.4), radius: 3, x: 0, y: 1) 55 | 56 | TabableText(title: "\(text2)") 57 | .foregroundColor(.accentColor) 58 | .zIndex(0) 59 | .shadow(color: Color.green.opacity(0.4), radius: 3, x: 0, y: 1) 60 | 61 | } 62 | .background { 63 | ZStack { 64 | Capsule().fill(Color.white) 65 | Capsule().stroke(Color.accentColor, lineWidth: 3) 66 | } 67 | } 68 | .rotation3DEffect(.init(degrees: shakeValue), axis: (x: 0, y: 1, z: 0)) 69 | } 70 | 71 | private func TabableText(title: String) -> some View { 72 | Text(title) 73 | .font(Font.system(size: 14)) 74 | .padding(.vertical, 5) 75 | .padding(.horizontal, 20) 76 | .frame(maxWidth: .infinity) 77 | .contentShape(Rectangle()) 78 | .onTapGesture { 79 | withAnimation(.interactiveSpring(response: 0.4, dampingFraction: 1, blendDuration: 1)) { 80 | self.isSelected = (title == "\(text2)") 81 | 82 | self.shakeValue = (title == "\(text2)" ? 10 : -10) 83 | 84 | DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { 85 | withAnimation(.interactiveSpring(response: 0.4, dampingFraction: 1, blendDuration: 1)) { 86 | self.shakeValue = 0 87 | } 88 | } 89 | } 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /IOS17-Swift/View/DarkModeSwitch/ThemeChangeSwitch.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ThemeChangeSwitch.swift 3 | // IOS17-Swift 4 | // 5 | // Created by xqsadness on 25/12/2023. 6 | // 7 | 8 | import SwiftUI 9 | 10 | //MARK: This view is for testing purposes only, do not use it !!! 11 | struct TestThemeChangeSwitch: View { 12 | 13 | //Props for Dark Mode Switch 14 | @State private var changeTheme: Bool = false 15 | @Environment(\.colorScheme) private var scheme 16 | @AppStorage("userTheme") private var userTheme: Theme = .systemDedault 17 | 18 | var body: some View { 19 | //MARK: - Dark Mode Switch 20 | VStack{ 21 | Button("Change Theme"){ 22 | changeTheme.toggle() 23 | } 24 | 25 | } 26 | .sheet(isPresented: $changeTheme, content: { 27 | ThemeChangeSwitch(scheme: scheme) 28 | //Since max height is 410 29 | .presentationDetents([.height(410)]) 30 | .presentationBackground(.clear) 31 | }) 32 | //For Dark Mode Switch 33 | .preferredColorScheme(userTheme.colorScheme) 34 | } 35 | } 36 | 37 | struct ThemeChangeSwitch: View { 38 | var scheme: ColorScheme 39 | @AppStorage("userTheme") private var userTheme: Theme = .systemDedault 40 | //For sliding effect 41 | @Namespace private var animation 42 | //View props 43 | @State private var circleOffset: CGSize 44 | 45 | init(scheme: ColorScheme){ 46 | self.scheme = scheme 47 | let isDark = scheme == .dark 48 | self._circleOffset = .init(initialValue: CGSize(width: isDark ? 30 : 150, height: isDark ? -25 : -150)) 49 | } 50 | 51 | var body: some View { 52 | VStack(spacing: 5){ 53 | Circle() 54 | .fill(userTheme.color(scheme).gradient) 55 | .frame(width: 150, height: 150) 56 | .mask { 57 | //Inverted mask 58 | Rectangle() 59 | .overlay { 60 | Circle() 61 | .offset(circleOffset) 62 | .blendMode(.destinationOut) 63 | } 64 | } 65 | 66 | Text("Choose a style") 67 | .font(.title).bold() 68 | .padding(.top, 25) 69 | 70 | Text("Pop or subtle, Day or night.\nCustomize your interface.") 71 | .multilineTextAlignment(.center) 72 | 73 | //Custom Segmented Picker 74 | HStack(spacing: 0){ 75 | ForEach(Theme.allCases, id: \.rawValue){theme in 76 | Text(theme.rawValue) 77 | .padding(.vertical, 10) 78 | .frame(width: 100) 79 | .background{ 80 | ZStack{ 81 | if userTheme == theme{ 82 | Capsule() 83 | .fill(.themeBG) 84 | .matchedGeometryEffect(id: "ACTIVETAB", in: animation) 85 | } 86 | } 87 | .animation(.snappy, value: userTheme) 88 | } 89 | .contentShape(.rect) 90 | .onTapGesture { 91 | userTheme = theme 92 | } 93 | } 94 | } 95 | .padding(3) 96 | .background(.primary.opacity(0.06), in: .capsule) 97 | .padding(.top, 20) 98 | } 99 | //Max height = 410 100 | .frame(maxWidth: .infinity, maxHeight: .infinity) 101 | .frame(height: 410) 102 | .background(.themeBG) 103 | .clipShape(.rect(cornerRadius: 30)) 104 | .padding(.horizontal, 15) 105 | .environment(\.colorScheme, scheme) 106 | .onChange(of: scheme, initial: false) { _, newValue in 107 | let isDark = newValue == .dark 108 | withAnimation(.bouncy) { 109 | circleOffset = CGSize(width: isDark ? 30 : 150, height: isDark ? -25 : -150) 110 | } 111 | } 112 | } 113 | } 114 | 115 | #Preview { 116 | ThemeChangeSwitch(scheme: .light) 117 | } 118 | 119 | //Theme 120 | enum Theme: String, CaseIterable{ 121 | case systemDedault = "Default" 122 | case light = "Light" 123 | case dark = "Dark" 124 | 125 | func color(_ scheme: ColorScheme) -> Color{ 126 | switch self{ 127 | case .systemDedault: 128 | return scheme == .dark ? .moon : .sun 129 | case .light: 130 | return .sun 131 | case .dark: 132 | return .moon 133 | } 134 | } 135 | 136 | var colorScheme: ColorScheme?{ 137 | switch self{ 138 | case .systemDedault: 139 | return nil 140 | case .light: 141 | return .light 142 | case .dark: 143 | return .dark 144 | } 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /IOS17-Swift/View/DeleteAccountView/DeleteAccountView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DeleteAccountView.swift 3 | // IOS17-Swift 4 | // 5 | // Created by xqsadness on 24/07/2024. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct DeleteAccountView: View { 11 | 12 | @State private var show = false 13 | 14 | var body: some View { 15 | ZStack{ 16 | Button{ 17 | withAnimation(.spring(response: 0.6, dampingFraction: 0.7)){ 18 | show.toggle() 19 | } 20 | }label: { 21 | Text("Show delete card") 22 | } 23 | 24 | DeleteCard(icon: "trash.circle", title: "Delete Account", details: "Are you sure you want to delete your account? All your data will be permanently removed.", BStartTitle: "ACCOUNT DELETED", BEndTitle: "HOLD TO DELETE", show: $show) 25 | } 26 | } 27 | } 28 | 29 | struct DeleteCard: View { 30 | 31 | @State var trimEnd: CGFloat = 0.0 32 | var icon: String 33 | var title: String 34 | var details:String 35 | var BStartTitle: String 36 | var BEndTitle: String 37 | @Binding var show: Bool 38 | 39 | var body: some View { 40 | VStack(spacing:20) { 41 | VStack(spacing:28){ 42 | Image(systemName: icon) 43 | .font(.system (size: 70)) 44 | .foregroundStyle (.white) 45 | 46 | VStack(spacing:16){ 47 | Text(title).font (.title2.bold()) 48 | Text(details) 49 | .multilineTextAlignment(.center) 50 | .font(.footnote) 51 | .foregroundStyle (.secondary) 52 | } 53 | 54 | HoldButton(BStartTitle: BStartTitle, BEndTitle: BEndTitle, trimEnd: $trimEnd, show: $show) 55 | } 56 | .padding (.horizontal) 57 | .frame(width: 350, height: 316) 58 | .background(.deleteCardBG, in: .rect (cornerRadius: 30)) 59 | .overlay { 60 | ReclineLine(trimEnd: $trimEnd) 61 | } 62 | 63 | Button{ 64 | withAnimation { 65 | show = false 66 | } 67 | }label: { 68 | Text("Cancel") 69 | .font(.title2) 70 | .frame(maxWidth: .infinity) 71 | .frame(height: 55) 72 | .background(.deleteCardBG, in: .rect(cornerRadius: 15)) 73 | .padding(.horizontal, 22) 74 | } 75 | .tint(.white) 76 | } 77 | .frame(maxHeight: .infinity, alignment: .bottom) 78 | .offset(y: show ? -20 : 500) 79 | } 80 | } 81 | 82 | #Preview { 83 | DeleteCard(icon: "trash.circle", title: "Delete Account", details: "Are you sure you want to delete your account? All your data will be permanently removed.", BStartTitle: "ACCOUNT DELETED", BEndTitle: "HOLD TO DELETE", show: .constant(false)) 84 | } 85 | 86 | struct HoldButton: View { 87 | 88 | @State var isComplete = false 89 | @State var isSuccess = false 90 | var BStartTitle: String 91 | var BEndTitle: String 92 | @Binding var trimEnd: CGFloat 93 | @Binding var show: Bool 94 | 95 | var body: some View { 96 | VStack{ 97 | ZStack{ 98 | ZStack(alignment: .leading){ 99 | Rectangle() 100 | .foregroundStyle(.red.opacity(0.5)) 101 | 102 | Rectangle() 103 | .frame(maxWidth: isComplete ? .infinity : 0) 104 | .foregroundStyle(isSuccess ? .deleteCardDB : .red) 105 | } 106 | .frame(maxWidth: .infinity) 107 | .frame(height: 55) 108 | .clipShape(.rect(cornerRadius: 15)) 109 | .padding(.horizontal, 8) 110 | 111 | Text(isSuccess ? "ACCOUNT DELETED" : "HOLD TO DELETE") 112 | .bold() 113 | .foregroundStyle(.white) 114 | } 115 | .onLongPressGesture(minimumDuration: 2, maximumDistance: 50) { isPressing in 116 | if isPressing{ 117 | withAnimation(.linear(duration: 2)){ 118 | isComplete = true 119 | trimEnd = 1 120 | } 121 | }else{ 122 | DispatchQueue.main.asyncAfter(deadline: .now() + 0.1){ 123 | if !isSuccess{ 124 | withAnimation { 125 | isComplete = false 126 | trimEnd = 0 127 | } 128 | } 129 | } 130 | } 131 | } perform: { 132 | withAnimation { 133 | isSuccess = true 134 | trimEnd = 1 135 | 136 | DispatchQueue.main.asyncAfter(deadline: .now() + 1.25){ 137 | withAnimation { 138 | show = false 139 | } 140 | } 141 | } 142 | } 143 | } 144 | } 145 | } 146 | 147 | struct ReclineLine: View { 148 | @Binding var trimEnd: CGFloat 149 | var body: some View { 150 | RoundedRectangle(cornerRadius: 30) 151 | .trim(from: 0.5 - trimEnd / 2, to: 0.5 + trimEnd / 2) 152 | .stroke(lineWidth: 1) 153 | .frame(width: 313, height: 347) 154 | .foregroundStyle(.red) 155 | .rotationEffect(.degrees(90)) 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /IOS17-Swift/View/DropDownView/DropDownView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DropDownView.swift 3 | // IOS17-Swift 4 | // 5 | // Created by xqsadness on 14/11/24. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct DropDown: View { 11 | 12 | @State private var selection: String? 13 | @State private var selection2: String? 14 | 15 | var body: some View { 16 | ScrollView{ 17 | VStack(spacing: 15){ 18 | DropDownView( 19 | hint: "Movies", 20 | options: ["Batman", "Superman", "Spider-Man", "Wonder Woman", "Aquaman", "Black Panther"], 21 | anchor: .bottom, 22 | height: 40, 23 | selection: $selection 24 | ) 25 | 26 | DropDownView( 27 | hint: "Artists", 28 | options: ["Joji","The weeknd", "Billie Eilish", "Taylor Swift"], 29 | anchor: .bottom, 30 | height: 40, 31 | selection: $selection2 32 | ) 33 | } 34 | .padding(.horizontal) 35 | .padding(.top, 50) 36 | } 37 | } 38 | } 39 | 40 | struct DropDownView: View { 41 | /// Customization Properties 42 | var hint: String 43 | var options: [String] 44 | var anchor: Anchor = .bottom 45 | var height: CGFloat = 50 46 | var cornerRadius: CGFloat = 10 47 | @Binding var selection: String? 48 | //view props 49 | @State private var showOptions: Bool = false 50 | @Environment(\.colorScheme) private var scheme 51 | @SceneStorage("drop_down_zindex") private var index = 1000.0 52 | @State private var zIndex: Double = 1000.0 53 | var body: some View { 54 | GeometryReader { geometry in 55 | let size = geometry.size 56 | 57 | VStack(spacing: 0){ 58 | if showOptions && anchor == .top{ 59 | OptionsView() 60 | } 61 | 62 | HStack(spacing: 0) { 63 | if let selection = selection{ 64 | Text(selection) 65 | .foregroundStyle(.primary) 66 | .font(.callout) 67 | .lineLimit(1) 68 | }else{ 69 | Text(hint) 70 | .foregroundStyle(.gray) 71 | .font(.callout) 72 | .lineLimit(1) 73 | } 74 | 75 | Spacer(minLength: 0) 76 | 77 | Image(systemName: "chevron.down") 78 | .imageScale(.medium) 79 | .foregroundStyle(.gray) 80 | .rotationEffect(.init(degrees: showOptions ? -180 : 0)) 81 | } 82 | .padding(.horizontal, 15) 83 | .frame(width: size.width, height: size.height) 84 | .background(scheme == .dark ? .black : .white) 85 | .contentShape(.rect) 86 | .onTapGesture { 87 | index += 1 88 | zIndex = index 89 | withAnimation(.snappy) { 90 | showOptions.toggle() 91 | } 92 | } 93 | .zIndex(10) 94 | 95 | if showOptions && anchor == .bottom{ 96 | OptionsView() 97 | } 98 | } 99 | .clipped() 100 | .contentShape(.rect) 101 | .background((scheme == .dark ? Color.black : Color.white) 102 | .shadow(.drop(color: selection != nil ? .accent.opacity(0.6) : .text.opacity(0.3), radius: 4)), in: .rect(cornerRadius: cornerRadius)) 103 | .frame(height: size.height, alignment: anchor == .top ? .bottom : .top) 104 | .overlay(alignment: .topLeading){ 105 | if selection != nil{ 106 | Text(hint) 107 | .foregroundStyle(.text.opacity(0.4)) 108 | .font(.caption) 109 | .lineLimit(1) 110 | .padding(.horizontal, 5) 111 | .background(.themeBG) 112 | .cornerRadius(5) 113 | .offset(x: 8, y: -6) 114 | } 115 | } 116 | } 117 | .frame(height: height) 118 | .frame(maxWidth: .infinity, alignment: .leading) 119 | .zIndex(zIndex) 120 | } 121 | 122 | /// Options View 123 | @ViewBuilder 124 | func OptionsView() -> some View { 125 | VStack(spacing: 10) { 126 | ForEach(options, id: \.self) { option in 127 | HStack(spacing: 0) { 128 | Text(option) 129 | .lineLimit(1) 130 | 131 | Spacer(minLength: 0) 132 | 133 | Image(systemName: "checkmark") 134 | .font(.caption) 135 | .opacity(selection == option ? 1 : 0) 136 | } 137 | .foregroundStyle(selection == option ? Color.primary : Color.gray) 138 | .animation(.none, value: selection) 139 | .frame(height: 40) 140 | .contentShape(.rect) 141 | .onTapGesture { 142 | withAnimation(.snappy) { 143 | selection = option 144 | /// Closing Drop Down View 145 | showOptions = false 146 | } 147 | } 148 | } 149 | } 150 | .padding(.horizontal, 15) 151 | .padding(.vertical, 5) 152 | .transition(.move(edge: anchor == .top ? .bottom : .top)) 153 | } 154 | 155 | /// Drop Down Direction 156 | enum Anchor { 157 | case top 158 | case bottom 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /IOS17-Swift/View/DynamicSlider/CustomSlider2.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CustomSlider.swift 3 | // IOS17-Swift 4 | // 5 | // Created by xqsadness on 13/06/2024. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct CustomSlider2: View { 11 | 12 | let width: CGFloat 13 | let height: CGFloat 14 | //horizontal slider or vertical 15 | let axis: Axis 16 | @Binding var thevalue: Double 17 | @State private var dragging: CGFloat = 0 18 | @State private var startDragging: CGFloat = 0 19 | 20 | var body: some View { 21 | ZStack{ 22 | Capsule() 23 | .frame(width: width, height: height) 24 | .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: axis == .horizontal ? .leading : .bottom) 25 | .foregroundStyle(.thinMaterial) 26 | Capsule() 27 | .frame(width: axis == .horizontal ? max(0, dragging + height) : width, height: axis == .vertical ? max(0, -dragging + width) : height) 28 | .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: axis == .horizontal ? .leading : .bottom) 29 | .foregroundStyle(LinearGradient(gradient: Gradient(colors: gradientColors()), startPoint: axis == .horizontal ? .leading : .bottom, endPoint: axis == .horizontal ? .trailing : .top)) 30 | } 31 | .frame(width: width, height: height) 32 | .overlay(alignment: axis == .horizontal ? .leading : .bottom){ 33 | Circle() 34 | .frame(width: axis == .horizontal ? height : width, height: axis == .vertical ? width : height) 35 | .foregroundStyle(.white) 36 | .offset(x: axis == .horizontal ? dragging : 0, y: axis == .vertical ? dragging : 0) 37 | .gesture( 38 | DragGesture() 39 | .onChanged{ ges in 40 | updateDragging(gesture: ges) 41 | thevalue = axis == .horizontal ? Double(dragging / maxDragDistance()) : Double(-dragging / maxDragDistance()) 42 | } 43 | .onEnded{ _ in 44 | startDragging = dragging 45 | } 46 | ) 47 | } 48 | .overlay { 49 | Text("\(thevalue)") 50 | .frame(width: 100) 51 | .offset(x: 100) 52 | } 53 | } 54 | 55 | private func updateDragging(gesture: DragGesture.Value){ 56 | if axis == .horizontal{ 57 | let newValue = startDragging + gesture.translation.width 58 | dragging = newValue <= 0 ? 0 : min(newValue, maxDragDistance()) 59 | }else{ 60 | let newValue = startDragging + gesture.translation.height 61 | dragging = newValue >= 0 ? 0 : max(newValue, -maxDragDistance()) 62 | } 63 | } 64 | 65 | private func maxDragDistance() -> CGFloat{ 66 | return axis == .horizontal ? width - height : height - width 67 | } 68 | 69 | private func gradientColors() -> [Color]{ 70 | let progress = axis == .horizontal ? min(max(dragging / maxDragDistance(), 0), 1) : min(max(-dragging / maxDragDistance(), 0), 1) 71 | let topColor = Color(red: progress, green: 1.0, blue: 1.0 - progress) 72 | let botttomColor = Color(red: progress, green: progress * 0.4, blue: 1.0 - progress) 73 | return [botttomColor, topColor] 74 | } 75 | } 76 | 77 | #Preview { 78 | CustomSlider2(width: 50, height: 300, axis: .vertical, thevalue: .constant(0.5)) 79 | } 80 | -------------------------------------------------------------------------------- /IOS17-Swift/View/DynamicTabbar/DynamicTabbar.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DynamicTabbar.swift 3 | // IOS17-Swift 4 | // 5 | // Created by xqsadness on 19/11/24. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct TabModel: Identifiable { 11 | var id = UUID() 12 | var icon: String 13 | var title: String 14 | } 15 | 16 | struct DynamicTabbar: View { 17 | var taps: [TabModel] = [ 18 | TabModel(icon: "person", title: "Accounts"), 19 | TabModel(icon: "house", title: "Home"), 20 | TabModel(icon: "creditcard", title: "Payments"), 21 | TabModel(icon: "arrow.right.arrow.left", title: "Transfers") 22 | ] 23 | 24 | @State var selectedTab = 0 25 | var body: some View { 26 | TabView(selection: $selectedTab) { 27 | redView() 28 | .tag(0) 29 | blueView() 30 | .containerRelativeFrame(.horizontal, count: 1, spacing: 0) 31 | .tag(1) 32 | greenView() 33 | .containerRelativeFrame(.horizontal, count: 1, spacing: 0) 34 | .tag(2) 35 | grayView() 36 | .containerRelativeFrame(.horizontal, count: 1, spacing: 0) 37 | .tag(3) 38 | } 39 | // GeometryReader { geo in 40 | // HStack(spacing: 0) { 41 | // redView() 42 | // .containerRelativeFrame(.horizontal, count: 1, spacing: 0) 43 | // blueView() 44 | // .containerRelativeFrame(.horizontal, count: 1, spacing: 0) 45 | // greenView() 46 | // .containerRelativeFrame(.horizontal, count: 1, spacing: 0) 47 | // grayView() 48 | // .containerRelativeFrame(.horizontal, count: 1, spacing: 0) 49 | // } 50 | // .offset(x: -geo.size.width * CGFloat(selectedTab)) 51 | // } 52 | .overlay(alignment: .bottom) { 53 | HStack { 54 | ForEach(taps.indices, id: \.self) { tap in 55 | HStack(spacing: 14) { 56 | Image(systemName: taps[tap].icon) 57 | // Now selectedTab is 58 | if selectedTab == tap { 59 | Text(taps[tap].title).bold() 60 | .transition(.scale(scale: 0, anchor: .trailing).combined(with: .opacity)) 61 | } 62 | } 63 | .font(.title2) 64 | .frame(maxWidth: selectedTab == tap ? .infinity : 55) 65 | .frame(height: 55) 66 | .background(Color(.systemGray6), in: .rect(cornerRadius: 12)) 67 | .clipped() 68 | .onTapGesture { 69 | withAnimation { 70 | selectedTab = tap 71 | } 72 | } 73 | } 74 | } 75 | .padding(.horizontal) 76 | } 77 | } 78 | } 79 | 80 | #Preview { 81 | DynamicTabbar() 82 | } 83 | 84 | struct redView: View { 85 | var body: some View { 86 | ZStack { 87 | Color.red.ignoresSafeArea() 88 | Text("YellowView").font(.largeTitle) 89 | } 90 | } 91 | } 92 | struct blueView: View { 93 | var body: some View { 94 | ZStack { 95 | Color.blue.ignoresSafeArea() 96 | Text("YellowView").font(.largeTitle) 97 | } 98 | } 99 | } 100 | struct greenView: View { 101 | var body: some View { 102 | ZStack { 103 | Color.green.ignoresSafeArea() 104 | Text("YellowView").font(.largeTitle) 105 | } 106 | } 107 | } 108 | struct grayView: View { 109 | var body: some View { 110 | ZStack { 111 | Color.gray.ignoresSafeArea() 112 | Text("YellowView").font(.largeTitle) 113 | } 114 | } 115 | } 116 | 117 | struct yellowView: View { 118 | var body: some View { 119 | ZStack { 120 | Color.yellow.ignoresSafeArea() 121 | Text("YellowView").font(.largeTitle) 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /IOS17-Swift/View/FloatingActionButton/FloatingActionButtonView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FloatingActionButton.swift 3 | // IOS17-Swift 4 | // 5 | // Created by iamblue on 08/04/2024. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct FloatingActionButton: View { 11 | @State private var colors: [Color] = [Color.red, Color.yellow, Color.blue, Color.gray, Color.black, Color.pink, Color.cyan, Color.purple, Color.green] 12 | var body: some View { 13 | ScrollView(.vertical){ 14 | LazyVStack{ 15 | ForEach(colors, id: \.self){ color in 16 | RoundedRectangle(cornerRadius: 15) 17 | .fill(color.gradient) 18 | .frame(height: 200) 19 | } 20 | } 21 | .padding(15) 22 | } 23 | .overlay(alignment: .bottomTrailing){ 24 | FloatingButton { 25 | FloatingAction(symbol: "tray.full.fill") { 26 | print("tray") 27 | } 28 | FloatingAction(symbol: "lasso.badge.sparkles") { 29 | print("lasso") 30 | } 31 | FloatingAction(symbol: "square.and.arrow.up.fill") { 32 | print("square") 33 | } 34 | } label: { isExpanded in 35 | Image(systemName: "plus") 36 | .font(.title3) 37 | .fontWeight(.semibold) 38 | .foregroundStyle(.white) 39 | .rotationEffect(.init(degrees: isExpanded ? 45 : 0)) 40 | .scaleEffect(1.02) 41 | .frame(maxWidth: .infinity, maxHeight: .infinity) 42 | .background(.black, in: .circle) 43 | //scaling effect when expanded 44 | .scaleEffect(isExpanded ? 0.9 : 1) 45 | } 46 | .padding() 47 | } 48 | } 49 | } 50 | 51 | #Preview { 52 | FloatingActionButton() 53 | } 54 | -------------------------------------------------------------------------------- /IOS17-Swift/View/FloatingBottomSheets/FloatingBottomSheetsView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FloatingBottomSheetsView.swift 3 | // IOS17-Swift 4 | // 5 | // Created by xqsadness on 29/8/24. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct FloatingBottomSheetsView: View { 11 | //View props 12 | @State private var showSheet: Bool = false 13 | var body: some View { 14 | NavigationStack{ 15 | VStack{ 16 | Button("Show sheet"){ 17 | showSheet.toggle() 18 | } 19 | } 20 | .navigationTitle("Floating bottom sheet") 21 | } 22 | .floatingBottomSheet(isPresented: $showSheet) { 23 | SheetView( 24 | title: "Replace Existing Folder?", 25 | content: "Lorem Ipsum is simply dummy text of the printing and typesetting industry .", 26 | image: .init( 27 | content: "questionmark.folder.fill", 28 | tint: .blue, 29 | foreground: .white 30 | ), 31 | button1: .init( 32 | content: "Replace", 33 | tint: .blue, 34 | foreground: .white 35 | ), 36 | button2: .init( 37 | content: "Cancel", 38 | tint: Color.primary.opacity(0.08), 39 | foreground: Color.primary 40 | ) 41 | ) 42 | .presentationDetents ([.height(330)]) 43 | } 44 | } 45 | } 46 | 47 | #Preview { 48 | FloatingBottomSheetsView() 49 | } 50 | 51 | /// Sample View 52 | struct SheetView: View { 53 | var title: String 54 | var content: String 55 | var image: Config 56 | var button1: Config 57 | var button2: Config? 58 | var body: some View { 59 | VStack(spacing: 15) { 60 | Image(systemName: image.content) 61 | .font(.title) 62 | .foregroundStyle(image.foreground) 63 | .frame(width: 65, height: 65) 64 | .background(image.tint.gradient, in: .circle) 65 | 66 | Text(title) 67 | .font(.title3.bold()) 68 | 69 | Text (content) 70 | .font(.callout) 71 | .multilineTextAlignment (.center) 72 | .lineLimit(2) 73 | .foregroundStyle(.gray) 74 | 75 | ButtonView(button1) 76 | 77 | if let button2{ 78 | ButtonView(button2) 79 | } 80 | } 81 | .padding([.horizontal, .bottom], 15) 82 | .background{ 83 | RoundedRectangle(cornerRadius: 15) 84 | .fill(.background) 85 | .padding(.top, 30) 86 | } 87 | .shadow(color: .black.opacity(0.12) ,radius: 8) 88 | .padding(.horizontal) 89 | } 90 | 91 | @ViewBuilder 92 | func ButtonView(_ config: Config) -> some View{ 93 | Button{ 94 | 95 | }label: { 96 | Text(config.content) 97 | .fontWeight(.bold) 98 | .foregroundStyle(config.foreground) 99 | .padding(.vertical, 10) 100 | .frame(maxWidth: .infinity) 101 | .background(config.tint.gradient, in: .rect(cornerRadius: 10)) 102 | } 103 | } 104 | 105 | struct Config { 106 | var content: String 107 | var tint: Color 108 | var foreground: Color 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /IOS17-Swift/View/FloatingBottomSheets/Helper/FloatingSheet.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FloatingSheet.swift 3 | // IOS17-Swift 4 | // 5 | // Created by xqsadness on 29/8/24. 6 | // 7 | 8 | import SwiftUI 9 | 10 | extension View{ 11 | @ViewBuilder 12 | func floatingBottomSheet(isPresented: Binding, onDismiss: @escaping () -> () = {}, @ViewBuilder content: @escaping () -> Content) -> some View{ 13 | self 14 | .sheet(isPresented: isPresented, onDismiss: onDismiss) { 15 | content() 16 | .presentationCornerRadius(0) 17 | .presentationBackground(.clear) 18 | .presentationDragIndicator(.hidden) 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /IOS17-Swift/View/FloatingTabBar/Model/TabFloating.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TabFloating.swift 3 | // IOS17-Swift 4 | // 5 | // Created by xqsadness on 29/8/24. 6 | // 7 | 8 | import SwiftUI 9 | 10 | enum TabFloating: String, CaseIterable{ 11 | case home = "house" 12 | case search = "magnifyingglass" 13 | case notifications = "bell" 14 | case settings = "gearshape" 15 | 16 | var title: String{ 17 | switch self{ 18 | case .home: "Home" 19 | case .search: "Search" 20 | case .notifications: "Notifications" 21 | case .settings: "Settings" 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /IOS17-Swift/View/FloatingTabBar/View/CustomTabbar.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CustomTabbar.swift 3 | // IOS17-Swift 4 | // 5 | // Created by xqsadness on 29/8/24. 6 | // 7 | 8 | import SwiftUI 9 | import CoreFoundation 10 | 11 | //MARK: Available in xcode 16 12 | 13 | //struct CustomTabbar: View { 14 | // var activeForeground:Color = .white 15 | // var activeBackground:Color = .blue 16 | // @Binding var activeTab: TabFloating 17 | // //For matched geometry effect 18 | // @Namespace private var animation 19 | // //view props 20 | // @State private var tabLocation: CGRect = .zero 21 | // var body: some View { 22 | // let status = activeTab == .home || activeTab == .search 23 | // 24 | // HStack(spacing: !status ? 0 : 12){ 25 | // HStack(spacing: 0) { 26 | // ForEach(TabFloating.allCases, id: \.rawValue) { tab in 27 | // Button { 28 | // activeTab = tab 29 | // } label: { 30 | // HStack(spacing: 5) { 31 | // Image(systemName: "\(tab.rawValue)") 32 | // .font(.title3) 33 | // .frame(width: 30, height: 30) 34 | // 35 | // if activeTab == tab { 36 | // Text("\(tab.title)") 37 | // .font(.caption) 38 | // .fontWeight(.semibold) 39 | // .lineLimit(1) 40 | // } 41 | // } 42 | // .foregroundStyle(activeTab == tab ? activeForeground : .gray) 43 | // .padding(.vertical,2) 44 | // .padding(.leading,10) 45 | // .padding(.trailing,15) 46 | // .contentShape(.rect) 47 | // .background{ 48 | // if activeTab == tab{ 49 | // Capsule() 50 | // .fill(activeBackground.gradient) 51 | // //onGeometryChange available in xcode 16 52 | // .onGeometryChange(for: CGRect.self, of: { $0.frame(in: .named("TABBARVIEW")) 53 | // }, action: { newValue in 54 | // tabLocation = newValue 55 | // }) 56 | // .matchedGeometryEffect(id: "ACTIVETABFLOAT", in: animation) 57 | // } 58 | // } 59 | // } 60 | // .buttonStyle(.plain) 61 | // } 62 | // } 63 | // .background(alignment: .leading){ 64 | // Capsule() 65 | // .fill(activeBackground.gradient) 66 | // .frame(width: tabLocation.width, height: tabLocation.height) 67 | // .offset(x: tabLocation.minX) 68 | // } 69 | // .coordinateSpace(.named("TABBARVIEW")) 70 | // .padding(.horizontal, 5) 71 | // .frame(height: 45) 72 | // .background( 73 | // .background 74 | // .shadow(.drop(color: .black.opacity(0.08), radius: 5, x: 5, y: 5)) 75 | // .shadow(.drop(color: .black.opacity(0.06), radius: 5, x: -5, y: -5)), in: .capsule 76 | // ) 77 | // .zIndex(10) 78 | // 79 | // Button{ 80 | // 81 | // }label: { 82 | // Image(systemName: activeTab == .home ? "person.fill" : "slider.vertical.3") 83 | // .font(.title3) 84 | // .frame(width: 42, height: 42) 85 | // .foregroundStyle(activeForeground) 86 | // .background(activeBackground.gradient) 87 | // .clipShape(.circle) 88 | // } 89 | // .allowsHitTesting(status) 90 | // .offset(x: status ? 0 : -20) 91 | // .padding(.leading, status ? 0 : -42) 92 | // } 93 | // .padding(.bottom, 5) 94 | // .animation(.smooth(duration: 0.3), value: activeTab) 95 | // } 96 | //} 97 | // 98 | ////#Preview { 99 | //// FloatingTabBarView() 100 | ////} 101 | -------------------------------------------------------------------------------- /IOS17-Swift/View/FloatingTabBar/View/FloatingTabBarView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FloatingTabBarView.swift 3 | // IOS17-Swift 4 | // 5 | // Created by xqsadness on 29/8/24. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct HomeView: View { 11 | var body: some View { 12 | NavigationStack{ 13 | ScrollView{ 14 | LazyVStack(spacing: 12){ 15 | ForEach(1...50, id: \.self){ _ in 16 | RoundedRectangle(cornerRadius: 12) 17 | .fill(.background) 18 | .frame(height: 55) 19 | } 20 | } 21 | .padding(15) 22 | } 23 | .navigationTitle("Floating tabbar") 24 | .background(Color.primary.opacity(0.07)) 25 | .safeAreaPadding(.bottom, 60) 26 | } 27 | } 28 | } 29 | 30 | struct FloatingTabBarView: View { 31 | // view props 32 | @State private var activeTab: TabFloating = .home 33 | @State private var isTabbarHidden: Bool = false 34 | var body: some View { 35 | ZStack(alignment: .bottom) { 36 | TabView(selection: $activeTab) { 37 | HomeView() 38 | .tag(TabFloating.home) 39 | .background{ 40 | if !isTabbarHidden{ 41 | HideTabBar{ 42 | isTabbarHidden = true 43 | } 44 | } 45 | } 46 | 47 | Text("Search") 48 | .tag(TabFloating.search) 49 | 50 | Text("Notifications") 51 | .tag(TabFloating.notifications) 52 | 53 | Text("Settings") 54 | .tag(TabFloating.settings) 55 | } 56 | 57 | //MARK: Available in xcode 16 58 | // CustomTabbar(activeTab: $activeTab) 59 | } 60 | } 61 | } 62 | 63 | struct HideTabBar: UIViewRepresentable { 64 | init(result: @escaping () -> Void) { 65 | UITabBar.appearance().isHidden = true 66 | self.result = result 67 | } 68 | 69 | var result: () -> () 70 | 71 | func makeUIView(context: Context) -> UIView { 72 | let view = UIView(frame: .zero) 73 | view.backgroundColor = .clear 74 | 75 | DispatchQueue.main.async { 76 | if let tabController = view.tabController { 77 | UITabBar.appearance().isHidden = false 78 | tabController.tabBar.isHidden = true 79 | result() 80 | } 81 | } 82 | 83 | return view 84 | } 85 | 86 | func updateUIView(_ uiView: UIView, context: Context) {} 87 | } 88 | 89 | extension UIView { 90 | var tabController: UITabBarController? { 91 | if let controller = sequence (first: self, next: { 92 | $0.next 93 | }).first (where: { $0 is UITabBarController }) as? UITabBarController { 94 | return controller 95 | } 96 | return nil 97 | } 98 | } 99 | 100 | #Preview { 101 | FloatingTabBarView() 102 | } 103 | -------------------------------------------------------------------------------- /IOS17-Swift/View/ImageParalax/ImageParalaxView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageParalaxView.swift 3 | // IOS17-Swift 4 | // 5 | // Created by xasadness on 10/01/2024. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ImageParalaxView: View { 11 | @State var valueTranslation: CGSize = .zero 12 | @State var isDragging = false 13 | 14 | var body: some View { 15 | ZStack{ 16 | Image(.stars) 17 | .resizable() 18 | .scaledToFill() 19 | .frame(width: 250, height: 550) 20 | .offset(x: valueTranslation.width / 10, y: valueTranslation.height / 10) 21 | 22 | Image(.planet) 23 | .resizable() 24 | .scaledToFill() 25 | .frame(width: 200, height: 200) 26 | .offset(x: valueTranslation.width / 5, y: valueTranslation.height / 5) 27 | 28 | Circle() 29 | .frame(width: 60, height: 60) 30 | .blur(radius: 50) 31 | .offset(x: valueTranslation.width / 1.5, y: valueTranslation.height / 1.5) 32 | } 33 | .offset(x: valueTranslation.width / 10, y: valueTranslation.height / 10) 34 | .frame(width: 300, height: 400) 35 | .background(Color(hex: "161228")) 36 | .clipShape(RoundedRectangle(cornerRadius: 30)) 37 | .rotation3DEffect( 38 | .degrees(isDragging ? 10 : 0), 39 | axis: (x: -valueTranslation.height, y: valueTranslation.width, z: 0.0) 40 | ) 41 | .gesture(DragGesture() 42 | .onChanged({ value in 43 | withAnimation { 44 | valueTranslation = value.translation 45 | isDragging = true 46 | } 47 | }) 48 | .onEnded({ value in 49 | withAnimation { 50 | valueTranslation = .zero 51 | isDragging = false 52 | } 53 | }) 54 | ) 55 | } 56 | } 57 | 58 | #Preview { 59 | ImageParalaxView() 60 | } 61 | -------------------------------------------------------------------------------- /IOS17-Swift/View/InlineToasts/Helpers/InlineToast.swift: -------------------------------------------------------------------------------- 1 | // 2 | // InlineToast.swift 3 | // IOS17-Swift 4 | // 5 | // Created by xqsadness on 25/4/25. 6 | // 7 | 8 | import SwiftUI 9 | 10 | import SwiftUI 11 | 12 | extension View { 13 | func inlineToast(config: InlineToastConfig, isPresented: Bool, alignment: Alignment) -> some View { 14 | VStack(spacing: 10) { 15 | if config.anchor == .bottom { 16 | self 17 | .compositingGroup() 18 | .frame(maxWidth: .infinity, alignment: alignment) 19 | } 20 | 21 | if isPresented{ 22 | InlineToastView(config: config) 23 | .transition(CustomTransitionInlineToast(anchor: config.animationAnchor)) 24 | } 25 | 26 | //Inline Toast View 27 | 28 | if config.anchor == .top{ 29 | self 30 | .compositingGroup() 31 | .frame(maxWidth: .infinity, alignment: alignment) 32 | } 33 | } 34 | .clipped() 35 | } 36 | } 37 | 38 | fileprivate struct CustomTransitionInlineToast: Transition { 39 | var anchor: InlineToastConfig.InlineToastAnchor 40 | func body(content: Content, phase: TransitionPhase) -> some View { 41 | content 42 | .visualEffect { [phase] content, proxy in 43 | content 44 | .offset(y: offset(proxy, phase: phase)) 45 | } 46 | // Clipping the view so that it won't appear on top of other views 47 | .clipped() 48 | } 49 | 50 | nonisolated func offset(_ proxy: GeometryProxy, phase: TransitionPhase) -> CGFloat { 51 | let height = proxy.size.height + 10 52 | return anchor == .top ? (phase.isIdentity ? 0 : -height) : (phase.isIdentity ? 0 : height) 53 | } 54 | } 55 | 56 | struct InlineToastConfig { 57 | var icon: String 58 | var title: String 59 | var subTitle: String 60 | var tint: Color 61 | var anchor: InlineToastAnchor = .top 62 | var animationAnchor: InlineToastAnchor = .top 63 | var actionIcon: String 64 | var actionHandler: () -> () = { } 65 | 66 | enum InlineToastAnchor { 67 | case top 68 | case bottom 69 | } 70 | } 71 | 72 | /// Custom Inline Toast View 73 | struct InlineToastView: View { 74 | var config: InlineToastConfig 75 | var body: some View { 76 | HStack(spacing: 15) { 77 | Image(systemName: config.icon) 78 | .font(.title2) 79 | .foregroundStyle(config.tint) 80 | 81 | VStack(alignment: .leading, spacing: 5) { 82 | Text(config.title) 83 | .font(.callout) 84 | .fontWeight(.semibold) 85 | 86 | if !config.subTitle.isEmpty { 87 | Text(config.subTitle) 88 | .font(.caption2) 89 | .foregroundStyle(.gray) 90 | } 91 | } 92 | 93 | Spacer(minLength: 0) 94 | 95 | /// Action Button 96 | Button(action: config.actionHandler) { 97 | Image(systemName: config.actionIcon) 98 | .foregroundStyle(.gray) 99 | .contentShape(.rect) 100 | } 101 | } 102 | .padding() 103 | .background{ 104 | ZStack{ 105 | Rectangle() 106 | .fill(.background) 107 | 108 | HStack(spacing: 0){ 109 | Rectangle() 110 | .fill(config.tint) 111 | .frame(width: 5) 112 | 113 | Rectangle() 114 | .fill(config.tint.opacity(0.15)) 115 | } 116 | } 117 | } 118 | .contentShape(.rect) 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /IOS17-Swift/View/InlineToasts/HomeInlineToastView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // InlineToastView.swift 3 | // IOS17-Swift 4 | // 5 | // Created by xqsadness on 25/4/25. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct HomeInlineToastView: View { 11 | 12 | @State private var showToast1: Bool = false 13 | @State private var showToast2: Bool = false 14 | 15 | var body: some View { 16 | NavigationStack { 17 | // Sample Toasts 18 | let toast1 = InlineToastConfig( 19 | icon: "exclamationmark.circle.fill", 20 | title: "Incorrect password :(", 21 | subTitle: "Oops! That didn't match. Give it another shot.", 22 | tint: .red, 23 | anchor: .top, 24 | animationAnchor: .top, 25 | actionIcon: "xmark" 26 | ) { 27 | showToast1 = false 28 | } 29 | 30 | let toast2 = InlineToastConfig( 31 | icon: "checkmark.circle.fill", 32 | title: "Password Reset Email Sent!", 33 | subTitle: "", 34 | tint: .green, 35 | anchor: .top, 36 | animationAnchor: .top, 37 | actionIcon: "xmark" 38 | ) { 39 | showToast2 = false 40 | } 41 | 42 | VStack(alignment: .leading, spacing: 15) { 43 | Text("Email Address") 44 | .font(.caption) 45 | .foregroundStyle(.gray) 46 | 47 | TextField("", text: .constant("")) 48 | 49 | Text("Password") 50 | .font(.caption) 51 | .foregroundStyle(.gray) 52 | 53 | SecureField("", text: .constant("")) 54 | 55 | VStack(alignment: .trailing, spacing: 20) { 56 | Button { 57 | showToast1.toggle() 58 | } label: { 59 | Text("Login in to Account") 60 | .frame(maxWidth: .infinity) 61 | .padding(.vertical, 2) 62 | } 63 | .tint(.blue) 64 | .buttonBorderShape(.roundedRectangle(radius: 10)) 65 | .buttonStyle(.borderedProminent) 66 | 67 | Button("Forgot Password") { 68 | showToast2.toggle() 69 | } 70 | } 71 | .inlineToast(config: toast1, isPresented: showToast1, alignment: .center) 72 | .inlineToast(config: toast2, isPresented: showToast2, alignment: .center) 73 | .padding(.top, 10) 74 | 75 | Spacer() 76 | } 77 | .textFieldStyle(.roundedBorder) 78 | .padding(15) 79 | .navigationTitle("Login") 80 | .animation(.smooth(duration: 0.35, extraBounce: 0), value: showToast1) 81 | .animation(.smooth(duration: 0.35, extraBounce: 0), value: showToast2) 82 | } 83 | } 84 | } 85 | 86 | #Preview { 87 | HomeInlineToastView() 88 | } 89 | -------------------------------------------------------------------------------- /IOS17-Swift/View/LimitedTextField /LimitedTextFieldHome .swift: -------------------------------------------------------------------------------- 1 | // 2 | // LimitedTextFieldHome .swift 3 | // IOS17-Swift 4 | // 5 | // Created by xqsadness on 01/04/2024. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct LimitedTextFieldHome: View { 11 | //View props 12 | @State private var text: String = "" 13 | var body: some View { 14 | VStack{ 15 | LimitedTextField( 16 | config: .init( 17 | limit: 40, 18 | tint: .secondary, 19 | autoResizes: true 20 | ), 21 | hint: "Type here", 22 | value: $text 23 | ) 24 | .autocorrectionDisabled() 25 | .frame(maxHeight: 150) 26 | .padding() 27 | } 28 | } 29 | } 30 | 31 | #Preview { 32 | LimitedTextFieldHome() 33 | } 34 | -------------------------------------------------------------------------------- /IOS17-Swift/View/OnBoardingAnimation/Intro.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Intro.swift 3 | // IOS17-Swift 4 | // 5 | // Created by xqsadness on 04/07/2024. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct Intro: Identifiable{ 11 | var id = UUID().uuidString 12 | var title: String 13 | var subTitle: String 14 | var description: String 15 | var pic: String 16 | var color: LinearGradient 17 | var offset: CGSize = .zero 18 | } 19 | -------------------------------------------------------------------------------- /IOS17-Swift/View/OnBoardingAnimation/LiquidShapeAnimation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftUIView.swift 3 | // IOS17-Swift 4 | // 5 | // Created by xqsadness on 04/07/2024. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct LiquidShape: Shape { 11 | var offset: CGSize 12 | var curvePoint: CGFloat 13 | // Multiple Animatable Data... 14 | // Animating Shapes... 15 | var animatableData: AnimatablePair { 16 | get { 17 | return AnimatablePair (offset.animatableData, curvePoint) 18 | } set { 19 | offset.animatableData = newValue.first 20 | curvePoint = newValue.second 21 | } 22 | } 23 | 24 | func path (in rect: CGRect) -> Path { 25 | return Path { path in 26 | // when user moves left... 27 | // increasing size both in top and bottom.... 28 | // so it will create a liquid swipe effect... 29 | let width = rect.width + (-offset.width > 0 ? offset.width: 0) 30 | // First Constructing Rectangle Shape... 31 | path.move(to: CGPoint(x: 0, y: 0)) 32 | path.addLine(to: CGPoint(x: rect.width, y: 0)) 33 | path.addLine(to: CGPoint(x: rect.width, y: rect.height)) 34 | path.addLine(to: CGPoint(x: 0, y: rect.height)) 35 | // Now Constructing Curve Shape.... 36 | // from 37 | let from = 80 + (offset.width) 38 | path.move(to: CGPoint(x: rect.width, y: from > 0 ? 80 : from)) 39 | //Adding Height 40 | var to = 180 + (offset.height) + (-offset.width) 41 | to = to < 180 ? 180 : to 42 | //Mid Between 80-180 43 | let mid: CGFloat = 80 + ((to - 80) / 2) 44 | path.addCurve(to: CGPoint(x: rect.width, y: to), control1: CGPoint(x: width - curvePoint, y: mid), control2: CGPoint(x: width - curvePoint, y: mid)) 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /IOS17-Swift/View/OnBoardingAnimation/ViewExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewExtension.swift 3 | // IOS17-Swift 4 | // 5 | // Created by xqsadness on 04/07/2024. 6 | // 7 | 8 | import SwiftUI 9 | 10 | extension View{ 11 | func getRect() -> CGRect{ 12 | return UIScreen.main.bounds 13 | } 14 | 15 | func getSafeArea() -> UIEdgeInsets{ 16 | guard let screen = UIApplication.shared.connectedScenes.first as? UIWindowScene else{ 17 | return .zero 18 | } 19 | 20 | guard let safeArea = screen.windows.first?.safeAreaInsets else{ 21 | return .zero 22 | } 23 | 24 | return safeArea 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /IOS17-Swift/View/Other/AnimatedSFSymbol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AnimatedSFSymbol.swift 3 | // IOS17-Swift 4 | // 5 | // Created by xqsadness on 30/11/2023. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct AnimatedSFSymbol: View { 11 | @State private var animateSymbol: Bool = false 12 | var body: some View { 13 | Image(systemName: "suit.heart.fill") 14 | .font(.largeTitle) 15 | .tint(.red) 16 | .symbolEffect(.bounce,options: .default ,value: animateSymbol) 17 | .onTapGesture { 18 | animateSymbol.toggle() 19 | } 20 | } 21 | } 22 | 23 | #Preview { 24 | AnimatedSFSymbol() 25 | } 26 | -------------------------------------------------------------------------------- /IOS17-Swift/View/Other/Animation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Animation.swift 3 | // IOS17-Swift 4 | // 5 | // Created by xqsadness on 30/11/2023. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct AnimationView: View { 11 | @State var isShow = false 12 | var body: some View { 13 | VStack{ 14 | if isShow{ 15 | Rectangle() 16 | .fill(.red.gradient) 17 | .frame(width: 150,height: 150) 18 | .transition(MyTransition()) 19 | } 20 | 21 | Button("ShowView") { 22 | withAnimation(.init(MyAnimation())){ 23 | isShow.toggle() 24 | } 25 | } 26 | } 27 | } 28 | } 29 | 30 | #Preview { 31 | AnimationView() 32 | } 33 | 34 | struct MyTransition: Transition { 35 | func body(content: Content, phase: TransitionPhase) -> some View { 36 | content 37 | .rotation3DEffect( 38 | .init(degrees: phase.value * (phase == .willAppear ? 90 : -90)), 39 | axis: (x: 1.0, y: 0.0, z: 0.0) 40 | ) 41 | } 42 | } 43 | 44 | struct MyAnimation: CustomAnimation{ 45 | var duration: CGFloat = 1 46 | func animate(value: V, time: TimeInterval, context: inout AnimationContext) -> V? where V : VectorArithmetic { 47 | if time > duration {return nil } 48 | return value.scaled(by: time) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /IOS17-Swift/View/Other/CustomTextField.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CustomTextField.swift 3 | // IOS17-Swift 4 | // 5 | // Created by xqsadness on 9/9/24. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct CustomSearchField: View { 11 | @State var show = false 12 | @State var text = "" 13 | @FocusState var Istyping: Bool 14 | var body: some View { 15 | ZStack(alignment: .leading){ 16 | RoundedRectangle (cornerRadius: show ? 15: 30) 17 | .foregroundStyle(.thinMaterial) 18 | .shadow(color: .black.opacity (0.1), radius: 0, x: 0, y: 6) 19 | HStack{ 20 | Image(systemName: show ? "xmark": "magnifyingglass") 21 | .font(.title2) 22 | .foregroundStyle (.primary.opacity (0.5)) 23 | .contentTransition(.symbolEffect) 24 | .onTapGesture { 25 | withAnimation { 26 | text = "" 27 | show.toggle() 28 | Istyping.toggle() 29 | } 30 | } 31 | 32 | TextField("Type here", text: $text) 33 | .focused($Istyping) 34 | .opacity(show ? 1 : 0) 35 | } 36 | .padding(.leading, 11) 37 | } 38 | .frame(width: show ? 300: 50, height: 50) 39 | .frame(maxWidth: .infinity, alignment: .leading) 40 | .frame(height: 70).clipped () 41 | } 42 | } 43 | 44 | #Preview { 45 | CustomTextField() 46 | } 47 | 48 | struct CustomTextField: View { 49 | @State private var name: String = "" 50 | @State private var email: String = "" 51 | var body: some View { 52 | VStack(spacing: 50){ 53 | InfoField(title: "Name", text: $name) 54 | InfoField(title: "Email", text: $email) 55 | 56 | CustomSearchField() 57 | } 58 | .padding(.horizontal) 59 | } 60 | } 61 | 62 | struct InfoField: View { 63 | let title: String 64 | @Binding var text: String 65 | @FocusState var isTying: Bool 66 | var body: some View { 67 | ZStack(alignment: .leading){ 68 | TextField("", text: $text) 69 | .padding(.leading) 70 | .frame(height: 55) 71 | .focused($isTying) 72 | .background(isTying ? .blue : Color.primary, in: RoundedRectangle(cornerRadius: 14).stroke(lineWidth: 2)) 73 | 74 | Text(title) 75 | .padding(.horizontal, 5) 76 | .background(.rev.opacity(isTying || !text.isEmpty ? 1 : 0)) 77 | .foregroundStyle(isTying ? .blue : Color.primary) 78 | .padding(.leading) 79 | .offset(y: isTying || !text.isEmpty ? -27 : 0) 80 | .onTapGesture { 81 | isTying.toggle() 82 | } 83 | } 84 | .animation(.linear(duration: 0.2), value: isTying) 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /IOS17-Swift/View/Other/PalettePickerView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PalettePickerView.swift 3 | // IOS17-Swift 4 | // 5 | // Created by xqsadness on 08/12/2023. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct PalettePickerView: View { 11 | @State private var selectedColor = MyColors.red 12 | @State private var contrast: Double = 1 13 | @State private var clear = false 14 | 15 | var body: some View { 16 | VStack{ 17 | Image("test-img") 18 | .resizable() 19 | .scaledToFit() 20 | .modifier(WithModifiers(selectedColor: selectedColor, contrast: contrast,clear: clear)) 21 | .padding(.horizontal) 22 | .contextMenu{ 23 | clearApply 24 | slider 25 | pickerPalette 26 | } 27 | } 28 | } 29 | } 30 | 31 | #Preview { 32 | PalettePickerView() 33 | } 34 | 35 | enum MyColors: String, CaseIterable, Identifiable { 36 | case red, orange, yellow, green, blue, indigo, black 37 | var id: Self {self } 38 | var color: Color { 39 | switch self { 40 | case .red: .red 41 | case .orange: .orange 42 | case .yellow: .yellow 43 | case .green: .green 44 | case .blue: .blue 45 | case .indigo: .indigo 46 | case .black: .black 47 | } 48 | } 49 | } 50 | 51 | extension PalettePickerView{ 52 | 53 | private var pickerPalette: some View{ 54 | // Menu(selectedColor.rawValue) { 55 | Picker("My Color", selection: $selectedColor) { 56 | ForEach (MyColors.allCases) { myColor in 57 | Label(myColor.rawValue, systemImage: "square") 58 | .tint(myColor.color) 59 | } 60 | } 61 | .paletteSelectionEffect(.symbolVariant(.fill)) 62 | .pickerStyle(.palette) 63 | // } 64 | // .buttonStyle(.borderedProminent) 65 | // .tint (selectedColor.color.gradient) 66 | } 67 | 68 | private var slider: some View{ 69 | Slider(value: $contrast, in: 0.5...3.0){ 70 | Text("Contrast: \(contrast, format: .number)") 71 | } 72 | } 73 | 74 | private var clearApply: some View{ 75 | ControlGroup{ 76 | Button{ 77 | withAnimation { 78 | clear = true 79 | } 80 | }label: { 81 | Label("Clear", systemImage: "x.circle") 82 | } 83 | 84 | Button{ 85 | withAnimation { 86 | clear = false 87 | } 88 | }label: { 89 | Label("Apply", systemImage: "checkmark") 90 | } 91 | } 92 | } 93 | } 94 | 95 | struct WithModifiers: ViewModifier{ 96 | let selectedColor: MyColors 97 | let contrast: Double 98 | let clear: Bool 99 | 100 | func body(content: Content) -> some View { 101 | if clear{ 102 | content 103 | }else{ 104 | content 105 | .colorMultiply(selectedColor.color) 106 | .contrast(contrast) 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /IOS17-Swift/View/Other/ScrollTransition.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ScrollTransition.swift 3 | // IOS17-Swift 4 | // 5 | // Created by xqsadness on 30/11/2023. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ScrollTransitionsView: View { 11 | var body: some View { 12 | ScrollView(.vertical){ 13 | LazyVStack{ 14 | ForEach(1...30, id: \.self){ _ in 15 | Rectangle() 16 | .fill(.red.gradient) 17 | .frame(height: 145) 18 | .scrollTransition(topLeading: .interactive, bottomTrailing: .interactive) { view, phase in view 19 | .opacity(1 - ( phase.value < 0 ? -phase.value : phase.value)) 20 | .scaleEffect(phase.isIdentity ? 1 : 0.75) 21 | .blur(radius: phase.isIdentity ? 0 : 10) 22 | } 23 | } 24 | } 25 | } 26 | .padding() 27 | } 28 | } 29 | 30 | #Preview { 31 | ScrollTransitionsView() 32 | } 33 | -------------------------------------------------------------------------------- /IOS17-Swift/View/ParallaxCarouselScroll/ParallaxCarouselScrollView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ParallaxCarouselScrollView.swift 3 | // IOS17-Swift 4 | // 5 | // Created by xqsadness on 01/12/2023. 6 | // 7 | 8 | import SwiftUI 9 | import Kingfisher 10 | 11 | struct ParallaxCarouselScrollView: View { 12 | @StateObject var parallaxCarouselScrollViewModel = ParallaxCarouselScrollViewModel() 13 | 14 | var body: some View { 15 | VStack(spacing: 15){ 16 | Text("Wallpaper") 17 | .font(.title) 18 | .bold() 19 | 20 | GeometryReader{ geo in 21 | let size = geo.size 22 | 23 | if parallaxCarouselScrollViewModel.isLoading{ 24 | VStack{ 25 | Text("Loading data ...") 26 | ProgressView() 27 | } 28 | .frame(maxWidth: .infinity,maxHeight: .infinity ,alignment: .center) 29 | }else{ 30 | ScrollView(.horizontal){ 31 | HStack(spacing: 5){ 32 | 33 | ForEach(parallaxCarouselScrollViewModel.imageData, id: \.id){ imageData in 34 | 35 | GeometryReader{ proxy in 36 | let cardSize = proxy.size 37 | // let minX = proxy.frame(in: .scrollView).minX - 30.0 38 | let minX = min((proxy.frame(in: .scrollView).minX - 30.0) * 1.4, size.width * 1.4) 39 | 40 | KFImage(URL(string: imageData.urls.raw)) 41 | .placeholder { 42 | Image("ImageDefault") 43 | .resizable() 44 | .aspectRatio(contentMode: .fill) 45 | .offset(x: -minX) 46 | .frame(width: proxy.size.width * 2.5) 47 | .frame(width: cardSize.width, height: cardSize.height) 48 | } 49 | .loadDiskFileSynchronously() 50 | .cacheMemoryOnly() 51 | .fade(duration: 0.25) 52 | .resizable() 53 | .aspectRatio(contentMode: .fill) 54 | // .scaleEffect(1.25) 55 | .offset(x: -minX) 56 | .frame(width: proxy.size.width * 2.5) 57 | .frame(width: cardSize.width, height: cardSize.height) 58 | .overlay(content: { 59 | OverlayView(imageData) 60 | }) 61 | .clipShape(.rect(cornerRadius: 15)) 62 | .shadow(color: .black.opacity(0.25), radius: 8, x: 5, y: 10) 63 | } 64 | .frame(width: size.width - 60, height: size.height - 50) 65 | // scroll animation 66 | .scrollTransition(.interactive, axis: .horizontal) { view, phase in 67 | view 68 | .scaleEffect(phase.isIdentity ? 1 : 0.95) 69 | } 70 | } 71 | } 72 | } 73 | .padding(.horizontal, 30) 74 | .scrollTargetLayout() 75 | .frame(height: size.height, alignment: .top) 76 | .scrollTargetBehavior(.viewAligned) 77 | .scrollIndicators(.hidden) 78 | } 79 | 80 | } 81 | .frame(height: 500) 82 | .padding(.horizontal, -15) 83 | .padding(.top, 10) 84 | .overlay(alignment: .bottomTrailing, content: { 85 | Text("By: I am blue !") 86 | .font(.callout) 87 | .italic() 88 | }) 89 | .padding(15) 90 | 91 | Spacer() 92 | } 93 | .scrollIndicators(.hidden) 94 | } 95 | 96 | /// Overlay View 97 | @ViewBuilder 98 | func OverlayView (_ card: ImageUnsplash) -> some View { 99 | ZStack(alignment: .bottomLeading, content: { 100 | LinearGradient (colors: [ 101 | .clear, 102 | .clear, 103 | .clear, 104 | .clear, 105 | .clear, 106 | .black.opacity (0.1), 107 | .black.opacity (0.5), 108 | .black 109 | ], startPoint: .top, endPoint: .bottom) 110 | VStack(alignment: .leading, spacing: 4, content: { 111 | Text (card.user.username) 112 | .font (.title2) 113 | .fontWeight(.black) 114 | .foregroundStyle (.white) 115 | Text (card.user.name) 116 | .font(.callout) 117 | .foregroundStyle (.white.opacity (0.8)) 118 | }) 119 | .padding() 120 | }) 121 | } 122 | } 123 | 124 | #Preview { 125 | ParallaxCarouselScrollView() 126 | } 127 | -------------------------------------------------------------------------------- /IOS17-Swift/View/ParallaxScrollEffect/ParallaxScrollEffectView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ParallaxScrollEffectView.swift 3 | // IOS17-Swift 4 | // 5 | // Created by xqsadness on 29/12/2023. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ParallaxScrollEffectView: View { 11 | var body: some View { 12 | ScrollView(.vertical, showsIndicators: false){ 13 | LazyVStack(spacing: 15){ 14 | DummySection(title: "Social Media") 15 | 16 | DummySection(title: "Sales", isLong: true) 17 | 18 | //Parallax Image 19 | ParallaxImage(usesFullWidth: true){ size in 20 | Image(.p1) 21 | .resizable() 22 | .aspectRatio(contentMode: .fill) 23 | .frame(width: size.width, height: size.height) 24 | } 25 | .frame(height: 300) 26 | 27 | DummySection(title: "Busniess", isLong: true) 28 | 29 | DummySection(title: "Promotion", isLong: true) 30 | 31 | ParallaxImage(maximumMovement: 150,usesFullWidth: false){ size in 32 | Image(.p2) 33 | .resizable() 34 | .aspectRatio(contentMode: .fill) 35 | .frame(width: size.width, height: size.height) 36 | } 37 | .frame(height: 400) 38 | 39 | DummySection(title: "Youtube") 40 | 41 | DummySection(title: "Meta") 42 | } 43 | .padding(15) 44 | } 45 | } 46 | 47 | // Dummy Section 48 | @ViewBuilder 49 | func DummySection(title: String, isLong: Bool = false) -> some View{ 50 | VStack(alignment: .leading, spacing: 8){ 51 | Text(title) 52 | .font(.title.bold()) 53 | 54 | Text("Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum") 55 | .multilineTextAlignment(.leading) 56 | .kerning(1.2) 57 | } 58 | .frame(maxWidth: .infinity, alignment: .leading) 59 | } 60 | } 61 | 62 | struct ParallaxImage: View{ 63 | var maximumMovement: CGFloat = 100 64 | var usesFullWidth: Bool = false 65 | 66 | @ViewBuilder var content: (CGSize) -> Content 67 | 68 | var body: some View{ 69 | GeometryReader{ 70 | let size = $0.size 71 | // Movement animation props 72 | let minY = $0.frame(in: .scrollView(axis: .vertical)).minY 73 | let scrollViewHeight = $0 .bounds(of: .scrollView)?.size.height ?? 0 74 | let maximumMovement = min(maximumMovement, (size.height * 0.35)) 75 | let stretchedSize: CGSize = .init(width: size.width, height: size.height + maximumMovement) 76 | 77 | let progress = minY / scrollViewHeight 78 | let cappedProgress = max(min(progress, 1.0), 0.0) 79 | let movementOffset = cappedProgress * -maximumMovement 80 | 81 | content(size) 82 | .offset(y: movementOffset) 83 | .frame(width: stretchedSize.width, height: stretchedSize.height) 84 | .frame(width: size.width, height: size.height) 85 | .overlay(alignment: .top){ 86 | Text("\(cappedProgress)") 87 | .font(.title) 88 | .foregroundStyle(.white) 89 | } 90 | .clipped() 91 | } 92 | .containerRelativeFrame(usesFullWidth ? [.horizontal] : []) 93 | } 94 | } 95 | 96 | #Preview { 97 | ParallaxScrollEffectView() 98 | } 99 | -------------------------------------------------------------------------------- /IOS17-Swift/View/Passcode/NumberPadView.swift: -------------------------------------------------------------------------------- 1 | 2 | import SwiftUI 3 | 4 | struct NumberPadView: View { 5 | @Binding var passcode: String 6 | private let columns: [GridItem] = [ 7 | .init(), 8 | .init(), 9 | .init()] 10 | var body: some View { 11 | LazyVGrid(columns: columns){ 12 | ForEach(1 ... 9, id: \.self){ index in 13 | Button { 14 | addValue(index) 15 | } 16 | label:{ 17 | Text("\(index)") 18 | .font(/*@START_MENU_TOKEN@*/.title/*@END_MENU_TOKEN@*/) 19 | .frame(maxWidth: .infinity) 20 | .padding(.vertical,16) 21 | .contentShape(.rect) 22 | } 23 | } 24 | Button { 25 | removeValue() 26 | } 27 | label:{ 28 | Image(systemName: "delete.backward") 29 | .font(/*@START_MENU_TOKEN@*/.title/*@END_MENU_TOKEN@*/) 30 | .frame(maxWidth: .infinity) 31 | .padding(.vertical,16) 32 | .contentShape(.rect) 33 | } 34 | Button { 35 | addValue(0) 36 | } 37 | label:{ 38 | Text("0") 39 | .font(/*@START_MENU_TOKEN@*/.title/*@END_MENU_TOKEN@*/) 40 | .frame(maxWidth: .infinity) 41 | .padding(.vertical,16) 42 | .contentShape(.rect) 43 | } 44 | } 45 | .foregroundStyle(.primary) 46 | } 47 | private func addValue(_ value: Int){ 48 | if passcode.count < 4{ 49 | passcode += "\(value)" 50 | } 51 | } 52 | private func removeValue(){ 53 | if !passcode.isEmpty{ 54 | passcode.removeLast() 55 | } 56 | } 57 | } 58 | 59 | #Preview { 60 | NumberPadView(passcode: .constant("")) 61 | } 62 | -------------------------------------------------------------------------------- /IOS17-Swift/View/Passcode/PasscodeIndicatorView.swift: -------------------------------------------------------------------------------- 1 | 2 | import SwiftUI 3 | 4 | struct PasscodeIndicatorView: View { 5 | @Binding var passcode: String 6 | @Binding var isAnimation: Bool 7 | 8 | var body: some View { 9 | HStack(spacing: 32){ 10 | ForEach(0 ..< 4){ index in 11 | Circle() 12 | .fill(passcode.count > index ? .primary : Color(.white)) 13 | .frame(width:20,height: 20) 14 | .overlay{ 15 | Circle() 16 | .stroke(.black,lineWidth: 1.0) 17 | } 18 | } 19 | } 20 | .offset(x: isAnimation ? -10 : 0) 21 | .animation(Animation.easeInOut(duration: 0.1).repeatCount(2), value: isAnimation) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /IOS17-Swift/View/Passcode/PasscodeView.swift: -------------------------------------------------------------------------------- 1 | 2 | import SwiftUI 3 | 4 | struct PasscodeHomeView: View { 5 | @State private var isAutheticated = false 6 | var body: some View { 7 | VStack { 8 | if isAutheticated{ 9 | VStack{ 10 | Text("You're in!") 11 | 12 | Button("Log Out"){ 13 | isAutheticated = false 14 | } 15 | } 16 | }else{ 17 | PasscodeView(isAuthenticated: $isAutheticated) 18 | } 19 | } 20 | .padding() 21 | .preferredColorScheme(.light) 22 | } 23 | } 24 | 25 | struct PasscodeView: View { 26 | @State private var passcode = "" 27 | @Binding var isAuthenticated: Bool 28 | 29 | @State private var isWrong = false 30 | @State private var isAnimation: Bool = false { 31 | didSet { 32 | if(isAnimation == true){ 33 | DispatchQueue.main.asyncAfter(deadline: .now () + 0.25){ 34 | isAnimation = false 35 | } 36 | } 37 | } 38 | } 39 | 40 | var body: some View { 41 | VStack(spacing: 40){ 42 | VStack(spacing: 20){ 43 | Text("Enter Passcode") 44 | .font(.largeTitle) 45 | .fontWeight(.heavy) 46 | 47 | Text("Please enter your 4-digit pin to securely access your account.") 48 | .font(.subheadline) 49 | .multilineTextAlignment(.center) 50 | .lineLimit(2) 51 | .frame(height: 60) 52 | } .padding(.top) 53 | 54 | // indicator view 55 | PasscodeIndicatorView(passcode: $passcode,isAnimation: $isAnimation) 56 | 57 | Spacer() 58 | 59 | if isWrong{ 60 | Text("Wrong password, please re-enter") 61 | .font(.subheadline) 62 | .multilineTextAlignment(.center) 63 | .foregroundStyle(.red) 64 | } 65 | 66 | //numberpad view 67 | NumberPadView(passcode: $passcode) 68 | 69 | }.onChange(of: passcode) { _ , _ in 70 | verifyPasscode() 71 | } 72 | } 73 | 74 | private func verifyPasscode(){ 75 | guard passcode.count == 4 else{ return } 76 | 77 | Task{ 78 | try? await Task.sleep(nanoseconds: 125_000_000) 79 | isAuthenticated = passcode == "1111" 80 | passcode = "" 81 | withAnimation(.spring) { 82 | self.isAnimation = true 83 | isWrong = true 84 | } 85 | } 86 | } 87 | } 88 | 89 | #Preview { 90 | PasscodeView(isAuthenticated: .constant(false)) 91 | } 92 | -------------------------------------------------------------------------------- /IOS17-Swift/View/SideMenu/AnimatedSideBar.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SideMenuView.swift 3 | // IOS17-Swift 4 | // 5 | // Created by iamblue on 28/03/2024. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct AnimatedSideBar: View { 11 | var rotationsWhenExpands: Bool = true 12 | var disableInteraction: Bool = true 13 | var sideMenuWidth: CGFloat = 200 14 | var connerRadius: CGFloat = 25 15 | @Binding var showMenu: Bool 16 | 17 | @ViewBuilder var content: (UIEdgeInsets) -> Content 18 | @ViewBuilder var menuView: (UIEdgeInsets) -> MenuView 19 | @ViewBuilder var background: Background 20 | 21 | @GestureState var isDragging = false 22 | @State var offsetX: CGFloat = 0 23 | @State var lastOffsetX: CGFloat = 0 24 | @State var progress: CGFloat = 0 25 | 26 | var body: some View { 27 | GeometryReader{ 28 | let size = $0.size 29 | let safeArea = (UIApplication.shared.connectedScenes.first as? UIWindowScene)?.keyWindow?.safeAreaInsets ?? .zero 30 | 31 | HStack(spacing: 0){ 32 | GeometryReader{_ in 33 | menuView(safeArea) 34 | } 35 | .frame(width: sideMenuWidth) 36 | 37 | GeometryReader{_ in 38 | content(safeArea) 39 | } 40 | .frame(width: size.width) 41 | .overlay{ 42 | if disableInteraction && progress > 0{ 43 | Rectangle() 44 | .fill(.black.opacity(progress * 0.2)) 45 | .onTapGesture { 46 | withAnimation(.snappy(duration: 0.3, extraBounce: 0)) { 47 | reset() 48 | } 49 | } 50 | } 51 | } 52 | .mask { 53 | RoundedRectangle(cornerRadius: progress * connerRadius) 54 | } 55 | .scaleEffect(rotationsWhenExpands ? 1 - (progress * 0.1) : 1, anchor: .trailing) 56 | .rotation3DEffect( 57 | .init(degrees: rotationsWhenExpands ? (progress * -15) : 0), 58 | axis: (x: 0.0, y: 1.0, z: 0.0) 59 | ) 60 | } 61 | .frame(width: size.width + sideMenuWidth, height: size.height) 62 | .offset(x: -sideMenuWidth) 63 | .offset(x: offsetX) 64 | .contentShape(.rect) 65 | .simultaneousGesture(dragGesture) 66 | } 67 | .background(background) 68 | .ignoresSafeArea() 69 | .onChange(of: showMenu, initial: true) { oldValue, newValue in 70 | withAnimation(.snappy(duration: 0.3, extraBounce: 0)) { 71 | if newValue{ 72 | showSideBar() 73 | } 74 | else{ 75 | reset() 76 | } 77 | } 78 | } 79 | } 80 | 81 | var dragGesture : some Gesture { 82 | DragGesture() 83 | .updating($isDragging) { _, out, _ in 84 | out = true 85 | } 86 | .onChanged { value in 87 | DispatchQueue.main.asyncAfter(deadline: .now()) { 88 | guard value.startLocation.x > 10, isDragging else { return } 89 | 90 | let translationX = isDragging ? max(min(value.translation.width + lastOffsetX, sideMenuWidth), 0) : 0 91 | 92 | offsetX = translationX 93 | calculateProgress() 94 | } 95 | } 96 | .onEnded { value in 97 | guard value.startLocation.x > 10 else {return} 98 | 99 | withAnimation(.snappy(duration: 0.3, extraBounce: 0)) { 100 | let velocityX = value.velocity.width / 8 101 | let total = velocityX + offsetX 102 | 103 | if total > (sideMenuWidth * 0.5){ 104 | showSideBar() 105 | } 106 | else{ 107 | reset() 108 | } 109 | } 110 | } 111 | } 112 | 113 | func showSideBar(){ 114 | offsetX = sideMenuWidth 115 | lastOffsetX = offsetX 116 | showMenu = true 117 | calculateProgress() 118 | } 119 | 120 | func reset(){ 121 | offsetX = 0 122 | lastOffsetX = 0 123 | showMenu = false 124 | calculateProgress() 125 | } 126 | 127 | func calculateProgress(){ 128 | progress = max(min(offsetX / sideMenuWidth , 1) ,0) 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /IOS17-Swift/View/SlideToUnlock/SlideToUnlockView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SlideToUnlockView.swift 3 | // IOS17-Swift 4 | // 5 | // Created by xqsadness on 29/03/2024. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct SlideToUnlockView: View { 11 | var body: some View { 12 | Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/) 13 | } 14 | } 15 | 16 | #Preview { 17 | SlideToUnlockView() 18 | } 19 | -------------------------------------------------------------------------------- /IOS17-Swift/View/StackAnimation/StackAnimation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StackAnimation.swift 3 | // IOS17-Swift 4 | // 5 | // Created by xqsadness on 1/10/24. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct StackAnimation: View { 11 | var body: some View { 12 | ScrollView{ 13 | VStack(spacing: 15){ 14 | StackView(Tcolor: .purple, color: .cyan, icon: "book", title: "Read Books") 15 | 16 | StackView(Tcolor: .blue.opacity(0.7), color: .cyan, icon: "movieclapper", title: "Movies") 17 | } 18 | } 19 | } 20 | } 21 | 22 | #Preview { 23 | StackAnimation() 24 | } 25 | -------------------------------------------------------------------------------- /IOS17-Swift/View/StackAnimation/StackView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StackView.swift 3 | // IOS17-Swift 4 | // 5 | // Created by xqsadness on 1/10/24. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct DataModel{ 11 | var title: String 12 | var isCompleted: Bool = false 13 | } 14 | 15 | struct StackView: View { 16 | @State private var tasks: [DataModel] = [ 17 | DataModel(title: "Task 1"), 18 | DataModel(title: "Task 2"), 19 | DataModel(title: "Task 3"), 20 | DataModel(title: "Task 4"), 21 | DataModel(title: "Task 5"), 22 | ] 23 | @State private var show = false 24 | var Tcolor: Color 25 | var color: Color 26 | var icon: String 27 | var title: String 28 | var completedTasks: Int { 29 | tasks.filter { $0.isCompleted } .count 30 | } 31 | var progress: Double { 32 | tasks.isEmpty ? 0 : Double(completedTasks) / Double(tasks.count) 33 | } 34 | var body: some View { 35 | ZStack{ 36 | VStack(spacing: 12){ 37 | ForEach(tasks.indices, id: \.self) { index in 38 | let task = tasks[index] 39 | let scaleValue = 1.0 - (CGFloat(index) * 0.02) 40 | let opacityValue = 1.0 - (CGFloat(index) * 0.2) 41 | 42 | HStack { 43 | Text(task.title).strikethrough(task.isCompleted, color: .white) 44 | Spacer() 45 | Image(systemName: task.isCompleted ? "checkmark.circle.fill" : "circle") 46 | .font(.title2).contentTransition(.symbolEffect) 47 | } 48 | .bold() 49 | .foregroundStyle(.white) 50 | .padding(.horizontal) 51 | .frame(height: 50) 52 | .frame(maxWidth: .infinity) 53 | .opacity(show ? 1 : 0) 54 | .background(color, in: .rect(cornerRadius: 12)) 55 | .scaleEffect(show ? 1 : scaleValue) 56 | .opacity(show ? 1 : opacityValue) 57 | .offset(y: CGFloat(show ? 0 * index : -56 * index)) 58 | .onTapGesture { 59 | withAnimation { 60 | tasks[index].isCompleted.toggle() 61 | } 62 | } 63 | } 64 | } 65 | .frame(height: show ? nil : 50, alignment: .top) 66 | .padding(.top, show ? 68 : 5) 67 | .overlay(alignment: .top) { 68 | HStack { 69 | Image(systemName: icon) 70 | Text(title) 71 | Spacer() 72 | ProgressV(progress: progress) 73 | } 74 | .bold().foregroundStyle(.white) 75 | .frame(height: 52) 76 | .padding(.horizontal) 77 | .background(Tcolor, in: .rect(cornerRadius: 12)) 78 | .overlay { 79 | RoundedRectangle(cornerRadius: 12).stroke(lineWidth: 2.5) 80 | } 81 | .onTapGesture { 82 | withAnimation { 83 | show.toggle() 84 | } 85 | } 86 | } 87 | } 88 | .padding() 89 | } 90 | } 91 | 92 | #Preview { 93 | StackAnimation() 94 | } 95 | 96 | struct ProgressV: View { 97 | var progress: Double 98 | var lineWidth: CGFloat = 3 99 | var circleSize: CGFloat = 20 100 | var body: some View { 101 | ZStack { 102 | let strokeColor = progress >= 1.0 ? Color.green : Color.white 103 | Circle() 104 | .trim(from: 0, to: progress) 105 | .stroke(strokeColor, style: StrokeStyle(lineWidth: lineWidth, lineCap: .round)) 106 | .frame(width: circleSize, height: circleSize) 107 | .rotationEffect(.degrees(-90)) 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /IOS17-Swift/View/StretchySlider/CustomSlider.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CustomSliderView.swift 3 | // IOS17-Swift 4 | // 5 | // Created by iamblue on 19/02/2024. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct CustomSlider: View { 11 | @Binding var sliderProgress: CGFloat 12 | /// Configuration 13 | var symbol: Symbol? 14 | var axis: SliderAxis 15 | var tint: Color 16 | //View props 17 | @State private var progress: CGFloat = .zero 18 | @State private var dragOffset: CGFloat = .zero 19 | @State private var lastDragOffset: CGFloat = .zero 20 | 21 | var body: some View { 22 | GeometryReader { 23 | let size = $0.size 24 | let orientationSize = axis == .horizontal ? size.width : size.height 25 | let progressValue = (max(progress, .zero)) * orientationSize 26 | 27 | ZStack(alignment: axis == .horizontal ? .leading : .bottom){ 28 | Rectangle() 29 | .fill(.fill) 30 | 31 | Rectangle() 32 | .fill(tint) 33 | .frame( 34 | width: axis == .horizontal ? progressValue : nil, 35 | height: axis == .vertical ? progressValue : nil 36 | ) 37 | 38 | if let symbol, symbol.display{ 39 | Image(systemName: symbol.icon) 40 | .font(symbol.font) 41 | .foregroundStyle(symbol.tint) 42 | .padding(symbol.padding) 43 | .frame(width: size.width, height: size.height, alignment: symbol.alignment) 44 | } 45 | } 46 | .clipShape(.rect(cornerRadius: 15)) 47 | .contentShape(.rect(cornerRadius: 15)) 48 | .optionalSizingModifier( 49 | axis: axis, 50 | size: size, 51 | progress: progress, 52 | orientationSize: orientationSize 53 | ) 54 | .gesture( 55 | DragGesture(minimumDistance: 0) 56 | .onChanged{ 57 | let translation = $0.translation 58 | let movement = (axis == .horizontal ? translation.width : -translation.height) + lastDragOffset 59 | dragOffset = movement 60 | calculatorProgress(orientationSize: orientationSize) 61 | } 62 | .onEnded{ _ in 63 | withAnimation(.smooth) { 64 | dragOffset = dragOffset > orientationSize ? orientationSize : (dragOffset < 0 ? 0 : dragOffset) 65 | calculatorProgress(orientationSize: orientationSize) 66 | } 67 | 68 | lastDragOffset = dragOffset 69 | } 70 | ) 71 | .frame( 72 | maxWidth: size.width, 73 | maxHeight: size.height, 74 | alignment: axis == .vertical ? ( 75 | progress < 0 ? .top : .bottom 76 | ) : ( 77 | progress < 0 ? .trailing : .leading 78 | ) 79 | ) 80 | .onChange(of: sliderProgress, initial: true) { oldValue, newValue in 81 | //Init progress settings 82 | guard sliderProgress != progress, (sliderProgress > 0 && sliderProgress < 1) else { return } 83 | progress = max(min(sliderProgress, 1.0), .zero) 84 | dragOffset = progress * orientationSize 85 | lastDragOffset = dragOffset 86 | } 87 | .onChange(of: axis) { oldValue, newValue in 88 | dragOffset = progress * orientationSize 89 | lastDragOffset = dragOffset 90 | } 91 | } 92 | .onChange(of: progress) { oldValue, newValue in 93 | sliderProgress = max(min(progress, 1.0), .zero) 94 | } 95 | } 96 | //Caculating Progress 97 | private func calculatorProgress(orientationSize: CGFloat){ 98 | let topAndTrailingExcessOffset = orientationSize + (dragOffset - orientationSize) * 0.08 99 | let bottomAndLeadingExcessOffset = dragOffset < 0 ? (dragOffset * 0.08) : dragOffset 100 | let progress = (dragOffset > orientationSize ? topAndTrailingExcessOffset : bottomAndLeadingExcessOffset) / orientationSize 101 | self.progress = progress 102 | } 103 | 104 | //Symbol Config 105 | struct Symbol { 106 | var icon: String 107 | var tint: Color 108 | var font: Font 109 | var padding: CGFloat 110 | var display: Bool = true 111 | var alignment: Alignment = .center 112 | } 113 | 114 | enum SliderAxis { 115 | case vertical 116 | case horizontal 117 | } 118 | } 119 | 120 | fileprivate extension View{ 121 | @ViewBuilder 122 | func optionalSizingModifier(axis: CustomSlider.SliderAxis, size: CGSize, progress: CGFloat, orientationSize: CGFloat) -> some View{ 123 | let topAndTrailingScale = 1 - (progress-1) * 0.25 124 | let bottomAndLeadingScale = 1 + (progress) * 0.25 125 | 126 | self 127 | .frame( 128 | width: axis == .horizontal && progress < 0 ? size.width + (-progress * size.width) : nil, 129 | height: axis == .vertical && progress < 0 ? size.height + (-progress * size.height) : nil 130 | ) 131 | .scaleEffect ( 132 | x: axis == .vertical ? (progress > 1 ? topAndTrailingScale : (progress < 0 ? bottomAndLeadingScale : 1)) : 1, 133 | y: axis == .horizontal ? (progress > 1 ? topAndTrailingScale : (progress < 0 ? bottomAndLeadingScale: 1)) : 1, 134 | anchor: axis == .horizontal ? (progress < 0 ? .trailing : .leading): 135 | (progress < 0 ? .top: .bottom) 136 | ) 137 | } 138 | } 139 | 140 | #Preview { 141 | StretchySliderView() 142 | } 143 | -------------------------------------------------------------------------------- /IOS17-Swift/View/StretchySlider/StretchySliderView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StretchySliderView.swift 3 | // IOS17-Swift 4 | // 5 | // Created by iamblue on 19/02/2024. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct StretchySliderView: View { 11 | //View props 12 | @State private var progress: CGFloat = 0.6 13 | @State private var axis: CustomSlider.SliderAxis = .vertical 14 | 15 | var body: some View { 16 | VStack{ 17 | Picker("", selection: $axis){ 18 | Text("Vertical") 19 | .tag(CustomSlider.SliderAxis.vertical) 20 | 21 | Text("Horizontal") 22 | .tag(CustomSlider.SliderAxis.horizontal) 23 | } 24 | .pickerStyle(.segmented) 25 | 26 | CustomSlider( 27 | sliderProgress: $progress, 28 | symbol: .init( 29 | icon: "airpodspro", 30 | tint: .gray, 31 | font: .system( 32 | size: 23 33 | ), 34 | padding: 20, 35 | display: axis == .vertical, 36 | alignment: .bottom 37 | ), 38 | axis: axis, 39 | tint: .white 40 | ) 41 | .frame(width: axis == .horizontal ? 220 : 60, height: axis == .horizontal ? 30 : 180) 42 | .frame(maxHeight: .infinity) 43 | .animation(.snappy, value: axis) 44 | } 45 | .padding() 46 | .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top) 47 | .background(.fill) 48 | } 49 | } 50 | 51 | #Preview { 52 | StretchySliderView() 53 | } 54 | -------------------------------------------------------------------------------- /IOS17-Swift/View/YouTubeMiniPlayer/YouTubeMiniPlayerView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // YouTubeMiniPlayerView.swift 3 | // IOS17-Swift 4 | // 5 | // Created by xqsadness on 24/02/2024. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct YouTubeMiniPlayerView: View { 11 | //View props 12 | @State private var activeTab: Tab = .home 13 | @State private var config: PlayerConfig = .init() 14 | var body: some View { 15 | 16 | ZStack(alignment: .bottom) { 17 | TabView(selection: $activeTab) { 18 | HomeTabView() 19 | .setuptab(.home) 20 | 21 | Text(Tab.shorts.rawValue) 22 | .setuptab(.shorts) 23 | 24 | Text(Tab.subscriptions.rawValue) 25 | .setuptab(.subscriptions) 26 | 27 | Text(Tab.you.rawValue) 28 | .setuptab(.you) 29 | } 30 | .padding(.bottom, tabBarHeight) 31 | 32 | GeometryReader{ 33 | let size = $0.size 34 | if config.showMiniPlayer{ 35 | MiniPlayerView(config: $config, size: size){ 36 | withAnimation(.easeInOut(duration: 0.3)) { 37 | config.showMiniPlayer = false 38 | config.selectedPlayerItem = nil 39 | 40 | } 41 | } 42 | } 43 | } 44 | 45 | CustomTabbar() 46 | .offset(y: config.showMiniPlayer ? tabBarHeight - (config.progress * tabBarHeight) : 0) 47 | } 48 | .ignoresSafeArea(.all, edges: .bottom) 49 | } 50 | 51 | @ViewBuilder 52 | func HomeTabView() -> some View{ 53 | NavigationStack{ 54 | ScrollView(.vertical){ 55 | ForEach(items){item in 56 | PlayerItemCardView(item) { 57 | config.selectedPlayerItem = item 58 | 59 | withAnimation(.easeIn(duration: 0.3)) { 60 | config.showMiniPlayer = true 61 | } 62 | } 63 | } 64 | } 65 | .navigationTitle("YouTube") 66 | .toolbarBackground(.visible, for: .navigationBar) 67 | .toolbarBackground(.ultraThinMaterial, for: .navigationBar) 68 | } 69 | } 70 | 71 | @ViewBuilder 72 | func PlayerItemCardView(_ item: PlayerItem, ontap: @escaping () -> ()) -> some View{ 73 | VStack(alignment: .leading, spacing: 6){ 74 | Image(item.image) 75 | .resizable() 76 | .aspectRatio(contentMode: .fill) 77 | .frame(height: 200) 78 | .clipShape(.rect(cornerRadius: 10)) 79 | .contentShape(.rect) 80 | .onTapGesture(perform: ontap) 81 | 82 | HStack(spacing: 10){ 83 | Image(systemName: "person.circle.fill") 84 | .font(.title) 85 | 86 | VStack(alignment: .leading, spacing: 4){ 87 | Text(item.title) 88 | .font(.callout) 89 | .lineLimit(2) 90 | 91 | HStack(spacing: 6){ 92 | Text(item.author) 93 | 94 | Text(" 2 Day ago") 95 | } 96 | .font(.caption) 97 | .foregroundStyle(.gray) 98 | } 99 | } 100 | } 101 | } 102 | 103 | @ViewBuilder 104 | func CustomTabbar() -> some View{ 105 | HStack(spacing: 0) { 106 | ForEach(Tab.allCases, id: \.rawValue){ tab in 107 | VStack(spacing: 4) { 108 | Image(systemName: tab.symbol) 109 | .font(.title3) 110 | 111 | Text(tab.rawValue) 112 | .font(.caption2) 113 | } 114 | .foregroundStyle(activeTab == tab ? Color.red : .gray) 115 | .frame(maxWidth: .infinity, maxHeight: .infinity) 116 | .contentShape(.rect) 117 | .onTapGesture { 118 | activeTab = tab 119 | } 120 | } 121 | } 122 | // .frame(height: 49) 123 | .overlay(alignment: .top){ 124 | Divider() 125 | } 126 | .frame(maxHeight: .infinity, alignment: .center) 127 | .frame(height: tabBarHeight) 128 | .background(.white) 129 | } 130 | } 131 | 132 | extension View{ 133 | @ViewBuilder 134 | func setuptab(_ tab: Tab) -> some View{ 135 | self 136 | .tag(tab) 137 | .toolbar(.hidden, for: .tabBar) 138 | } 139 | 140 | // SafeArea Value 141 | var safeArea: UIEdgeInsets{ 142 | if let safeArea = (UIApplication.shared.connectedScenes.first as? UIWindowScene)?.keyWindow?.safeAreaInsets{ 143 | return safeArea 144 | } 145 | return .zero 146 | } 147 | 148 | var tabBarHeight: CGFloat{ 149 | return 49 + safeArea.bottom 150 | } 151 | } 152 | 153 | 154 | 155 | #Preview { 156 | YouTubeMiniPlayerView() 157 | } 158 | -------------------------------------------------------------------------------- /IOS17-Swift/ViewModels/ParallaxCarouselScrollViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ParallaxCarouselScrollViewModel.swift 3 | // IOS17-Swift 4 | // 5 | // Created by xqsadness on 01/12/2023. 6 | // 7 | 8 | import Foundation 9 | import Combine 10 | 11 | class ParallaxCarouselScrollViewModel: ObservableObject{ 12 | 13 | @Published var imageData: [ImageUnsplash] = [] 14 | @Published var isLoading: Bool = true 15 | 16 | private var cancellables = Set() 17 | 18 | init(){ 19 | getImage() 20 | } 21 | 22 | private func getImage(){ 23 | 24 | let urlUnsplash = "https://api.unsplash.com/photos/?client_id=XWZWXva1XGdYNuIjZ8xJf6CmdzeZZm09PhOpUuUKAuA" 25 | 26 | guard let url = URL(string: urlUnsplash) else {return} 27 | 28 | NetworkingManager.download(url: url) 29 | .decode(type: [ImageUnsplash].self, decoder: JSONDecoder()) 30 | .sink(receiveCompletion: NetworkingManager.handleCompeltion) { [weak self] returnedImage in 31 | self?.imageData = returnedImage 32 | self?.isLoading = false 33 | } 34 | .store(in: &cancellables) 35 | } 36 | } 37 | 38 | struct ImageUnsplash: Codable{ 39 | var id: String 40 | var user: User 41 | var urls: Urls 42 | } 43 | 44 | struct User: Codable{ 45 | var name: String 46 | var username: String 47 | } 48 | 49 | struct Urls: Codable{ 50 | var raw: String 51 | var full: String 52 | var regular: String 53 | var small: String 54 | var thumb: String 55 | } 56 | -------------------------------------------------------------------------------- /Patterns/Adapter/Adapter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Adapter.swift 3 | // IOS17-Swift 4 | // 5 | // Created by iamblue on 26/12/2023. 6 | // 7 | 8 | import SwiftUI 9 | 10 | /*: 11 | Adapter 12 | ---------- 13 | 14 | The adapter pattern is used to provide a link between two otherwise incompatible types by wrapping the "adaptee" with a class that supports the interface required by the client. 15 | 16 | ### Example 17 | */ 18 | protocol NewDeathStarSuperLaserAiming { 19 | var angleV: Double { get } 20 | var angleH: Double { get } 21 | } 22 | /*: 23 | **Adapte** 24 | */ 25 | struct OldDeathStarSuperlaserTarget { 26 | let angleHorizontal: Float 27 | let angleVertical: Float 28 | 29 | init(angleHorizontal: Float, angleVertical: Float) { 30 | self.angleHorizontal = angleHorizontal 31 | self.angleVertical = angleVertical 32 | } 33 | } 34 | /*: 35 | **Adapter** 36 | */ 37 | struct NewDeathStarSuperlaserTarget: NewDeathStarSuperLaserAiming { 38 | 39 | private let target: OldDeathStarSuperlaserTarget 40 | 41 | var angleV: Double { 42 | return Double(target.angleVertical) 43 | } 44 | 45 | var angleH: Double { 46 | return Double(target.angleHorizontal) 47 | } 48 | 49 | init(_ target: OldDeathStarSuperlaserTarget) { 50 | self.target = target 51 | } 52 | } 53 | 54 | #Preview { 55 | VStack{ } 56 | .onAppear{ 57 | /*: 58 | ### Usage 59 | */ 60 | let target = OldDeathStarSuperlaserTarget(angleHorizontal: 14.0, angleVertical: 12.0) 61 | let newFormat = NewDeathStarSuperlaserTarget(target) 62 | 63 | print(newFormat.angleH) 64 | print(newFormat.angleV) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Patterns/CombineData/CombineSearch.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Combine.swift 3 | // IOS17-Swift 4 | // 5 | // Created by xqsadness on 01/12/2023. 6 | // 7 | 8 | import Foundation 9 | import Combine 10 | import SwiftUI 11 | 12 | struct CombineSearchView: View{ 13 | @StateObject var vm: CombineVM 14 | 15 | //DI 16 | init(coinDataSevice: CoinDataService) { 17 | _vm = StateObject(wrappedValue: CombineVM(coinDataSevice: coinDataSevice)) 18 | } 19 | 20 | var body: some View{ 21 | VStack{ 22 | ScrollView{ 23 | if vm.isLoadData{ 24 | ProgressView() 25 | }else{ 26 | if vm.data.isEmpty{ 27 | Text("No data !") 28 | }else{ 29 | ForEach(vm.data, id: \.id){ value in 30 | Text(value.name) 31 | Text("\(value.currentPrice)") 32 | } 33 | } 34 | } 35 | } 36 | 37 | TextField("textSearch", text: $vm.textSearch) 38 | } 39 | .padding() 40 | } 41 | } 42 | 43 | #Preview{ 44 | CombineSearchView(coinDataSevice: CoinDataService()) 45 | } 46 | 47 | class CombineVM: ObservableObject { 48 | @Published var textSearch: String = "" 49 | @Published var isLoadData: Bool = true 50 | @Published var data: [CoinModel] = [] 51 | 52 | var cancellables: Set = [] 53 | var coinDataSevice: CoinDataService 54 | 55 | //DI 56 | init(coinDataSevice: CoinDataService) { 57 | self.coinDataSevice = coinDataSevice 58 | 59 | searchCoin() 60 | } 61 | 62 | func searchCoin(){ 63 | $textSearch 64 | .debounce(for: 0.5, scheduler: RunLoop.main) 65 | .combineLatest(self.coinDataSevice.$allCoins) 66 | .map { searchText, allCoins in 67 | guard !searchText.isEmpty else{ 68 | return allCoins 69 | } 70 | // filter with name and price 71 | return allCoins.filter( {$0.name.lowercased().contains(searchText.lowercased()) || String($0.currentPrice).contains(searchText.lowercased())} ) 72 | } 73 | .sink { [weak self] filteredCoins in 74 | self?.isLoadData = false 75 | self?.data = filteredCoins 76 | } 77 | .store(in: &cancellables) 78 | 79 | 80 | 81 | 82 | 83 | } 84 | } 85 | 86 | -------------------------------------------------------------------------------- /Patterns/Dependency-Injection/DIHomeView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DIView.swift 3 | // IOS17-Swift 4 | // 5 | // Created by xqsadness on 01/12/2023. 6 | // 7 | 8 | import SwiftUI 9 | import Combine 10 | 11 | 12 | // Dependency container 13 | class DependencyContainer { 14 | static let shared = DependencyContainer() 15 | 16 | // Dependencies 17 | let numberGenerator: NumberGeneratorProtocol 18 | let networkingManager: NetworkingManagerProtocol 19 | 20 | private init() { 21 | self.numberGenerator = NumberGeneratorService() 22 | self.networkingManager = NetworkingManagerService() 23 | } 24 | } 25 | 26 | struct DIHomeView: View { 27 | let dependencyContainer = DependencyContainer.shared 28 | 29 | var body: some View { 30 | VStack{ 31 | NumberGeneratorView(numberGenerator: dependencyContainer.numberGenerator) 32 | CoinView(service: dependencyContainer.networkingManager) 33 | } 34 | } 35 | } 36 | 37 | #Preview { 38 | DIHomeView() 39 | } 40 | -------------------------------------------------------------------------------- /Patterns/Dependency-Injection/Model/CoinDIModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CoinDIModel.swift 3 | // IOS17-Swift 4 | // 5 | // Created by xqsadness on 13/12/2023. 6 | // 7 | 8 | import Foundation 9 | 10 | struct CoinDIModel: Codable{ 11 | var ip: String 12 | var city: String 13 | var country: String 14 | var region: String 15 | } 16 | -------------------------------------------------------------------------------- /Patterns/Dependency-Injection/Protocol/ProtocolManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProtocolManager.swift 3 | // IOS17-Swift 4 | // 5 | // Created by xqsadness on 13/12/2023. 6 | // 7 | 8 | import Foundation 9 | import Combine 10 | 11 | protocol NumberGeneratorProtocol { 12 | func getRandomNumber() -> Int 13 | } 14 | 15 | protocol NetworkingManagerProtocol { 16 | func download(url: URL) -> AnyPublisher 17 | 18 | func handleURLResponse(output: URLSession.DataTaskPublisher.Output, url: URL) throws -> Data 19 | 20 | func handleCompeltion(completion: Subscribers.Completion) 21 | } 22 | -------------------------------------------------------------------------------- /Patterns/Dependency-Injection/Services/NetworkingManagerService.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Network.swift 3 | // L-Swift 4 | // 5 | // Created by xqsadness on 28/11/2023. 6 | // 7 | 8 | import SwiftUI 9 | import Combine 10 | 11 | // for DI 12 | class NetworkingManagerService: ObservableObject, NetworkingManagerProtocol{ 13 | 14 | enum NetworkingError: LocalizedError{ 15 | case badURLResponse(url: URL) 16 | case unowned 17 | 18 | var errorDescription: String?{ 19 | switch self{ 20 | case .badURLResponse(url: let url): return "Bad response from url: \(url)" 21 | case .unowned: return "unowned error occured" 22 | } 23 | } 24 | } 25 | 26 | func download(url: URL) -> AnyPublisher{ 27 | return URLSession.shared.dataTaskPublisher(for: url) 28 | .subscribe(on: DispatchQueue.global(qos: .default)) 29 | .tryMap({ try self.handleURLResponse(output: $0, url: url) }) 30 | .receive(on: DispatchQueue.main) 31 | .eraseToAnyPublisher() 32 | } 33 | 34 | func handleURLResponse(output: URLSession.DataTaskPublisher.Output, url: URL) throws -> Data{ 35 | guard let response = output.response as? HTTPURLResponse, 36 | response.statusCode >= 200 && response.statusCode < 300 else{ 37 | throw NetworkingError.badURLResponse(url: url) 38 | } 39 | return output.data 40 | } 41 | 42 | func handleCompeltion(completion: Subscribers.Completion){ 43 | switch completion{ 44 | case .finished: 45 | break 46 | case .failure(let err): 47 | print(err.localizedDescription) 48 | } 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /Patterns/Dependency-Injection/Services/NumberGeneratorService.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NumberGeneratorService.swift 3 | // IOS17-Swift 4 | // 5 | // Created by xqsadness on 13/12/2023. 6 | // 7 | 8 | import Foundation 9 | 10 | class NumberGeneratorService: ObservableObject, NumberGeneratorProtocol{ 11 | func getRandomNumber() -> Int{ 12 | return Int.random(in: 1...10000) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Patterns/Dependency-Injection/ViewModels/CoinViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CoinViewModel.swift 3 | // IOS17-Swift 4 | // 5 | // Created by xqsadness on 13/12/2023. 6 | // 7 | 8 | import Foundation 9 | import Combine 10 | 11 | class CoinViewModel: ObservableObject{ 12 | @Published var data: CoinDIModel? 13 | 14 | var cancellables: AnyCancellable? 15 | var service: NetworkingManagerProtocol 16 | 17 | init(service: NetworkingManagerProtocol){ 18 | self.service = service 19 | 20 | getData() 21 | } 22 | 23 | func getData(){ 24 | guard let url = URL(string: "https://ipinfo.io/161.185.160.93/geo") else { return } 25 | 26 | cancellables = service.download(url: url) 27 | .decode(type: CoinDIModel.self, decoder: JSONDecoder()) 28 | .sink(receiveCompletion: service.handleCompeltion) { data in 29 | self.data = data 30 | } 31 | // .store(in: &cancellables) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Patterns/Dependency-Injection/ViewModels/NumberGeneratorViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NumberGeneratorViewModel.swift 3 | // IOS17-Swift 4 | // 5 | // Created by xqsadness on 13/12/2023. 6 | // 7 | 8 | import Foundation 9 | 10 | class NumberGeneratorViewModel: ObservableObject{ 11 | private let numberGenerator: NumberGeneratorProtocol 12 | 13 | init(numberGenerator: NumberGeneratorProtocol) { 14 | print("___.___") 15 | self.numberGenerator = numberGenerator 16 | } 17 | 18 | @Published var number = 0 19 | 20 | func getRamDomNumber(){ 21 | self.number = numberGenerator.getRandomNumber() 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Patterns/Dependency-Injection/Views/CoinView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CoinView.swift 3 | // IOS17-Swift 4 | // 5 | // Created by xqsadness on 13/12/2023. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct CoinView: View{ 11 | @StateObject var vm: CoinViewModel 12 | 13 | init(service: NetworkingManagerProtocol) { 14 | _vm = StateObject(wrappedValue: CoinViewModel(service: service)) 15 | } 16 | 17 | var body: some View{ 18 | VStack{ 19 | if let data = vm.data{ 20 | Text(data.country) 21 | Text(data.ip) 22 | Text(data.city) 23 | Text(data.region) 24 | .foregroundColor(.black) 25 | } 26 | } 27 | } 28 | } 29 | 30 | #Preview { 31 | CoinView(service: NetworkingManagerService()) 32 | } 33 | -------------------------------------------------------------------------------- /Patterns/Dependency-Injection/Views/NumberGeneratorView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NumberGeneratorView.swift 3 | // IOS17-Swift 4 | // 5 | // Created by xqsadness on 13/12/2023. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct NumberGeneratorView: View { 11 | @StateObject private var vm: NumberGeneratorViewModel 12 | 13 | init( numberGenerator: NumberGeneratorProtocol ) { 14 | _vm = StateObject(wrappedValue: NumberGeneratorViewModel(numberGenerator: numberGenerator)) 15 | } 16 | 17 | var body: some View { 18 | VStack{ 19 | Text(vm.number.description) 20 | Button("Generate New Number") { 21 | vm.getRamDomNumber() 22 | } 23 | } 24 | } 25 | } 26 | 27 | #Preview { 28 | NumberGeneratorView(numberGenerator: NumberGeneratorService()) 29 | } 30 | -------------------------------------------------------------------------------- /Patterns/Factory-Pattern/FactoryView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FactoryView.swift 3 | // IOS17-Swift 4 | // 5 | // Created by xqsadness on 05/12/2023. 6 | // 7 | 8 | import SwiftUI 9 | 10 | protocol Computer{ 11 | func aboutDevice() -> String 12 | } 13 | 14 | struct OperatingSystem{ 15 | enum SystemType: String{ 16 | case iOS 17 | case iPadOS 18 | case macOS 19 | } 20 | 21 | var version: Int 22 | var type: SystemType 23 | } 24 | 25 | struct Mac: Computer{ 26 | var system: OperatingSystem 27 | 28 | init(system: OperatingSystem) { 29 | self.system = system 30 | } 31 | 32 | func aboutDevice() -> String { 33 | return "Mac \(system.type) - \(system.version)" 34 | } 35 | } 36 | 37 | struct Ipad: Computer{ 38 | var system: OperatingSystem 39 | 40 | init(system: OperatingSystem) { 41 | self.system = system 42 | } 43 | 44 | func aboutDevice() -> String { 45 | return "Ipad \(system.type) - \(system.version)" 46 | } 47 | } 48 | 49 | struct Iphone: Computer{ 50 | var system: OperatingSystem 51 | 52 | init(system: OperatingSystem) { 53 | self.system = system 54 | } 55 | 56 | func aboutDevice() -> String { 57 | return "Iphone \(system.type) - \(system.version)" 58 | } 59 | } 60 | 61 | struct ComputerFactory{ 62 | static func makeComputer(system: OperatingSystem) -> Computer{ 63 | switch system.type{ 64 | case .iOS: 65 | return Iphone(system: system) 66 | case .iPadOS: 67 | return Ipad(system: system) 68 | case .macOS: 69 | return Mac(system: system) 70 | } 71 | } 72 | } 73 | 74 | struct FactoryView: View { 75 | @State var systems: [OperatingSystem] = [ 76 | OperatingSystem(version: 2, type: .iOS), 77 | OperatingSystem(version: 6, type: .macOS), 78 | OperatingSystem(version: 2, type: .iPadOS), 79 | ] 80 | 81 | var body: some View { 82 | VStack{ 83 | ForEach(systems, id: \.type.hashValue){ system in 84 | let factory = ComputerFactory.makeComputer(system: system) 85 | Text(factory.aboutDevice()) 86 | } 87 | 88 | Button{ 89 | self.systems.append(OperatingSystem(version: Int.random(in: 1...100), type: .iOS)) 90 | }label: { 91 | Text("Add system") 92 | } 93 | .padding() 94 | } 95 | } 96 | } 97 | 98 | #Preview { 99 | FactoryView() 100 | } 101 | -------------------------------------------------------------------------------- /Patterns/Swift-Data/Model/Event.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Event.swift 3 | // IOS17-Swift 4 | // 5 | // Created by xqsadness on 28/12/2023. 6 | // 7 | 8 | import Foundation 9 | import SwiftData 10 | 11 | @Model 12 | class Event{ 13 | var name: String 14 | var location: String 15 | var people = [Person]() 16 | // or @Relationship(deleteRule: .cascade, inverse: \Person.metAt) var people: [Person] 17 | 18 | init(name: String, location: String) { 19 | self.name = name 20 | self.location = location 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Patterns/Swift-Data/Model/Person.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Person.swift 3 | // IOS17-Swift 4 | // 5 | // Created by xqsadness on 28/12/2023. 6 | // 7 | 8 | import Foundation 9 | import SwiftData 10 | 11 | @Model 12 | class Person{ 13 | @Attribute(originalName: "name") var fullName: String 14 | var emailAddress: String 15 | var details: String 16 | 17 | var metAt: Event? 18 | @Attribute(.externalStorage) var photo: Data? 19 | 20 | @Relationship(inverse: \Job.people) var jobs: [Job] 21 | 22 | init(name: String, emailAddress: String, details: String, metAt: Event? = nil, photo: Data? = nil, jobs: [Job]) { 23 | self.fullName = name 24 | self.emailAddress = emailAddress 25 | self.details = details 26 | self.metAt = metAt 27 | self.photo = photo 28 | self.jobs = jobs 29 | } 30 | } 31 | 32 | @Model 33 | class Job{ 34 | var name: String 35 | var jobDescription: String 36 | var salary: String 37 | 38 | var people: [Person] 39 | 40 | init(name: String, jobDescription: String, salary: String, people: [Person]) { 41 | self.name = name 42 | self.jobDescription = jobDescription 43 | self.salary = salary 44 | self.people = people 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Patterns/Swift-Data/Views/EditEventView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EditEventView.swift 3 | // IOS17-Swift 4 | // 5 | // Created by xqsadness on 28/12/2023. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct EditEventView: View { 11 | @Bindable var event: Event 12 | 13 | var body: some View { 14 | Form{ 15 | TextField("Name of event", text: $event.name) 16 | TextField("Location", text: $event.location) 17 | } 18 | .navigationTitle("Edit event") 19 | .navigationBarTitleDisplayMode(.inline) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Patterns/Swift-Data/Views/EditJobView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EditJobView.swift 3 | // IOS17-Swift 4 | // 5 | // Created by xqsadness on 04/01/2024. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct EditJobView: View { 11 | @Bindable var job: Job 12 | 13 | var body: some View { 14 | Form{ 15 | TextField("Name job", text: $job.name) 16 | TextField("JD", text: $job.jobDescription) 17 | TextField("Salary", text: $job.salary) 18 | 19 | } 20 | .navigationTitle("Edit Job") 21 | .navigationBarTitleDisplayMode(.inline) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Patterns/Swift-Data/Views/EditPersonView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EditPersonView.swift 3 | // IOS17-Swift 4 | // 5 | // Created by xqsadness on 28/12/2023. 6 | // 7 | 8 | import SwiftUI 9 | import SwiftData 10 | import PhotosUI 11 | 12 | struct EditPersonView: View { 13 | 14 | @Environment(\.modelContext) var modelContext 15 | @Bindable var person: Person 16 | @Binding var navigationPath: NavigationPath 17 | 18 | @State private var selectedItem: PhotosPickerItem? 19 | 20 | @Query(sort: [ 21 | SortDescriptor(\Event.name), 22 | SortDescriptor(\Event.location) 23 | ]) var events: [Event] 24 | 25 | var body: some View { 26 | Form{ 27 | Section{ 28 | if let imgData = person.photo,let uiImage = UIImage(data: imgData){ 29 | Image(uiImage: uiImage) 30 | .resizable() 31 | .scaledToFit() 32 | } 33 | 34 | PhotosPicker(selection: $selectedItem, matching: .images) { 35 | Label("Select a photo", systemImage: "person") 36 | } 37 | } 38 | 39 | Section("Name and email"){ 40 | TextField("Name", text: $person.fullName) 41 | .textContentType(.name) 42 | .foregroundStyle(.text) 43 | 44 | TextField("Email address", text: $person.emailAddress) 45 | .textContentType(.emailAddress) 46 | .textInputAutocapitalization(.never) 47 | } 48 | 49 | Section("Jobs"){ 50 | if !person.jobs.isEmpty{ 51 | ForEach(person.jobs){ job in 52 | Text(job.name) 53 | .frame(maxWidth: .infinity, alignment: .leading) 54 | .contentShape(Rectangle()) 55 | .onTapGesture { 56 | navigationPath.append(job) 57 | } 58 | } 59 | } 60 | 61 | Button("Add a new job") { 62 | addJob() 63 | } 64 | } 65 | 66 | Section("Where did you meet them?"){ 67 | Picker("Met at", selection: $person.metAt){ 68 | Text("Unk Event") 69 | .tag(Optional.none) 70 | 71 | if !events.isEmpty{ 72 | Divider() 73 | 74 | ForEach(events){evt in 75 | Text(evt.name) 76 | .tag(Optional(evt)) 77 | } 78 | } 79 | } 80 | 81 | Button("Add a new event") { 82 | addEvent() 83 | } 84 | } 85 | 86 | Section("Notes"){ 87 | TextField("Details about this person", text: $person.details, axis: .vertical) 88 | } 89 | } 90 | .navigationTitle("Edit person") 91 | .navigationBarTitleDisplayMode(.inline) 92 | .navigationDestination(for: Event.self) { evt in 93 | EditEventView(event: evt) 94 | } 95 | .navigationDestination(for: Job.self) { job in 96 | EditJobView(job: job) 97 | } 98 | .onChange(of: selectedItem, loadPhoto) 99 | } 100 | 101 | func addEvent(){ 102 | let evt = Event(name: "", location: "") 103 | modelContext.insert(evt) 104 | navigationPath.append(evt) 105 | } 106 | 107 | func addJob(){ 108 | let job = Job(name: "", jobDescription: "", salary: "", people: []) 109 | 110 | person.jobs.append(job) 111 | navigationPath.append(job) 112 | } 113 | 114 | func loadPhoto(){ 115 | Task{ @MainActor in 116 | person.photo = try await selectedItem?.loadTransferable(type: Data.self) 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /Patterns/Swift-Data/Views/FaceFactsView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FaceFactsView.swift 3 | // IOS17-Swift 4 | // 5 | // Created by xqsadness on 28/12/2023. 6 | // 7 | 8 | import SwiftUI 9 | import SwiftData 10 | 11 | struct FaceFactsView: View { 12 | 13 | @Environment(\.modelContext) var modelContext 14 | @State private var path = NavigationPath() 15 | 16 | @State private var sortOrder = [SortDescriptor(\Person.fullName)] 17 | @State private var searchText = "" 18 | 19 | var body: some View { 20 | NavigationStack(path: $path){ 21 | PeopleView(searchString: searchText, sortOrder: sortOrder) 22 | .navigationTitle("Face facts") 23 | .navigationDestination(for: Person.self) { person in 24 | EditPersonView(person: person, navigationPath: $path) 25 | } 26 | .toolbar{ 27 | Menu("Sort", systemImage: "arrow.up.arrow.down"){ 28 | Picker("Sort", selection: $sortOrder){ 29 | Text("Name (A-Z)") 30 | .tag([SortDescriptor(\Person.fullName)]) 31 | 32 | Text("Name (Z-A)") 33 | .tag([SortDescriptor(\Person.fullName, order: .reverse)]) 34 | } 35 | } 36 | 37 | Button("Add person", systemImage: "plus") { 38 | addPerson() 39 | } 40 | } 41 | .searchable(text: $searchText) 42 | } 43 | } 44 | 45 | func addPerson(){ 46 | let person = Person(name: "", emailAddress: "", details: "", jobs: []) 47 | 48 | modelContext.insert(person) 49 | path.append(person) 50 | } 51 | } 52 | 53 | #Preview { 54 | FaceFactsView() 55 | } 56 | -------------------------------------------------------------------------------- /Patterns/Swift-Data/Views/PeopleView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PeopleView.swift 3 | // IOS17-Swift 4 | // 5 | // Created by xqsadness on 28/12/2023. 6 | // 7 | 8 | import SwiftUI 9 | import SwiftData 10 | 11 | struct PeopleView: View { 12 | @Environment(\.modelContext) var modelContext 13 | @Query var people: [Person] 14 | 15 | init(searchString: String = "", sortOrder: [SortDescriptor] = []){ 16 | _people = Query(filter: #Predicate{ person in 17 | if searchString.isEmpty{ 18 | true 19 | }else{ 20 | person.fullName.localizedStandardContains(searchString) 21 | || person.emailAddress.localizedStandardContains(searchString) 22 | || person.details.localizedStandardContains(searchString) 23 | } 24 | }, sort: sortOrder) 25 | } 26 | 27 | var body: some View { 28 | List{ 29 | ForEach(people){ person in 30 | NavigationLink(value: person){ 31 | Text(person.fullName) 32 | } 33 | } 34 | .onDelete(perform: deletePeople) 35 | } 36 | } 37 | 38 | func deletePeople(at offsets: IndexSet){ 39 | for offset in offsets{ 40 | let person = people[offset] 41 | modelContext.delete(person) 42 | } 43 | } 44 | } 45 | 46 | #Preview { 47 | PeopleView() 48 | } 49 | --------------------------------------------------------------------------------