├── 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 |
--------------------------------------------------------------------------------