├── .github
└── FUNDING.yml
├── .gitignore
├── Gifs
├── 001.gif
├── 002.gif
├── 003.gif
├── 004.gif
├── 005.gif
├── 006.gif
├── 007.gif
├── 008.gif
├── 009.gif
├── 010.gif
├── 011.gif
├── 012.gif
├── 013.gif
├── 014.gif
├── 015.gif
├── 016.gif
├── 017.gif
├── 018.gif
├── 019.gif
├── 020.gif
├── 021.gif
├── 022.gif
├── 023.gif
├── 024.gif
├── 025.gif
├── 026.gif
└── 027.png
├── LICENSE
├── README.md
├── Sponsorship
├── 51111854458284T4.JPG
├── Alipay.JPG
└── Wechat.JPG
├── SwiftUICodeSnippets.xcodeproj
├── project.pbxproj
└── project.xcworkspace
│ └── contents.xcworkspacedata
├── SwiftUICodeSnippets
├── 001-RainbowText
│ └── RainbowTextNumericView.swift
├── 002-ScaledButton
│ └── ScaledButtonStyle.swift
├── 003-ButtonRotationAnimation
│ └── ButtonRotateAnimation.swift
├── 004-SegmentedControl
│ └── CustomSegmentedControl.swift
├── 005-SymbolImageAnimation
│ └── SymbolImageAnimation.swift
├── 006-PopoverTip
│ └── PopoverTipView.swift
├── 007-PolishAnimation
│ └── PolishView.swift
├── 008-NavTransition
│ └── NavigationTransitionAnimationView.swift
├── 009-ImageTransition
│ └── ImageTransitionAnimationView.swift
├── 010-StickyHeader
│ └── StickyHeaderView.swift
├── 011-RainbowButton
│ └── RainbowButton.swift
├── 012-AnimatedBorder
│ └── AnimatedBorderView.swift
├── 013-FunnyToggle
│ └── FunnyToggleView.swift
├── 014-ToDoCell
│ └── TodoCellsView.swift
├── 015-ScrollTransition
│ └── ScrollViewTransition.swift
├── 016-MeshGradient
│ └── MeshGradientView.swift
├── 017-LoginPage
│ └── LoginPage.swift
├── 018-SubscribePage
│ └── SubscribePage.swift
├── 019-NumericAnimation
│ └── NumericAnimationView.swift
├── 020-WaveShader
│ ├── Wave.metal
│ └── WaveShaderView.swift
├── 021-SnowShader
│ ├── SnowShader.metal
│ └── SnowShaderView.swift
├── 022-DaysPostponementView
│ └── DaysPostponementView.swift
├── 023-CalendarCard
│ └── CalendarCardView.swift
├── 024-PurchaseCard
│ └── PurchaseCardView.swift
├── 025-BackgroundAnimation
│ ├── BackgroundColorAnimationView.swift
│ └── WaveShape.swift
├── 026-HorizontalPicker
│ └── HorizontalPickerView.swift
├── 027-StarsBackgroundView
│ └── StartsBackgroundView.swift
├── Assets.xcassets
│ ├── AccentColor.colorset
│ │ └── Contents.json
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ ├── Contents.json
│ ├── DarkBlue.colorset
│ │ └── Contents.json
│ └── images
│ │ ├── Contents.json
│ │ ├── background-1.imageset
│ │ ├── Contents.json
│ │ └── image (2).jpg
│ │ ├── background-2.imageset
│ │ ├── Contents.json
│ │ └── image (3).jpg
│ │ ├── background.imageset
│ │ ├── Contents.json
│ │ └── image (1).jpg
│ │ ├── car-image-1.imageset
│ │ ├── Contents.json
│ │ └── image1.jpg
│ │ ├── car-image-2.imageset
│ │ ├── Contents.json
│ │ └── image2.jpg
│ │ ├── image-1.imageset
│ │ ├── Contents.json
│ │ └── image-1.jpg
│ │ ├── image-2.imageset
│ │ ├── Contents.json
│ │ └── image-2.jpg
│ │ ├── image-3.imageset
│ │ ├── Contents.json
│ │ └── image-3.jpg
│ │ ├── image-4.imageset
│ │ ├── Contents.json
│ │ └── image-4.jpg
│ │ ├── image-5.imageset
│ │ ├── Contents.json
│ │ └── image-5.jpg
│ │ └── image-6.imageset
│ │ ├── Contents.json
│ │ └── image.jpg
├── ContentView.swift
├── Preview Content
│ └── Preview Assets.xcassets
│ │ └── Contents.json
└── SwiftUICodeSnippetsApp.swift
└── Widgets
├── AppIntent.swift
├── Assets.xcassets
├── AccentColor.colorset
│ └── Contents.json
├── AppIcon.appiconset
│ └── Contents.json
├── Contents.json
└── WidgetBackground.colorset
│ └── Contents.json
├── Info.plist
├── Widgets.swift
└── WidgetsBundle.swift
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
12 | polar: # Replace with a single Polar username
13 | buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
14 | thanks_dev: # Replace with a single thanks.dev username
15 | custom: ['https://github.com/Mas0nSun/SwiftUICodeSnippets/blob/main/Sponsorship/Alipay.JPG', 'https://github.com/Mas0nSun/SwiftUICodeSnippets/blob/main//Sponsorship/Wechat.JPG']
16 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## User settings
6 | xcuserdata/
7 |
8 | ## Obj-C/Swift specific
9 | *.hmap
10 |
11 | ## App packaging
12 | *.ipa
13 | *.dSYM.zip
14 | *.dSYM
15 |
16 | ## Playgrounds
17 | timeline.xctimeline
18 | playground.xcworkspace
19 |
20 | # Swift Package Manager
21 | #
22 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
23 | # Packages/
24 | # Package.pins
25 | # Package.resolved
26 | # *.xcodeproj
27 | #
28 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
29 | # hence it is not needed unless you have added a package configuration file to your project
30 | # .swiftpm
31 |
32 | .build/
33 |
34 | # CocoaPods
35 | #
36 | # We recommend against adding the Pods directory to your .gitignore. However
37 | # you should judge for yourself, the pros and cons are mentioned at:
38 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
39 | #
40 | # Pods/
41 | #
42 | # Add this line if you want to avoid checking in source code from the Xcode workspace
43 | # *.xcworkspace
44 |
45 | # Carthage
46 | #
47 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
48 | # Carthage/Checkouts
49 |
50 | Carthage/Build/
51 |
52 | # fastlane
53 | #
54 | # It is recommended to not store the screenshots in the git repo.
55 | # Instead, use fastlane to re-generate the screenshots whenever they are needed.
56 | # For more information about the recommended setup visit:
57 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
58 |
59 | fastlane/report.xml
60 | fastlane/Preview.html
61 | fastlane/screenshots/**/*.png
62 | fastlane/test_output
63 |
--------------------------------------------------------------------------------
/Gifs/001.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mas0nSun/SwiftUICodeSnippets/2dcf104ed16fb79a01a9a802d6c63236d74de5b2/Gifs/001.gif
--------------------------------------------------------------------------------
/Gifs/002.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mas0nSun/SwiftUICodeSnippets/2dcf104ed16fb79a01a9a802d6c63236d74de5b2/Gifs/002.gif
--------------------------------------------------------------------------------
/Gifs/003.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mas0nSun/SwiftUICodeSnippets/2dcf104ed16fb79a01a9a802d6c63236d74de5b2/Gifs/003.gif
--------------------------------------------------------------------------------
/Gifs/004.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mas0nSun/SwiftUICodeSnippets/2dcf104ed16fb79a01a9a802d6c63236d74de5b2/Gifs/004.gif
--------------------------------------------------------------------------------
/Gifs/005.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mas0nSun/SwiftUICodeSnippets/2dcf104ed16fb79a01a9a802d6c63236d74de5b2/Gifs/005.gif
--------------------------------------------------------------------------------
/Gifs/006.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mas0nSun/SwiftUICodeSnippets/2dcf104ed16fb79a01a9a802d6c63236d74de5b2/Gifs/006.gif
--------------------------------------------------------------------------------
/Gifs/007.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mas0nSun/SwiftUICodeSnippets/2dcf104ed16fb79a01a9a802d6c63236d74de5b2/Gifs/007.gif
--------------------------------------------------------------------------------
/Gifs/008.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mas0nSun/SwiftUICodeSnippets/2dcf104ed16fb79a01a9a802d6c63236d74de5b2/Gifs/008.gif
--------------------------------------------------------------------------------
/Gifs/009.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mas0nSun/SwiftUICodeSnippets/2dcf104ed16fb79a01a9a802d6c63236d74de5b2/Gifs/009.gif
--------------------------------------------------------------------------------
/Gifs/010.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mas0nSun/SwiftUICodeSnippets/2dcf104ed16fb79a01a9a802d6c63236d74de5b2/Gifs/010.gif
--------------------------------------------------------------------------------
/Gifs/011.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mas0nSun/SwiftUICodeSnippets/2dcf104ed16fb79a01a9a802d6c63236d74de5b2/Gifs/011.gif
--------------------------------------------------------------------------------
/Gifs/012.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mas0nSun/SwiftUICodeSnippets/2dcf104ed16fb79a01a9a802d6c63236d74de5b2/Gifs/012.gif
--------------------------------------------------------------------------------
/Gifs/013.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mas0nSun/SwiftUICodeSnippets/2dcf104ed16fb79a01a9a802d6c63236d74de5b2/Gifs/013.gif
--------------------------------------------------------------------------------
/Gifs/014.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mas0nSun/SwiftUICodeSnippets/2dcf104ed16fb79a01a9a802d6c63236d74de5b2/Gifs/014.gif
--------------------------------------------------------------------------------
/Gifs/015.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mas0nSun/SwiftUICodeSnippets/2dcf104ed16fb79a01a9a802d6c63236d74de5b2/Gifs/015.gif
--------------------------------------------------------------------------------
/Gifs/016.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mas0nSun/SwiftUICodeSnippets/2dcf104ed16fb79a01a9a802d6c63236d74de5b2/Gifs/016.gif
--------------------------------------------------------------------------------
/Gifs/017.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mas0nSun/SwiftUICodeSnippets/2dcf104ed16fb79a01a9a802d6c63236d74de5b2/Gifs/017.gif
--------------------------------------------------------------------------------
/Gifs/018.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mas0nSun/SwiftUICodeSnippets/2dcf104ed16fb79a01a9a802d6c63236d74de5b2/Gifs/018.gif
--------------------------------------------------------------------------------
/Gifs/019.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mas0nSun/SwiftUICodeSnippets/2dcf104ed16fb79a01a9a802d6c63236d74de5b2/Gifs/019.gif
--------------------------------------------------------------------------------
/Gifs/020.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mas0nSun/SwiftUICodeSnippets/2dcf104ed16fb79a01a9a802d6c63236d74de5b2/Gifs/020.gif
--------------------------------------------------------------------------------
/Gifs/021.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mas0nSun/SwiftUICodeSnippets/2dcf104ed16fb79a01a9a802d6c63236d74de5b2/Gifs/021.gif
--------------------------------------------------------------------------------
/Gifs/022.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mas0nSun/SwiftUICodeSnippets/2dcf104ed16fb79a01a9a802d6c63236d74de5b2/Gifs/022.gif
--------------------------------------------------------------------------------
/Gifs/023.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mas0nSun/SwiftUICodeSnippets/2dcf104ed16fb79a01a9a802d6c63236d74de5b2/Gifs/023.gif
--------------------------------------------------------------------------------
/Gifs/024.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mas0nSun/SwiftUICodeSnippets/2dcf104ed16fb79a01a9a802d6c63236d74de5b2/Gifs/024.gif
--------------------------------------------------------------------------------
/Gifs/025.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mas0nSun/SwiftUICodeSnippets/2dcf104ed16fb79a01a9a802d6c63236d74de5b2/Gifs/025.gif
--------------------------------------------------------------------------------
/Gifs/026.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mas0nSun/SwiftUICodeSnippets/2dcf104ed16fb79a01a9a802d6c63236d74de5b2/Gifs/026.gif
--------------------------------------------------------------------------------
/Gifs/027.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mas0nSun/SwiftUICodeSnippets/2dcf104ed16fb79a01a9a802d6c63236d74de5b2/Gifs/027.png
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Mason Sun
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # SwiftUICodeSnippets
2 | 这里是短视频中创建的所有代码, 希望大家喜欢, 如果发现有任何问题欢迎大家提 issues 或者 PR
3 |
4 | # 通知
5 | 目前该仓库为公开状态, 后续会变为私有状态, 已付费的用户会收到邀请, 点击接受后可正常访问, 没有收到邀请的话可以联系我, 目前是是早鸟价大家可以在小红书/B站购买
6 |
7 | ## 目录说明
8 | 001-500 为已发布的代码
9 |
10 | 501-??? 为草稿代码, 属于制作视频时的副产物 = =
11 |
12 | # 预览效果
13 |
14 | ### [027-StartsBackground](/SwiftUICodeSnippets/027-StartsBackgroundView)
15 |
16 |
17 | ### [026-HorizontalPicker](/SwiftUICodeSnippets/026-HorizontalPicker)
18 |
19 |
20 | ### [025-BackgroundAnimation](/SwiftUICodeSnippets/025-BackgroundAnimation)
21 |
22 |
23 | ### [024-PurchaseCard](/SwiftUICodeSnippets/024-PurchaseCard)
24 |
25 |
26 | ### [023-CalendarCard](/SwiftUICodeSnippets/023-CalendarCard)
27 |
28 |
29 | ### [022-DaysPostponement](/SwiftUICodeSnippets/022-DaysPostponement)
30 |
31 |
32 | ### [021-SnowShader](/SwiftUICodeSnippets/021-SnowShader)
33 |
34 |
35 | ### [020-WaveShader](/SwiftUICodeSnippets/020-WaveShader)
36 |
37 |
38 | ### [019-NumericAnimation](/SwiftUICodeSnippets/019-NumericAnimation)
39 |
40 |
41 | ### [018-SubscribePage](/SwiftUICodeSnippets/018-SubscribePage)
42 |
43 |
44 | ### [017-LoginPage](/SwiftUICodeSnippets/017-LoginPage)
45 |
46 |
47 | ### [016-MeshGradient](/SwiftUICodeSnippets/016-MeshGradient)
48 |
49 |
50 | ### [015-ScrollTransition](/SwiftUICodeSnippets/015-ScrollTransition)
51 |
52 |
53 | ### [014-TodoCell](/SwiftUICodeSnippets/014-TodoCell)
54 |
55 |
56 | ### [013-FunnyToggle](/SwiftUICodeSnippets/013-FunnyToggle)
57 |
58 |
59 | ### [012-AnimatedBorder](/SwiftUICodeSnippets/012-AnimatedBorder)
60 |
61 |
62 | ### [011-RainbowButton](/SwiftUICodeSnippets/011-RainbowButton)
63 |
64 |
65 | ### [010-StickyHeader](/SwiftUICodeSnippets/010-StickyHeader)
66 |
67 |
68 | ### [009-ImageTransition](/SwiftUICodeSnippets/009-ImageTransition)
69 |
70 |
71 | ### [008-NavigationTransition](/SwiftUICodeSnippets/008-NavTransition)
72 |
73 |
74 | ### [007-PolishAnimation](/SwiftUICodeSnippets/007-PolishAnimation)
75 |
76 |
77 | ### [006-PopoverTip](/SwiftUICodeSnippets/006-PopoverTip)
78 |
79 |
80 | ### [005-SymbolImageAnimation](/SwiftUICodeSnippets/005-SymbolImageAnimation)
81 |
82 |
83 | ### [004-SegmentedControl](/SwiftUICodeSnippets/004-SegmentedControl)
84 |
85 |
86 | ### [003-ButtonRotationAnimation](/SwiftUICodeSnippets/003-ButtonRotationAnimation)
87 |
88 |
89 | ### [002-ScaledButton](/SwiftUICodeSnippets/002-ScaledButton)
90 |
91 |
92 | ### [001-RainbowText](/SwiftUICodeSnippets/001-RainbowText)
93 |
94 |
--------------------------------------------------------------------------------
/Sponsorship/51111854458284T4.JPG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mas0nSun/SwiftUICodeSnippets/2dcf104ed16fb79a01a9a802d6c63236d74de5b2/Sponsorship/51111854458284T4.JPG
--------------------------------------------------------------------------------
/Sponsorship/Alipay.JPG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mas0nSun/SwiftUICodeSnippets/2dcf104ed16fb79a01a9a802d6c63236d74de5b2/Sponsorship/Alipay.JPG
--------------------------------------------------------------------------------
/Sponsorship/Wechat.JPG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mas0nSun/SwiftUICodeSnippets/2dcf104ed16fb79a01a9a802d6c63236d74de5b2/Sponsorship/Wechat.JPG
--------------------------------------------------------------------------------
/SwiftUICodeSnippets.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 77;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | DA1EBD7B2D3F6EED0087436D /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA1EBD7A2D3F6EED0087436D /* WidgetKit.framework */; };
11 | DA1EBD7D2D3F6EED0087436D /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA1EBD7C2D3F6EED0087436D /* SwiftUI.framework */; };
12 | DA1EBD8A2D3F6EEE0087436D /* WidgetsExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = DA1EBD782D3F6EEC0087436D /* WidgetsExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
13 | /* End PBXBuildFile section */
14 |
15 | /* Begin PBXContainerItemProxy section */
16 | DA1EBD882D3F6EEE0087436D /* PBXContainerItemProxy */ = {
17 | isa = PBXContainerItemProxy;
18 | containerPortal = DAAB5A412D18405100D5E1B8 /* Project object */;
19 | proxyType = 1;
20 | remoteGlobalIDString = DA1EBD772D3F6EEC0087436D;
21 | remoteInfo = WidgetsExtension;
22 | };
23 | /* End PBXContainerItemProxy section */
24 |
25 | /* Begin PBXCopyFilesBuildPhase section */
26 | DA1EBD8B2D3F6EEE0087436D /* Embed Foundation Extensions */ = {
27 | isa = PBXCopyFilesBuildPhase;
28 | buildActionMask = 2147483647;
29 | dstPath = "";
30 | dstSubfolderSpec = 13;
31 | files = (
32 | DA1EBD8A2D3F6EEE0087436D /* WidgetsExtension.appex in Embed Foundation Extensions */,
33 | );
34 | name = "Embed Foundation Extensions";
35 | runOnlyForDeploymentPostprocessing = 0;
36 | };
37 | /* End PBXCopyFilesBuildPhase section */
38 |
39 | /* Begin PBXFileReference section */
40 | DA1EBD782D3F6EEC0087436D /* WidgetsExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = WidgetsExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
41 | DA1EBD7A2D3F6EED0087436D /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; };
42 | DA1EBD7C2D3F6EED0087436D /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; };
43 | DAAB5A492D18405100D5E1B8 /* SwiftUICodeSnippets.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwiftUICodeSnippets.app; sourceTree = BUILT_PRODUCTS_DIR; };
44 | /* End PBXFileReference section */
45 |
46 | /* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
47 | DA1EBD8E2D3F6EEE0087436D /* Exceptions for "Widgets" folder in "WidgetsExtension" target */ = {
48 | isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
49 | membershipExceptions = (
50 | Info.plist,
51 | );
52 | target = DA1EBD772D3F6EEC0087436D /* WidgetsExtension */;
53 | };
54 | /* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
55 |
56 | /* Begin PBXFileSystemSynchronizedRootGroup section */
57 | DA1EBD7E2D3F6EED0087436D /* Widgets */ = {
58 | isa = PBXFileSystemSynchronizedRootGroup;
59 | exceptions = (
60 | DA1EBD8E2D3F6EEE0087436D /* Exceptions for "Widgets" folder in "WidgetsExtension" target */,
61 | );
62 | path = Widgets;
63 | sourceTree = "";
64 | };
65 | DAAB5A4B2D18405100D5E1B8 /* SwiftUICodeSnippets */ = {
66 | isa = PBXFileSystemSynchronizedRootGroup;
67 | path = SwiftUICodeSnippets;
68 | sourceTree = "";
69 | };
70 | /* End PBXFileSystemSynchronizedRootGroup section */
71 |
72 | /* Begin PBXFrameworksBuildPhase section */
73 | DA1EBD752D3F6EEC0087436D /* Frameworks */ = {
74 | isa = PBXFrameworksBuildPhase;
75 | buildActionMask = 2147483647;
76 | files = (
77 | DA1EBD7D2D3F6EED0087436D /* SwiftUI.framework in Frameworks */,
78 | DA1EBD7B2D3F6EED0087436D /* WidgetKit.framework in Frameworks */,
79 | );
80 | runOnlyForDeploymentPostprocessing = 0;
81 | };
82 | DAAB5A462D18405100D5E1B8 /* Frameworks */ = {
83 | isa = PBXFrameworksBuildPhase;
84 | buildActionMask = 2147483647;
85 | files = (
86 | );
87 | runOnlyForDeploymentPostprocessing = 0;
88 | };
89 | /* End PBXFrameworksBuildPhase section */
90 |
91 | /* Begin PBXGroup section */
92 | DA1EBD792D3F6EEC0087436D /* Frameworks */ = {
93 | isa = PBXGroup;
94 | children = (
95 | DA1EBD7A2D3F6EED0087436D /* WidgetKit.framework */,
96 | DA1EBD7C2D3F6EED0087436D /* SwiftUI.framework */,
97 | );
98 | name = Frameworks;
99 | sourceTree = "";
100 | };
101 | DAAB5A402D18405100D5E1B8 = {
102 | isa = PBXGroup;
103 | children = (
104 | DAAB5A4B2D18405100D5E1B8 /* SwiftUICodeSnippets */,
105 | DA1EBD7E2D3F6EED0087436D /* Widgets */,
106 | DA1EBD792D3F6EEC0087436D /* Frameworks */,
107 | DAAB5A4A2D18405100D5E1B8 /* Products */,
108 | );
109 | sourceTree = "";
110 | };
111 | DAAB5A4A2D18405100D5E1B8 /* Products */ = {
112 | isa = PBXGroup;
113 | children = (
114 | DAAB5A492D18405100D5E1B8 /* SwiftUICodeSnippets.app */,
115 | DA1EBD782D3F6EEC0087436D /* WidgetsExtension.appex */,
116 | );
117 | name = Products;
118 | sourceTree = "";
119 | };
120 | /* End PBXGroup section */
121 |
122 | /* Begin PBXNativeTarget section */
123 | DA1EBD772D3F6EEC0087436D /* WidgetsExtension */ = {
124 | isa = PBXNativeTarget;
125 | buildConfigurationList = DA1EBD8F2D3F6EEE0087436D /* Build configuration list for PBXNativeTarget "WidgetsExtension" */;
126 | buildPhases = (
127 | DA1EBD742D3F6EEC0087436D /* Sources */,
128 | DA1EBD752D3F6EEC0087436D /* Frameworks */,
129 | DA1EBD762D3F6EEC0087436D /* Resources */,
130 | );
131 | buildRules = (
132 | );
133 | dependencies = (
134 | );
135 | fileSystemSynchronizedGroups = (
136 | DA1EBD7E2D3F6EED0087436D /* Widgets */,
137 | );
138 | name = WidgetsExtension;
139 | packageProductDependencies = (
140 | );
141 | productName = WidgetsExtension;
142 | productReference = DA1EBD782D3F6EEC0087436D /* WidgetsExtension.appex */;
143 | productType = "com.apple.product-type.app-extension";
144 | };
145 | DAAB5A482D18405100D5E1B8 /* SwiftUICodeSnippets */ = {
146 | isa = PBXNativeTarget;
147 | buildConfigurationList = DAAB5A572D18405300D5E1B8 /* Build configuration list for PBXNativeTarget "SwiftUICodeSnippets" */;
148 | buildPhases = (
149 | DAAB5A452D18405100D5E1B8 /* Sources */,
150 | DAAB5A462D18405100D5E1B8 /* Frameworks */,
151 | DAAB5A472D18405100D5E1B8 /* Resources */,
152 | DA1EBD8B2D3F6EEE0087436D /* Embed Foundation Extensions */,
153 | );
154 | buildRules = (
155 | );
156 | dependencies = (
157 | DA1EBD892D3F6EEE0087436D /* PBXTargetDependency */,
158 | );
159 | fileSystemSynchronizedGroups = (
160 | DAAB5A4B2D18405100D5E1B8 /* SwiftUICodeSnippets */,
161 | );
162 | name = SwiftUICodeSnippets;
163 | packageProductDependencies = (
164 | );
165 | productName = SwiftUICodeSnippets;
166 | productReference = DAAB5A492D18405100D5E1B8 /* SwiftUICodeSnippets.app */;
167 | productType = "com.apple.product-type.application";
168 | };
169 | /* End PBXNativeTarget section */
170 |
171 | /* Begin PBXProject section */
172 | DAAB5A412D18405100D5E1B8 /* Project object */ = {
173 | isa = PBXProject;
174 | attributes = {
175 | BuildIndependentTargetsInParallel = 1;
176 | LastSwiftUpdateCheck = 1610;
177 | LastUpgradeCheck = 1610;
178 | TargetAttributes = {
179 | DA1EBD772D3F6EEC0087436D = {
180 | CreatedOnToolsVersion = 16.1;
181 | };
182 | DAAB5A482D18405100D5E1B8 = {
183 | CreatedOnToolsVersion = 16.1;
184 | };
185 | };
186 | };
187 | buildConfigurationList = DAAB5A442D18405100D5E1B8 /* Build configuration list for PBXProject "SwiftUICodeSnippets" */;
188 | developmentRegion = en;
189 | hasScannedForEncodings = 0;
190 | knownRegions = (
191 | en,
192 | Base,
193 | );
194 | mainGroup = DAAB5A402D18405100D5E1B8;
195 | minimizedProjectReferenceProxies = 1;
196 | preferredProjectObjectVersion = 77;
197 | productRefGroup = DAAB5A4A2D18405100D5E1B8 /* Products */;
198 | projectDirPath = "";
199 | projectRoot = "";
200 | targets = (
201 | DAAB5A482D18405100D5E1B8 /* SwiftUICodeSnippets */,
202 | DA1EBD772D3F6EEC0087436D /* WidgetsExtension */,
203 | );
204 | };
205 | /* End PBXProject section */
206 |
207 | /* Begin PBXResourcesBuildPhase section */
208 | DA1EBD762D3F6EEC0087436D /* Resources */ = {
209 | isa = PBXResourcesBuildPhase;
210 | buildActionMask = 2147483647;
211 | files = (
212 | );
213 | runOnlyForDeploymentPostprocessing = 0;
214 | };
215 | DAAB5A472D18405100D5E1B8 /* Resources */ = {
216 | isa = PBXResourcesBuildPhase;
217 | buildActionMask = 2147483647;
218 | files = (
219 | );
220 | runOnlyForDeploymentPostprocessing = 0;
221 | };
222 | /* End PBXResourcesBuildPhase section */
223 |
224 | /* Begin PBXSourcesBuildPhase section */
225 | DA1EBD742D3F6EEC0087436D /* Sources */ = {
226 | isa = PBXSourcesBuildPhase;
227 | buildActionMask = 2147483647;
228 | files = (
229 | );
230 | runOnlyForDeploymentPostprocessing = 0;
231 | };
232 | DAAB5A452D18405100D5E1B8 /* Sources */ = {
233 | isa = PBXSourcesBuildPhase;
234 | buildActionMask = 2147483647;
235 | files = (
236 | );
237 | runOnlyForDeploymentPostprocessing = 0;
238 | };
239 | /* End PBXSourcesBuildPhase section */
240 |
241 | /* Begin PBXTargetDependency section */
242 | DA1EBD892D3F6EEE0087436D /* PBXTargetDependency */ = {
243 | isa = PBXTargetDependency;
244 | target = DA1EBD772D3F6EEC0087436D /* WidgetsExtension */;
245 | targetProxy = DA1EBD882D3F6EEE0087436D /* PBXContainerItemProxy */;
246 | };
247 | /* End PBXTargetDependency section */
248 |
249 | /* Begin XCBuildConfiguration section */
250 | DA1EBD8C2D3F6EEE0087436D /* Debug */ = {
251 | isa = XCBuildConfiguration;
252 | buildSettings = {
253 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
254 | ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
255 | CODE_SIGN_STYLE = Automatic;
256 | CURRENT_PROJECT_VERSION = 1;
257 | DEVELOPMENT_TEAM = 4TJ7K2DH6D;
258 | GENERATE_INFOPLIST_FILE = YES;
259 | INFOPLIST_FILE = Widgets/Info.plist;
260 | INFOPLIST_KEY_CFBundleDisplayName = Widgets;
261 | INFOPLIST_KEY_NSHumanReadableCopyright = "";
262 | LD_RUNPATH_SEARCH_PATHS = (
263 | "$(inherited)",
264 | "@executable_path/Frameworks",
265 | "@executable_path/../../Frameworks",
266 | );
267 | MARKETING_VERSION = 1.0;
268 | PRODUCT_BUNDLE_IDENTIFIER = com.masonsun.SwiftUICodeSnippets.Widgets;
269 | PRODUCT_NAME = "$(TARGET_NAME)";
270 | SKIP_INSTALL = YES;
271 | SWIFT_EMIT_LOC_STRINGS = YES;
272 | SWIFT_VERSION = 5.0;
273 | TARGETED_DEVICE_FAMILY = "1,2";
274 | };
275 | name = Debug;
276 | };
277 | DA1EBD8D2D3F6EEE0087436D /* Release */ = {
278 | isa = XCBuildConfiguration;
279 | buildSettings = {
280 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
281 | ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
282 | CODE_SIGN_STYLE = Automatic;
283 | CURRENT_PROJECT_VERSION = 1;
284 | DEVELOPMENT_TEAM = 4TJ7K2DH6D;
285 | GENERATE_INFOPLIST_FILE = YES;
286 | INFOPLIST_FILE = Widgets/Info.plist;
287 | INFOPLIST_KEY_CFBundleDisplayName = Widgets;
288 | INFOPLIST_KEY_NSHumanReadableCopyright = "";
289 | LD_RUNPATH_SEARCH_PATHS = (
290 | "$(inherited)",
291 | "@executable_path/Frameworks",
292 | "@executable_path/../../Frameworks",
293 | );
294 | MARKETING_VERSION = 1.0;
295 | PRODUCT_BUNDLE_IDENTIFIER = com.masonsun.SwiftUICodeSnippets.Widgets;
296 | PRODUCT_NAME = "$(TARGET_NAME)";
297 | SKIP_INSTALL = YES;
298 | SWIFT_EMIT_LOC_STRINGS = YES;
299 | SWIFT_VERSION = 5.0;
300 | TARGETED_DEVICE_FAMILY = "1,2";
301 | };
302 | name = Release;
303 | };
304 | DAAB5A552D18405300D5E1B8 /* Debug */ = {
305 | isa = XCBuildConfiguration;
306 | buildSettings = {
307 | ALWAYS_SEARCH_USER_PATHS = NO;
308 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
309 | CLANG_ANALYZER_NONNULL = YES;
310 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
311 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
312 | CLANG_ENABLE_MODULES = YES;
313 | CLANG_ENABLE_OBJC_ARC = YES;
314 | CLANG_ENABLE_OBJC_WEAK = YES;
315 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
316 | CLANG_WARN_BOOL_CONVERSION = YES;
317 | CLANG_WARN_COMMA = YES;
318 | CLANG_WARN_CONSTANT_CONVERSION = YES;
319 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
320 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
321 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
322 | CLANG_WARN_EMPTY_BODY = YES;
323 | CLANG_WARN_ENUM_CONVERSION = YES;
324 | CLANG_WARN_INFINITE_RECURSION = YES;
325 | CLANG_WARN_INT_CONVERSION = YES;
326 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
327 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
328 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
329 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
330 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
331 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
332 | CLANG_WARN_STRICT_PROTOTYPES = YES;
333 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
334 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
335 | CLANG_WARN_UNREACHABLE_CODE = YES;
336 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
337 | COPY_PHASE_STRIP = NO;
338 | DEBUG_INFORMATION_FORMAT = dwarf;
339 | ENABLE_STRICT_OBJC_MSGSEND = YES;
340 | ENABLE_TESTABILITY = YES;
341 | ENABLE_USER_SCRIPT_SANDBOXING = YES;
342 | GCC_C_LANGUAGE_STANDARD = gnu17;
343 | GCC_DYNAMIC_NO_PIC = NO;
344 | GCC_NO_COMMON_BLOCKS = YES;
345 | GCC_OPTIMIZATION_LEVEL = 0;
346 | GCC_PREPROCESSOR_DEFINITIONS = (
347 | "DEBUG=1",
348 | "$(inherited)",
349 | );
350 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
351 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
352 | GCC_WARN_UNDECLARED_SELECTOR = YES;
353 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
354 | GCC_WARN_UNUSED_FUNCTION = YES;
355 | GCC_WARN_UNUSED_VARIABLE = YES;
356 | IPHONEOS_DEPLOYMENT_TARGET = 18.1;
357 | LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
358 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
359 | MTL_FAST_MATH = YES;
360 | ONLY_ACTIVE_ARCH = YES;
361 | SDKROOT = iphoneos;
362 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
363 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
364 | };
365 | name = Debug;
366 | };
367 | DAAB5A562D18405300D5E1B8 /* Release */ = {
368 | isa = XCBuildConfiguration;
369 | buildSettings = {
370 | ALWAYS_SEARCH_USER_PATHS = NO;
371 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
372 | CLANG_ANALYZER_NONNULL = YES;
373 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
374 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
375 | CLANG_ENABLE_MODULES = YES;
376 | CLANG_ENABLE_OBJC_ARC = YES;
377 | CLANG_ENABLE_OBJC_WEAK = YES;
378 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
379 | CLANG_WARN_BOOL_CONVERSION = YES;
380 | CLANG_WARN_COMMA = YES;
381 | CLANG_WARN_CONSTANT_CONVERSION = YES;
382 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
383 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
384 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
385 | CLANG_WARN_EMPTY_BODY = YES;
386 | CLANG_WARN_ENUM_CONVERSION = YES;
387 | CLANG_WARN_INFINITE_RECURSION = YES;
388 | CLANG_WARN_INT_CONVERSION = YES;
389 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
390 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
391 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
392 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
393 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
394 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
395 | CLANG_WARN_STRICT_PROTOTYPES = YES;
396 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
397 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
398 | CLANG_WARN_UNREACHABLE_CODE = YES;
399 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
400 | COPY_PHASE_STRIP = NO;
401 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
402 | ENABLE_NS_ASSERTIONS = NO;
403 | ENABLE_STRICT_OBJC_MSGSEND = YES;
404 | ENABLE_USER_SCRIPT_SANDBOXING = YES;
405 | GCC_C_LANGUAGE_STANDARD = gnu17;
406 | GCC_NO_COMMON_BLOCKS = YES;
407 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
408 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
409 | GCC_WARN_UNDECLARED_SELECTOR = YES;
410 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
411 | GCC_WARN_UNUSED_FUNCTION = YES;
412 | GCC_WARN_UNUSED_VARIABLE = YES;
413 | IPHONEOS_DEPLOYMENT_TARGET = 18.1;
414 | LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
415 | MTL_ENABLE_DEBUG_INFO = NO;
416 | MTL_FAST_MATH = YES;
417 | SDKROOT = iphoneos;
418 | SWIFT_COMPILATION_MODE = wholemodule;
419 | VALIDATE_PRODUCT = YES;
420 | };
421 | name = Release;
422 | };
423 | DAAB5A582D18405300D5E1B8 /* Debug */ = {
424 | isa = XCBuildConfiguration;
425 | buildSettings = {
426 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
427 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
428 | CODE_SIGN_STYLE = Automatic;
429 | CURRENT_PROJECT_VERSION = 1;
430 | DEVELOPMENT_ASSET_PATHS = "\"SwiftUICodeSnippets/Preview Content\"";
431 | DEVELOPMENT_TEAM = 4TJ7K2DH6D;
432 | ENABLE_PREVIEWS = YES;
433 | GENERATE_INFOPLIST_FILE = YES;
434 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
435 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
436 | INFOPLIST_KEY_UILaunchScreen_Generation = YES;
437 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
438 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
439 | LD_RUNPATH_SEARCH_PATHS = (
440 | "$(inherited)",
441 | "@executable_path/Frameworks",
442 | );
443 | MARKETING_VERSION = 1.0;
444 | PRODUCT_BUNDLE_IDENTIFIER = com.masonsun.SwiftUICodeSnippets;
445 | PRODUCT_NAME = "$(TARGET_NAME)";
446 | SWIFT_EMIT_LOC_STRINGS = YES;
447 | SWIFT_VERSION = 5.0;
448 | TARGETED_DEVICE_FAMILY = "1,2";
449 | };
450 | name = Debug;
451 | };
452 | DAAB5A592D18405300D5E1B8 /* Release */ = {
453 | isa = XCBuildConfiguration;
454 | buildSettings = {
455 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
456 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
457 | CODE_SIGN_STYLE = Automatic;
458 | CURRENT_PROJECT_VERSION = 1;
459 | DEVELOPMENT_ASSET_PATHS = "\"SwiftUICodeSnippets/Preview Content\"";
460 | DEVELOPMENT_TEAM = 4TJ7K2DH6D;
461 | ENABLE_PREVIEWS = YES;
462 | GENERATE_INFOPLIST_FILE = YES;
463 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
464 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
465 | INFOPLIST_KEY_UILaunchScreen_Generation = YES;
466 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
467 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
468 | LD_RUNPATH_SEARCH_PATHS = (
469 | "$(inherited)",
470 | "@executable_path/Frameworks",
471 | );
472 | MARKETING_VERSION = 1.0;
473 | PRODUCT_BUNDLE_IDENTIFIER = com.masonsun.SwiftUICodeSnippets;
474 | PRODUCT_NAME = "$(TARGET_NAME)";
475 | SWIFT_EMIT_LOC_STRINGS = YES;
476 | SWIFT_VERSION = 5.0;
477 | TARGETED_DEVICE_FAMILY = "1,2";
478 | };
479 | name = Release;
480 | };
481 | /* End XCBuildConfiguration section */
482 |
483 | /* Begin XCConfigurationList section */
484 | DA1EBD8F2D3F6EEE0087436D /* Build configuration list for PBXNativeTarget "WidgetsExtension" */ = {
485 | isa = XCConfigurationList;
486 | buildConfigurations = (
487 | DA1EBD8C2D3F6EEE0087436D /* Debug */,
488 | DA1EBD8D2D3F6EEE0087436D /* Release */,
489 | );
490 | defaultConfigurationIsVisible = 0;
491 | defaultConfigurationName = Release;
492 | };
493 | DAAB5A442D18405100D5E1B8 /* Build configuration list for PBXProject "SwiftUICodeSnippets" */ = {
494 | isa = XCConfigurationList;
495 | buildConfigurations = (
496 | DAAB5A552D18405300D5E1B8 /* Debug */,
497 | DAAB5A562D18405300D5E1B8 /* Release */,
498 | );
499 | defaultConfigurationIsVisible = 0;
500 | defaultConfigurationName = Release;
501 | };
502 | DAAB5A572D18405300D5E1B8 /* Build configuration list for PBXNativeTarget "SwiftUICodeSnippets" */ = {
503 | isa = XCConfigurationList;
504 | buildConfigurations = (
505 | DAAB5A582D18405300D5E1B8 /* Debug */,
506 | DAAB5A592D18405300D5E1B8 /* Release */,
507 | );
508 | defaultConfigurationIsVisible = 0;
509 | defaultConfigurationName = Release;
510 | };
511 | /* End XCConfigurationList section */
512 | };
513 | rootObject = DAAB5A412D18405100D5E1B8 /* Project object */;
514 | }
515 |
--------------------------------------------------------------------------------
/SwiftUICodeSnippets.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/SwiftUICodeSnippets/001-RainbowText/RainbowTextNumericView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RainbowTextNumericView.swift
3 | // SwiftUICodeSnippets
4 | //
5 | // Created by Mason Sun on 2024/12/21.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct RainbowTextNumericView: View {
11 | @State private var counter: Int = 0
12 |
13 | var body: some View {
14 | Text("Welcome to SwiftUI! \(counter)")
15 | .font(.title)
16 | .foregroundStyle(
17 | .linearGradient(
18 | colors: [.red, .green, .blue],
19 | startPoint: .leading,
20 | endPoint: .trailing
21 | )
22 | )
23 | .contentTransition(.numericText(value: Double(counter)))
24 | .onTapGesture {
25 | withAnimation {
26 | counter += 1
27 | }
28 | }
29 | }
30 | }
31 |
32 | #Preview {
33 | RainbowTextNumericView()
34 | }
35 |
--------------------------------------------------------------------------------
/SwiftUICodeSnippets/002-ScaledButton/ScaledButtonStyle.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ScaledButtonStyle.swift
3 | // SwiftUICodeSnippets
4 | //
5 | // Created by Mason Sun on 2024/12/21.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct ScaledButtonStyle: ButtonStyle {
11 | func makeBody(configuration: Configuration) -> some View {
12 | configuration
13 | .label
14 | .scaleEffect(configuration.isPressed ? 0.9 : 1)
15 | .animation(.interactiveSpring(), value: configuration.isPressed)
16 | }
17 | }
18 |
19 | struct ScaledButtonView: View {
20 | var body: some View {
21 | Button {
22 | //
23 | } label: {
24 | Text("Hello, world")
25 | .fontWeight(.medium)
26 | .foregroundStyle(.white)
27 | .frame(maxWidth: .infinity)
28 | .padding()
29 | .background(Color.black)
30 | .clipShape(.capsule)
31 | }
32 | .buttonStyle(ScaledButtonStyle())
33 | .padding()
34 | }
35 | }
36 |
37 | #Preview {
38 | ScaledButtonView()
39 | }
40 |
--------------------------------------------------------------------------------
/SwiftUICodeSnippets/003-ButtonRotationAnimation/ButtonRotateAnimation.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ButtonRotateAnimation.swift
3 | // SwiftUICodeSnippets
4 | //
5 | // Created by Mason Sun on 2024/12/21.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct ButtonRotateAnimation: View {
11 | @State private var isRotated = false
12 |
13 | var body: some View {
14 | Button {
15 | isRotated.toggle()
16 | } label: {
17 | Image(systemName: "chevron.right.circle")
18 | .font(.largeTitle)
19 | .rotationEffect(.degrees(isRotated ? 90 : 0))
20 | .animation(.default, value: isRotated)
21 | }
22 | }
23 | }
24 |
25 | #Preview {
26 | ButtonRotateAnimation()
27 | }
28 |
--------------------------------------------------------------------------------
/SwiftUICodeSnippets/004-SegmentedControl/CustomSegmentedControl.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CustomSegmentedControl.swift
3 | // SwiftUICodeSnippets
4 | //
5 | // Created by Mason Sun on 2024/12/21.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct CustomSegmentedControl: View {
11 | @Namespace var nameSpace
12 | @State private var currentTab: Tab = .first
13 |
14 | var body: some View {
15 | ZStack {
16 | Color(uiColor: .secondarySystemBackground)
17 | .ignoresSafeArea()
18 | HStack(spacing: 0) {
19 | ForEach(Tab.allCases, id: \.self) { tab in
20 | Button {
21 | withAnimation {
22 | currentTab = tab
23 | }
24 | } label: {
25 | Text(tab.rawValue)
26 | .foregroundStyle(.black)
27 | .padding(.vertical, 8)
28 | .frame(maxWidth: .infinity)
29 | .background {
30 | if currentTab == tab {
31 | Capsule()
32 | .fill(.white)
33 | .matchedGeometryEffect(
34 | id: "Tab",
35 | in: nameSpace
36 | )
37 | }
38 | }
39 | }
40 | }
41 | }
42 | .frame(width: 200)
43 | .padding(2)
44 | .background {
45 | Capsule()
46 | .fill(.gray.opacity(0.5))
47 | }
48 | }
49 | }
50 | }
51 |
52 | enum Tab: String, CaseIterable {
53 | case first = "First"
54 | case second = "Second"
55 | }
56 |
57 | #Preview {
58 | CustomSegmentedControl()
59 | }
60 |
--------------------------------------------------------------------------------
/SwiftUICodeSnippets/005-SymbolImageAnimation/SymbolImageAnimation.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SymbolImageAnimation.swift
3 | // SwiftUICodeSnippets
4 | //
5 | // Created by Mason Sun on 2024/12/21.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct SymbolImageAnimation: View {
11 | @State private var isAnimating = false
12 |
13 | var body: some View {
14 | HStack(spacing: 12) {
15 | Button {
16 | isAnimating.toggle()
17 | } label: {
18 | Image(systemName: "arrowshape.up")
19 | .imageScale(.large)
20 | .symbolEffect(.bounce, value: isAnimating)
21 | }
22 | Button {
23 | isAnimating.toggle()
24 | } label: {
25 | Image(systemName: "arrowshape.up")
26 | .imageScale(.large)
27 | .symbolEffect(.pulse, value: isAnimating)
28 | }
29 | Button {
30 | isAnimating.toggle()
31 | } label: {
32 | Image(systemName: "arrowshape.up")
33 | .imageScale(.large)
34 | .symbolEffect(.rotate, value: isAnimating)
35 | }
36 | Button {
37 | isAnimating.toggle()
38 | } label: {
39 | Image(systemName: "arrowshape.up")
40 | .imageScale(.large)
41 | .symbolEffect(.wiggle, value: isAnimating)
42 | }
43 | }
44 | .accentColor(.black)
45 | }
46 | }
47 |
48 | #Preview {
49 | SymbolImageAnimation()
50 | }
51 |
--------------------------------------------------------------------------------
/SwiftUICodeSnippets/006-PopoverTip/PopoverTipView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ContentView.swift
3 | // SwiftUICodeSnippets
4 | //
5 | // Created by Mason Sun on 2024/12/21.
6 | //
7 |
8 |
9 | import SwiftUI
10 | import TipKit
11 |
12 | struct PopoverTip: Tip {
13 | var title: Text {
14 | Text("查看详情")
15 | .foregroundStyle(.indigo)
16 | }
17 |
18 | var message: Text? {
19 | Text("点击 \(Image(systemName: "globe")) 查看更多信息")
20 | }
21 | }
22 |
23 | struct PopoverTipView: View {
24 | var body: some View {
25 | Text("Hello, world")
26 | .popoverTip(PopoverTip())
27 | .task {
28 | try? Tips.configure()
29 | }
30 | }
31 | }
32 |
33 | #Preview {
34 | PopoverTipView()
35 | }
36 |
--------------------------------------------------------------------------------
/SwiftUICodeSnippets/007-PolishAnimation/PolishView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PolishView.swift
3 | // SwiftUICodeSnippets
4 | //
5 | // Created by Mason Sun on 2024/12/9.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct PolishView: View {
11 | @State private var trigger = false
12 | var body: some View {
13 | Rectangle()
14 | .fill(Color.blue)
15 | .overlay {
16 | // 100 比较 hardcode 我们动态获取视图的高度
17 | GeometryReader { proxy in
18 | let size = proxy.size
19 | LinearGradient(
20 | colors: [
21 | Color.white.opacity(0),
22 | Color.white.opacity(0),
23 | Color.white.opacity(0),
24 | Color.white.opacity(0.8),
25 | Color.white.opacity(0),
26 | Color.white.opacity(0),
27 | Color.white.opacity(0),
28 | ],
29 | startPoint: .bottomLeading,
30 | endPoint: .topTrailing
31 | )
32 | // 渐变视图左右两侧会出现露出缺角
33 | // 我们放大 2 倍的 size, 避免出现这种情况
34 | .scaleEffect(2)
35 | .keyframeAnimator(
36 | initialValue: 0,
37 | trigger: trigger,
38 | content: { content, progress in
39 | // 我们需要将渐变色从 View 底部移动到 View 右上角
40 | content
41 | .offset(
42 | // 当 progress 从 0 到 1 的时候
43 | // offset 的 x 和 y 分别会趋近于 0
44 | // 偏移的距离还不够, 我们改为 2 倍
45 | x: -size.width + progress * size.width * 2,
46 | y: size.height - progress * size.height * 2
47 | )
48 | },
49 | keyframes: { _ in
50 | CubicKeyframe(0, duration: 0)
51 | CubicKeyframe(1, duration: 1)
52 | }
53 | )
54 | }
55 | }
56 | .onTapGesture {
57 | trigger.toggle()
58 | }
59 | .frame(width: 200, height: 200)
60 | .cornerRadius(24)
61 | }
62 | }
63 |
64 | #Preview {
65 | PolishView()
66 | }
67 |
--------------------------------------------------------------------------------
/SwiftUICodeSnippets/008-NavTransition/NavigationTransitionAnimationView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NavigationTransitionAnimationView.swift
3 | // SwiftUICodeSnippets
4 | //
5 | // Created by Mason Sun on 2024/12/9.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct NavigationTransitionAnimationView: View {
11 | @Namespace private var scrollView
12 |
13 | var body: some View {
14 | NavigationStack {
15 | ScrollView {
16 | VStack {
17 | ForEach(0...100, id: \.self) { index in
18 | NavigationLink {
19 | largeLabel(title: "\(index)")
20 | .navigationBarBackButtonHidden()
21 | .navigationTransition(.zoom(sourceID: index, in: scrollView))
22 | } label: {
23 | largeLabel(title: "\(index)")
24 | .frame(height: 100)
25 | .cornerRadius(24)
26 | }
27 | .matchedTransitionSource(
28 | id: index,
29 | in: scrollView
30 | )
31 | }
32 | }
33 | .padding()
34 | }
35 | }
36 | }
37 |
38 | func largeLabel(title: String) -> some View {
39 | Text(title)
40 | .font(.title)
41 | .foregroundStyle(.white)
42 | .frame(maxWidth: .infinity, maxHeight: .infinity)
43 | .background(Color.blue)
44 | }
45 | }
46 |
47 | #Preview {
48 | NavigationTransitionAnimationView()
49 | }
50 |
--------------------------------------------------------------------------------
/SwiftUICodeSnippets/009-ImageTransition/ImageTransitionAnimationView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ImageTransitionAnimationView.swift
3 | // SwiftUICodeSnippets
4 | //
5 | // Created by Mason Sun on 2024/12/9.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct ImageTransitionAnimationView: View {
11 | @State private var toggle: Bool = false
12 |
13 | var body: some View {
14 | ZStack {
15 | if toggle {
16 | Image("car-image-1")
17 | .resizable()
18 | .transition(.blurReplace)
19 | } else {
20 | Image("car-image-2")
21 | .resizable()
22 | .transition(.blurReplace)
23 | }
24 | }
25 | .aspectRatio(contentMode: .fit)
26 | .frame(width: 300)
27 | .cornerRadius(24)
28 | .onTapGesture {
29 | withAnimation(.easeInOut(duration: 2)) {
30 | toggle.toggle()
31 | }
32 | }
33 | }
34 | }
35 |
36 | #Preview {
37 | ImageTransitionAnimationView()
38 | }
39 |
--------------------------------------------------------------------------------
/SwiftUICodeSnippets/010-StickyHeader/StickyHeaderView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StickyHeaderView.swift
3 | // SwiftUICodeSnippets
4 | //
5 | // Created by Mason Sun on 2024/12/10.
6 | //
7 |
8 |
9 | import SwiftUI
10 |
11 | struct StickyHeaderView: View {
12 | // 记录 scrollView 的偏移量
13 | @State private var scrollOffset: CGFloat = 0
14 |
15 | var body: some View {
16 | ScrollView {
17 | VStack(spacing: 12) {
18 | Image("background")
19 | .scaledToFill()
20 | .frame(
21 | // 由于向下拖拽 scrollView 时, offset 会变为负数
22 | // e.g. -1, -2, -5, -10...
23 | // 这里当判定 offset < 0 时将图片的 height 变大
24 | // offset > 0, 图片高度不变
25 | height: 400 + (scrollOffset < 0 ? -scrollOffset : 0)
26 | )
27 | .clipped()
28 | mockList()
29 | }
30 | .offset(y: scrollOffset > 0 ? 0 : scrollOffset)
31 | }
32 | .ignoresSafeArea()
33 | .onScrollGeometryChange(
34 | for: CGFloat.self,
35 | of: {
36 | $0.contentOffset.y
37 | },
38 | action: { oldValue, newValue in
39 | // 监听 scrollView 的 offsetY
40 | scrollOffset = newValue
41 | }
42 | )
43 | }
44 |
45 | private func mockList() -> some View {
46 | ForEach(0...100, id: \.self) { index in
47 | Text("\(index)")
48 | .padding()
49 | }
50 | }
51 | }
52 |
53 | #Preview {
54 | StickyHeaderView()
55 | }
56 |
--------------------------------------------------------------------------------
/SwiftUICodeSnippets/011-RainbowButton/RainbowButton.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RainbowButton.swift
3 | // SwiftUICodeSnippets
4 | //
5 | // Created by Mason Sun on 2024/12/11.
6 | //
7 |
8 |
9 | import SwiftUI
10 |
11 | struct RainbowButton: View {
12 | var body: some View {
13 | let backgroundColor = Color("DarkBlue")
14 | Button {
15 | //
16 | } label: {
17 | Text("Hello, world")
18 | .padding(.vertical, 12)
19 | .padding(.horizontal, 24)
20 | .foregroundStyle(
21 | .linearGradient(
22 | colors: [
23 | .indigo,
24 | .purple,
25 | .pink,
26 | .orange,
27 | .yellow,
28 | .green
29 | ],
30 | startPoint: .leading,
31 | endPoint: .trailing
32 | )
33 | )
34 | .background(backgroundColor)
35 | .cornerRadius(8)
36 | .shadow(
37 | color: backgroundColor.opacity(0.5),
38 | radius: 2,
39 | x: 0,
40 | y: 1
41 | )
42 | .shadow(
43 | color: backgroundColor.opacity(0.5),
44 | radius: 16,
45 | x: 0,
46 | y: 4
47 | )
48 | }
49 | .buttonStyle(ScaledButtonStyle())
50 | }
51 | }
52 |
53 | // 把我们之前实现的 ScaledButtonStyle 粘贴过来
54 | extension RainbowButton {
55 | private struct ScaledButtonStyle: ButtonStyle {
56 | func makeBody(configuration: Configuration) -> some View {
57 | configuration
58 | .label
59 | .scaleEffect(configuration.isPressed ? 0.9 : 1)
60 | .animation(.interactiveSpring(), value: configuration.isPressed)
61 | }
62 | }
63 | }
64 |
65 | #Preview {
66 | RainbowButton()
67 | .offset(y: -100)
68 | }
69 |
--------------------------------------------------------------------------------
/SwiftUICodeSnippets/012-AnimatedBorder/AnimatedBorderView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AnimatedBorderView.swift
3 | // SwiftUICodeSnippets
4 | //
5 | // Created by Mason Sun on 2024/12/14.
6 | //
7 |
8 |
9 | import SwiftUI
10 |
11 | struct AnimatedBorderView: View {
12 | var body: some View {
13 | Image("background-2")
14 | .resizable()
15 | .scaledToFit()
16 | .frame(height: 200)
17 | .overlay {
18 | AnimatedBorder()
19 | }
20 | }
21 | }
22 |
23 | struct AnimatedBorder: View {
24 | @State private var rotation: CGFloat = 0
25 |
26 | var body: some View {
27 | GeometryReader { proxy in
28 | Rectangle()
29 | .fill(
30 | .linearGradient(
31 | colors: [
32 | .indigo.opacity(0.3),
33 | .indigo,
34 | .indigo.opacity(0.3)
35 | ],
36 | startPoint: .leading,
37 | endPoint: .trailing
38 | )
39 | )
40 | // 为了实现对角线出现部分 border 的效果, 需要将宽度减半, 高度增加一倍
41 | // 这样矩形旋转起来的时候才会看到 border 旋转的效果
42 | .frame(
43 | width: proxy.size.width / 2,
44 | height: proxy.size.height * 2
45 | )
46 | // 需要改变 position, 才能将 GeometryReader 中的 View 居中
47 | .position(
48 | x: proxy.frame(in: .local).width / 2,
49 | y: proxy.frame(in: .local).height / 2
50 | )
51 | // 将矩形转起来
52 | .rotationEffect(.degrees(rotation))
53 | // 动画时间为 4s, 永不停止
54 | .animation(
55 | .linear(duration: 4).repeatForever(
56 | autoreverses: false
57 | ),
58 | value: rotation
59 | )
60 | // 使用 mask 裁剪矩形的中间部分, 只保留边框为 4 的线
61 | .mask {
62 | Rectangle()
63 | .stroke(lineWidth: 4)
64 | }
65 | .onAppear {
66 | // 让矩形旋转 360
67 | rotation = 360
68 | }
69 | }
70 | }
71 | }
72 |
73 | #Preview {
74 | AnimatedBorderView()
75 | .offset(x: 0, y: -100)
76 | }
77 |
--------------------------------------------------------------------------------
/SwiftUICodeSnippets/013-FunnyToggle/FunnyToggleView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FunnyToggleView.swift
3 | // SwiftUICodeSnippets
4 | //
5 | // Created by Mason Sun on 2024/12/14.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct FunnyToggleView: View {
11 | // 用来管理 Toggler 是否打开的状态
12 | @State private var isOn: Bool = false
13 | private let indicatorSize: CGFloat = 30
14 | // 让 indicator 和背景有一些间距, 这里默认是 4
15 | private let insetPadding: CGFloat = 4
16 | private var toggleHeight: CGFloat {
17 | // 由于是上下都有边距, 这里需要 x2
18 | indicatorSize + insetPadding * 2
19 | }
20 |
21 | var body: some View {
22 | UnevenRoundedRectangle(
23 | // 增加一些有意思的圆角变化效果
24 | cornerRadii: .init(
25 | topLeading: isOn ? indicatorSize / 2 : 4,
26 | // 制造最大的圆角效果
27 | bottomLeading: indicatorSize / 2,
28 | bottomTrailing: isOn ? 4 : indicatorSize / 2,
29 | topTrailing: indicatorSize / 2
30 | )
31 | )
32 | // Indicator 设置开启为白色, 关闭时黄色
33 | .fill(isOn ? Color.white : Color.yellow)
34 | // 增加一个黑色边框效果
35 | .stroke(Color.darkBlue, lineWidth: 2)
36 | .frame(width: indicatorSize, height: indicatorSize)
37 | // 设置左右边距
38 | .padding(.horizontal, insetPadding)
39 | // 绘制整体的 toggle 背景
40 | .frame(
41 | width: indicatorSize * 2.5,
42 | height: toggleHeight,
43 | // 开启后让 indicator 回到右侧, 关闭在左侧
44 | alignment: isOn ? .trailing : .leading
45 | )
46 | .background {
47 | // 同样绘制一个部分圆角效果的矩形
48 | UnevenRoundedRectangle(
49 | // 同样背景也设置一样的效果
50 | cornerRadii: .init(
51 | topLeading: isOn ? toggleHeight / 2 : 4,
52 | // 制造最大的圆角效果
53 | bottomLeading: toggleHeight / 2,
54 | bottomTrailing: isOn ? 4 : toggleHeight / 2,
55 | topTrailing: toggleHeight / 2
56 | )
57 | )
58 | // 设置开启时背景为黄色, 关闭时白色
59 | .fill(isOn ? Color.yellow : Color.white)
60 | }
61 | .onTapGesture {
62 | // 点击切换 开/关 状态
63 | // 增加一些动画
64 | withAnimation(.easeInOut(duration: 0.5)) {
65 | isOn.toggle()
66 | }
67 | }
68 | .frame(maxWidth: .infinity, maxHeight: .infinity)
69 | .background(Color.darkBlue)
70 | }
71 | }
72 |
73 | #Preview {
74 | FunnyToggleView()
75 | .offset(x: 0, y: -100)
76 | .frame(maxWidth: .infinity, maxHeight: .infinity)
77 | .background(Color.darkBlue)
78 | }
79 |
--------------------------------------------------------------------------------
/SwiftUICodeSnippets/014-ToDoCell/TodoCellsView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TodoCellsView.swift
3 | // SwiftUICodeSnippets
4 | //
5 | // Created by Mason Sun on 2024/12/15.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct TodoCellsView: View {
11 | var body: some View {
12 | VStack(spacing: 0) {
13 | ToDoCell(emoji: "📚", title: "读书", isDone: true)
14 | Divider()
15 | ToDoCell(emoji: "⌨️", title: "写代码", isDone: false)
16 | Divider()
17 | ToDoCell(emoji: "🏊♀️", title: "游泳", isDone: false)
18 | }
19 | .padding()
20 | .frame(
21 | maxWidth: .infinity,
22 | maxHeight: .infinity,
23 | alignment: .top
24 | )
25 | }
26 | }
27 |
28 | struct ToDoCell: View {
29 | let emoji: String
30 | let title: String
31 | @State var isDone: Bool
32 |
33 | var body: some View {
34 | HStack(spacing: 8) {
35 | Text(emoji)
36 | .font(.title3)
37 | Text(title)
38 | .font(.title3)
39 | // 完成后文本变为删除效果, 同时颜色淡化
40 | .foregroundStyle(isDone ? .gray.opacity(0.7) : .primary)
41 | .strikethrough(isDone)
42 | Spacer()
43 | Image(systemName: isDone ? "checkmark.circle.fill" : "circle.fill")
44 | .imageScale(.large)
45 | // 给完成图标添加一些颜色和动画效果
46 | .foregroundStyle(isDone ? .yellow : .gray.opacity(0.3))
47 | .contentTransition(.symbolEffect(.replace))
48 | }
49 | .padding(.horizontal, 12)
50 | .padding(.vertical, 16)
51 | // 让点击区域变为整个 Cell, 不添加这行代码 Spacer() 填充的
52 | // 位置无法被点击
53 | .contentShape(Rectangle())
54 | .onTapGesture {
55 | // 点击 Cell 后触发完成操作
56 | withAnimation(.easeInOut) {
57 | isDone.toggle()
58 | }
59 | }
60 | }
61 | }
62 |
63 | #Preview {
64 | TodoCellsView()
65 | }
66 |
--------------------------------------------------------------------------------
/SwiftUICodeSnippets/015-ScrollTransition/ScrollViewTransition.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ScrollViewTransition.swift
3 | // SwiftUICodeSnippets
4 | //
5 | // Created by Mason Sun on 2024/12/16.
6 | //
7 |
8 |
9 | import SwiftUI
10 |
11 | struct ScrollViewTransition: View {
12 | var body: some View {
13 | ScrollView(.horizontal) {
14 | HStack(spacing: 16) {
15 | ForEach(1..<6) { index in
16 | // 增加一个动态的视觉效果
17 | // 使用 ZStack 包裹 Image 为了让 Image 动起来
18 | // 容器层不发生变化
19 | ZStack {
20 | Image("image-\(index)")
21 | .resizable()
22 | .scaledToFill()
23 | .scrollTransition { content, phase in
24 | // phase 为当前 image 的出现阶段
25 | // -1 位于最左侧, 0 位于居中, 1 位于最右侧
26 | content
27 | .offset(x: phase.value * 200)
28 | }
29 | }
30 | // 让图片的 Size 根据 ScrollView 展示的区域来决定
31 | .containerRelativeFrame(.horizontal)
32 | .clipShape(RoundedRectangle(cornerRadius: 36))
33 | }
34 | }
35 | }
36 | // 设置 ScrollView 为分页模式
37 | .scrollTargetBehavior(.paging)
38 | // 这里需要保证左右的 margin 是 HStack 中 spacing 16 的 2 倍
39 | // 才能达到中间的图片是居中的状态
40 | .contentMargins(32)
41 | // 我们将 ScrollView 改小一点, 这样效果不太好
42 | .frame(height: 600)
43 | }
44 | }
45 |
46 | #Preview {
47 | ScrollViewTransition()
48 | }
49 |
--------------------------------------------------------------------------------
/SwiftUICodeSnippets/016-MeshGradient/MeshGradientView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MeshGradientView.swift
3 | // SwiftUICodeSnippets
4 | //
5 | // Created by Mason Sun on 2024/12/17.
6 | //
7 |
8 |
9 | import SwiftUI
10 |
11 | struct MeshGradientView: View {
12 |
13 | // 我们来创建一个动画, MeshGradient 是支持动画效果的
14 | @State private var toggle: Bool = false
15 |
16 | // 由于创建的是 3*3 的网格, 因此我们需要 9 个点
17 | var points: [SIMD2] {
18 | [
19 | [0.0, 0.0], [0.5, 0.0], [1.0, 0.0],
20 | [0.0, 0.5], [toggle ? 1 : 0.0, 0.5], [1.0, 0.5],
21 | [0.0, 1.0], [0.5, 1.0], [1.0, 1.0],
22 | ]
23 | }
24 |
25 | // 同样从左上角顺时针转一圈, 创建 9 个颜色
26 | // (分别是彩虹的颜色, 中间用白色填充)
27 | var colors: [Color] {
28 | [
29 | // 这里我先用白色填充, 方便按彩虹的颜色顺序更改
30 | .red, .orange, .yellow,
31 | .purple, .white, .green,
32 | .indigo, .blue, .mint,
33 | ]
34 | }
35 |
36 | var body: some View {
37 | MeshGradient(
38 | width: 3,
39 | height: 3,
40 | points: points,
41 | colors: colors
42 | )
43 | // 忽略安全区
44 | .ignoresSafeArea()
45 | .onTapGesture {
46 | withAnimation {
47 | toggle.toggle()
48 | }
49 | }
50 | }
51 | }
52 |
53 | #Preview {
54 | MeshGradientView()
55 | }
56 |
--------------------------------------------------------------------------------
/SwiftUICodeSnippets/017-LoginPage/LoginPage.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LoginPage.swift
3 | // SwiftUICodeSnippets
4 | //
5 | // Created by Mason Sun on 2024/12/18.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct LoginPage: View {
11 | var body: some View {
12 | ZStack(alignment: .bottom) {
13 | // 紫色背景
14 | Color.indigo
15 | .ignoresSafeArea()
16 |
17 | // 我们只做一个卡片效果
18 | VStack(alignment: .leading, spacing: 16) {
19 | // 左上角的 Logo
20 | HStack {
21 | Image(systemName: "sparkle")
22 | .font(.title)
23 | .padding(10)
24 | .background(Circle().fill(Color.gray.opacity(0.08)))
25 | Spacer()
26 | }
27 | // 一些介绍文本
28 | Text("Get Started")
29 | .font(.title2)
30 | .fontWeight(.semibold)
31 | Text("Register for events, subscribe to calendars and manage events you're going to.")
32 | .font(.callout)
33 | .foregroundStyle(.secondary)
34 | // 登陆按钮
35 | // 按钮之间的间距有些大, 我们调整小一点
36 | VStack(spacing: 10) {
37 | // Phone
38 | Button {
39 | //
40 | } label: {
41 | Text("Continue with Phone")
42 | .foregroundStyle(.white)
43 | .fontWeight(.medium)
44 | .frame(maxWidth: .infinity)
45 | // 制造一个卡片效果
46 | .padding(.vertical, 16)
47 | .background(Color.black)
48 | .cornerRadius(12)
49 | }
50 | // Email
51 | Button {
52 | //
53 | } label: {
54 | Text("Continue With Email")
55 | .fontWeight(.medium)
56 | .frame(maxWidth: .infinity)
57 | // 制造一个卡片效果
58 | .padding(.vertical, 16)
59 | .background(Color.gray.opacity(0.3))
60 | .cornerRadius(12)
61 | }
62 | HStack(spacing: 10) {
63 | // Apple
64 | Button {
65 | //
66 | } label: {
67 | Image(systemName: "apple.logo")
68 | .imageScale(.large)
69 | .frame(maxWidth: .infinity)
70 | // 制造一个卡片效果
71 | .padding(.vertical, 12)
72 | .background(Color.gray.opacity(0.3))
73 | .cornerRadius(12)
74 | }
75 | // Google
76 | Button {
77 | //
78 | } label: {
79 | Image(systemName: "g.square")
80 | .imageScale(.large)
81 | .frame(maxWidth: .infinity)
82 | // 制造一个卡片效果
83 | .padding(.vertical, 12)
84 | .background(Color.gray.opacity(0.3))
85 | .cornerRadius(12)
86 | }
87 | }
88 | }
89 | }
90 | .padding(20)
91 | // 底部额外增加 24 间距
92 | .padding(.bottom, 24)
93 | .background(Color.white)
94 | .cornerRadius(32)
95 | .padding(12)
96 | // 将按钮的图标和文本改为黑色
97 | .tint(Color.black)
98 | }
99 | }
100 | }
101 |
102 | #Preview {
103 | LoginPage()
104 | }
105 |
--------------------------------------------------------------------------------
/SwiftUICodeSnippets/018-SubscribePage/SubscribePage.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SubscribePage.swift
3 | // SwiftUICodeSnippets
4 | //
5 | // Created by Mason Sun on 2024/12/20.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct SubscribePage: View {
11 | var body: some View {
12 | ZStack {
13 | gradientBackground
14 | .ignoresSafeArea()
15 | //
16 | // 开始绘制内容
17 | VStack(alignment: .leading, spacing: 24) {
18 | // 同时增加一个 Spacer() 让主内容居中
19 | Spacer()
20 | // Title
21 | Text("Try Balabala\nMusic Premium")
22 | .font(.largeTitle)
23 | .fontDesign(.serif)
24 | Text("* $14.99/month")
25 |
26 | VStack(spacing: 16) {
27 | HStack(spacing: 0) {
28 | Text("Ad-free music in the Balabala Music app")
29 | Spacer()
30 | Image(systemName: "checkmark.circle.fill")
31 | }
32 | HStack(spacing: 0) {
33 | Text("Background play")
34 | Spacer()
35 | Image(systemName: "checkmark.circle.fill")
36 | }
37 | HStack(spacing: 0) {
38 | Text("Downloads")
39 | Spacer()
40 | Image(systemName: "checkmark.circle.fill")
41 | }
42 | }
43 | // 我们需要让按钮在底部展示
44 | Spacer()
45 | // 增加订阅按钮
46 | Button {
47 | //
48 | } label: {
49 | Text("Subscribe Now")
50 | .font(.title2)
51 | .fontWeight(.medium)
52 | .foregroundStyle(.black)
53 | .padding(.vertical, 12)
54 | .frame(maxWidth: .infinity)
55 | .background(Color.white)
56 | .clipShape(Capsule())
57 | }
58 | VStack(spacing: 8) {
59 | Text("Restrictions apply. Learn more here.")
60 | Image(systemName: "chevron.down")
61 | }
62 | .frame(maxWidth: .infinity)
63 | }
64 | .padding(.horizontal, 32)
65 | .frame(
66 | maxWidth: .infinity,
67 | maxHeight: .infinity,
68 | alignment: .center
69 | )
70 | // 制造毛玻璃效果
71 | .background(.ultraThinMaterial)
72 | // 为了好看我们使用黑色主题
73 | .colorScheme(.dark)
74 | }
75 | }
76 |
77 | // 绘制渐变背景
78 | private var gradientBackground: some View {
79 | MeshGradient(
80 | width: 2,
81 | height: 3,
82 | points: [
83 | // 这里绘制一个 2x3 的网格
84 | // 左侧 3 个点, 右侧 3 个点
85 | [0.0, 0.0], [1.0, 0.0],
86 | [0.0, 0.5], [1.0, 0.5],
87 | [0.0, 1.0], [1.0, 1.0]
88 | // 补全 0 纯粹为了好看
89 | ],
90 | colors: [
91 | .blue, .green,
92 | .cyan, .black,
93 | .orange, .gray,
94 | ]
95 | )
96 | }
97 | }
98 |
99 | #Preview {
100 | SubscribePage()
101 | }
102 |
--------------------------------------------------------------------------------
/SwiftUICodeSnippets/019-NumericAnimation/NumericAnimationView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NumericAnimationView.swift
3 | // SwiftUICodeSnippets
4 | //
5 | // Created by Mason Sun on 2024/12/20.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct NumericAnimationView: View {
11 | // 维护展示在屏幕上的文本 & 颜色
12 | @State private var index = 0
13 | // 每一个标题对应一种颜色
14 | let colors: [Color] = [.red, .orange, .green, .blue]
15 | let titles = ["Hello", "SwiftUI", "你好", "SwiftUI"]
16 |
17 | var body: some View {
18 | let color = colors[index]
19 | return Text(titles[index])
20 | .font(.system(size: 80, weight: .thin))
21 | // 增加阴影效果
22 | .shadow(color: color, radius: 5)
23 | .shadow(color: color, radius: 5)
24 | .shadow(color: color, radius: 50)
25 | .shadow(color: color, radius: 100)
26 | .shadow(color: color, radius: 200)
27 | .contentTransition(.numericText())
28 | .animation(.bouncy, value: index)
29 | .onAppear {
30 | Timer.scheduledTimer(withTimeInterval: 1.5, repeats: true) { _ in
31 | // 每次 index + 1, 取 colors 的余数防止数组越界
32 | // index + 1 == colors.count 时结果为 0
33 | index = (index + 1) % colors.count
34 | }
35 | }
36 | }
37 | }
38 |
39 | #Preview {
40 | NumericAnimationView()
41 | }
42 |
--------------------------------------------------------------------------------
/SwiftUICodeSnippets/020-WaveShader/Wave.metal:
--------------------------------------------------------------------------------
1 | //
2 | // Wave.metal
3 | // SwiftUICodeSnippets
4 | //
5 | // Created by Mason Sun on 2024/12/24.
6 | //
7 |
8 | #include
9 | using namespace metal;
10 |
11 | // 这里粘贴过来的方法是 SwiftUI 桥接 Shader 方法的固定写法
12 | // 我们可以更改方法名称, 以及后面的额外参数
13 | // 这里我添加了一个参数 time 用来做动态效果
14 | [[ stitchable ]] float2 wave(float2 position, float time) {
15 | // position 为每隔像素点的位置
16 | // 我们将 y 得值动态增加 -1 ~ 1 (sin 之后的值)
17 | // 增加一些 y 的偏移幅度, 同时减少相邻 y 之间的差异 (position.y / 20, 之后会让 sin 0~1 的周期变长)
18 | position.y += 3 * sin(time + position.y / 20);
19 | return position;
20 | }
21 |
--------------------------------------------------------------------------------
/SwiftUICodeSnippets/020-WaveShader/WaveShaderView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WaveShaderView.swift
3 | // SwiftUICodeSnippets
4 | //
5 | // Created by Mason Sun on 2024/12/24.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct WaveShaderView: View {
11 | var body: some View {
12 | let start = Date.now
13 | TimelineView(.animation) { context in
14 | // .animation 会根据系统给出一个合理的回掉次数
15 | // 这里通过回掉时的 date 减去 start 来计算出一个时间戳
16 | // e.g. 0.3, 0.4, 0.6, 1 xxx 之类的 (具体取决于系统 1s 内回掉次数)
17 | let time = start.distance(to: context.date)
18 | Image("image-6")
19 | .resizable()
20 | .scaledToFit()
21 | .padding(24)
22 | .distortionEffect(
23 | // 如果 time 是 1, 图片会出现固定的波纹效果
24 | ShaderLibrary.wave(.float(time)),
25 | maxSampleOffset: .zero
26 | )
27 | }
28 | }
29 | }
30 |
31 | #Preview {
32 | WaveShaderView()
33 | }
34 |
--------------------------------------------------------------------------------
/SwiftUICodeSnippets/021-SnowShader/SnowShader.metal:
--------------------------------------------------------------------------------
1 | //
2 | // SnowShader.metal
3 | // SwiftUICodeSnippets
4 | //
5 | // Created by Mason Sun on 2024/12/24.
6 | //
7 |
8 | #include
9 | using namespace metal;
10 |
11 | // 我们去网上找一个下雪的 shader 效果, 不过这不是 SwiftUI 里可以直接使用的
12 | // 我们需要让别人帮我们转化下, 打开我们的好兄弟 cursor
13 |
14 | [[ stitchable ]] half4 snow(float2 position,
15 | // 好兄弟转换的有点问题, 这个 bounds 没有用, 但 SwiftUI 中需要有一个 color 的参数, 我们帮好兄弟改一下 😂
16 | // 看下效果
17 | half4 color,
18 | float time,
19 | float2 resolution) {
20 | float snow = 0.0;
21 | // float gradient = (1.0-float(position.y / resolution.x))*0.4;
22 | float random = fract(sin(dot(position.xy,float2(12.9898,78.233)))* 43758.5453);
23 |
24 | for(int k=0;k<6;k++){
25 | for(int i=0;i<8;i++){
26 | float cellSize = 1 + (float(i)*3.0);
27 | float downSpeed = -0.3+(sin(time*0.4+float(k+i*20))+1.0)*0.00008;
28 | float2 uv = (position.xy / resolution.x)+float2(0.01*sin((time+float(k*6185))*0.6+float(i))*(5.0/float(i)),downSpeed*(time+float(k*1352))*(1.0/float(i)));
29 | float2 uvStep = (ceil((uv)*cellSize-float2(0.5,0.5))/cellSize);
30 | float x = fract(sin(dot(uvStep.xy,float2(12.9898+float(k)*12.0,78.233+float(k)*315.156)))* 43758.5453+float(k)*12.0)-0.5;
31 | float y = fract(sin(dot(uvStep.xy,float2(62.2364+float(k)*23.0,94.674+float(k)*95.0)))* 62159.8432+float(k)*12.0)-0.5;
32 |
33 | float randomMagnitude1 = sin(time*2.5)*0.7/cellSize;
34 | float randomMagnitude2 = cos(time*2.5)*0.7/cellSize;
35 |
36 | float d = 5.0*distance((uvStep.xy + float2(x*sin(y),y)*randomMagnitude1 + float2(y,x)*randomMagnitude2),uv.xy);
37 |
38 | float omiVal = fract(sin(dot(uvStep.xy,float2(32.4691,94.615)))* 31572.1684);
39 | if(omiVal<0.08){
40 | float newd = (x+1.0)*0.4*clamp(1.9-d*(15.0+(x*6.3))*(cellSize/1.4),0.0,1.0);
41 | snow += newd;
42 | }
43 | }
44 | }
45 | // 这里把 gradient 直接干掉
46 | return half4(snow) + random*0.01;
47 | }
48 |
--------------------------------------------------------------------------------
/SwiftUICodeSnippets/021-SnowShader/SnowShaderView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SnowShaderView.swift
3 | // SwiftUICodeSnippets
4 | //
5 | // Created by Mason Sun on 2024/12/24.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct SnowShaderView: View {
11 | let start = Date.now
12 |
13 | var body: some View {
14 | ZStack {
15 | // 这里才是我们的颜色
16 | Color.black
17 | .ignoresSafeArea()
18 | // 这个颜色板块会被 shader 重新渲染后的雪的颜色重写调, 我们其实不太关心
19 | // 它具体是什么颜色, 给一个站位的 View 即可
20 | TimelineView(.animation) { context in
21 | let time = start.distance(to: context.date)
22 | Rectangle()
23 | .ignoresSafeArea()
24 | // 我们去创建一个 Shader 文件
25 | .visualEffect { view, proxy in
26 | view.colorEffect(
27 | ShaderLibrary.snow(
28 | // 参数弄反了, 第一个是 time
29 | // 发现有底色我们去调整下
30 | // 现在我们让雪花飘起来
31 | .float(time),
32 | // 方向好像有点反了, 我们将 speed 改成了负数
33 | .float2(proxy.size.width, proxy.size.height)
34 | )
35 | )
36 | }
37 | }
38 | Text("🎄 Merry Christmas 🎄")
39 | .font(.title)
40 | .foregroundStyle(.white)
41 | }
42 | }
43 | }
44 |
45 | #Preview {
46 | SnowShaderView()
47 | }
48 |
--------------------------------------------------------------------------------
/SwiftUICodeSnippets/022-DaysPostponementView/DaysPostponementView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DaysPostponementView.swift
3 | // SwiftUICodeSnippets
4 | //
5 | // Created by Mason Sun on 2024/12/25.
6 | //
7 |
8 | import SwiftUI
9 |
10 | // 创建一个 enum 用来维护所有的 days 选项
11 | enum DaysOption: String, CaseIterable {
12 | case two = "2"
13 | case seven = "7"
14 | case fourteen = "14"
15 | case thirty = "30"
16 | case sixty = "60"
17 | }
18 |
19 | struct DaysPostponementView: View {
20 | // 维护当前选中的 days
21 | @State private var selectedDays: DaysOption = .fourteen
22 |
23 | var body: some View {
24 | ZStack {
25 | Color.black
26 | .ignoresSafeArea()
27 | VStack(spacing: 44) {
28 | // icon
29 | Image(systemName: "macbook.and.iphone")
30 | .font(.title)
31 | .foregroundStyle(.gray)
32 | .padding(32)
33 | .background(Color.gray.opacity(0.3))
34 | .clipShape(Circle())
35 | VStack(spacing: 24) {
36 | // title
37 | Text("Rethink MackBook Pro in:")
38 | .font(.title2)
39 | .fontWeight(.bold)
40 | .foregroundStyle(.white)
41 | // 添加 days 选择器
42 | // 我们让文本对其
43 | HStack(alignment: .firstTextBaseline) {
44 | ForEach(DaysOption.allCases, id: \.self) { option in
45 | let isSelected = selectedDays == option
46 | VStack(spacing: 12) {
47 | Text(option.rawValue)
48 | // 我们给选中的 days 增加一些样式
49 | .foregroundStyle(isSelected ? .pink : .gray)
50 | .font(isSelected ? .title : .title3)
51 | .fontWeight(isSelected ? .bold : .regular)
52 | if isSelected {
53 | Text("DAYS")
54 | .font(.caption)
55 | .foregroundStyle(.pink.opacity(0.7))
56 | }
57 | }
58 | // 制造圆角灰色背景效果
59 | .padding(.vertical, 10)
60 | .padding(.horizontal, 12)
61 | .background {
62 | if isSelected {
63 | Color.gray.opacity(0.3)
64 | .cornerRadius(12)
65 | }
66 | }
67 | // 目前每隔选项都各自有大小, 我们希望他们的 width 是一致的
68 | // 这样中间的选项视图才能居中
69 | .frame(maxWidth: .infinity)
70 | // 增加点击事件
71 | .onTapGesture {
72 | // 增加一个系统的动画
73 | withAnimation {
74 | selectedDays = option
75 | }
76 | }
77 | }
78 | }
79 | // 给一些 padding 避免太宽
80 | .padding(.horizontal, 32)
81 | // description
82 | Text("One of the basic ticks to avoid\nimpulsive purchase is to delay\nthem in time.")
83 | .font(.title3)
84 | .fontDesign(.rounded)
85 | .multilineTextAlignment(.center)
86 | .foregroundStyle(.white.opacity(0.9))
87 | }
88 | }
89 | .offset(y: -60)
90 | }
91 | // 添加底部按钮
92 | .safeAreaInset(edge: .bottom) {
93 | Button {
94 | //
95 | } label: {
96 | // 这里需要根据选中的效果来展示 days
97 | Text("postpone for \(selectedDays.rawValue) days")
98 | .font(.callout)
99 | .foregroundStyle(.white)
100 | // 制造胶囊背景效果
101 | .padding(.horizontal, 24)
102 | .padding(.vertical, 12)
103 | .background(Color.pink.opacity(0.6))
104 | .clipShape(Capsule())
105 | }
106 | .padding()
107 | .padding(.bottom, 60)
108 | }
109 | }
110 | }
111 |
112 | #Preview {
113 | DaysPostponementView()
114 | }
115 |
--------------------------------------------------------------------------------
/SwiftUICodeSnippets/023-CalendarCard/CalendarCardView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CalendarCardView.swift
3 | // SwiftUICodeSnippets
4 | //
5 | // Created by Mason Sun on 2024/12/26.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct CalendarCardView: View {
11 | // 通过当前 date 获取前后 2 天
12 | var dates: [Date] {
13 | [-2, -1, 0, 1, 2].compactMap { day in
14 | Calendar.current.date(
15 | byAdding: .day,
16 | value: day,
17 | to: .now
18 | )
19 | }
20 | }
21 |
22 | var body: some View {
23 | HStack(spacing: 8) {
24 | ForEach(dates, id: \.self) { date in
25 | // 如果是当天的话给一个卡片背景效果
26 | let isToday = date.formatted(.dateTime.day()) == Date.now.formatted(.dateTime.day())
27 | VStack(spacing: 20) {
28 | // 调整下日期的样式
29 | Text(date.formatted(.dateTime.day()))
30 | .font(.title)
31 | .fontWeight(isToday ? .bold : .regular)
32 | Text(date.formatted(.dateTime.weekday()))
33 | .font(.title3)
34 | .fontWeight(isToday ? .medium : .thin)
35 | }
36 | .padding(.horizontal, 8)
37 | .padding(.vertical, 16)
38 | .background {
39 | if isToday {
40 | RoundedRectangle(cornerRadius: 8)
41 | .fill(Color.cyan.opacity(0.7))
42 | .stroke(Color.black, lineWidth: 2)
43 | }
44 | }
45 | }
46 | }
47 | // 制造卡片效果
48 | .padding(24)
49 | .background {
50 | RoundedRectangle(cornerRadius: 12)
51 | .fill(Color.white)
52 | .stroke(Color.black, lineWidth: 2)
53 | }
54 | // 制造偏移效果
55 | .background {
56 | RoundedRectangle(cornerRadius: 12)
57 | .fill(Color.yellow.opacity(0.86))
58 | .stroke(Color.black, lineWidth: 2)
59 | .offset(x: 8, y: 8)
60 | }
61 | .frame(maxWidth: .infinity, maxHeight: .infinity)
62 | .background(Color.indigo)
63 | }
64 | }
65 |
66 | #Preview {
67 | CalendarCardView()
68 | }
69 |
--------------------------------------------------------------------------------
/SwiftUICodeSnippets/024-PurchaseCard/PurchaseCardView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PurchaseCardView.swift
3 | // SwiftUICodeSnippets
4 | //
5 | // Created by Mason Sun on 2024/12/27.
6 | //
7 |
8 | import SwiftUI
9 |
10 | // 做一个 Product 的 Model
11 | struct Product {
12 | var title: String
13 | var description: String
14 | var price: String
15 |
16 | // 同时制造几个 Mock 数据
17 | static var swiftProduct: Product {
18 | .init(
19 | title: "Swift 教程",
20 | description: "一次性买断, 永久可访问",
21 | price: "$99"
22 | )
23 | }
24 |
25 | static var swiftUIProduct: Product {
26 | .init(
27 | title: "SwiftUI 教程",
28 | description: "一次性买断, 永久可访问",
29 | price: "$99"
30 | )
31 | }
32 |
33 | static var iOSProduct: Product {
34 | .init(
35 | title: "iOS 教程",
36 | description: "一次性买断, 永久可访问",
37 | price: "$99"
38 | )
39 | }
40 | }
41 |
42 | struct PurchaseCardView: View {
43 | // 制造选中效果
44 | @State private var selectedProduct: Product?
45 | // 当前 Hover 的 Product
46 | @State private var hoveredProduct: Product?
47 |
48 | var allProducts: [Product] = [
49 | .swiftProduct, .swiftUIProduct, .iOSProduct
50 | ]
51 |
52 | var body: some View {
53 | ZStack {
54 | Color(uiColor: .secondarySystemBackground)
55 | .ignoresSafeArea()
56 | // spacing 改大点
57 | HStack(spacing: 24) {
58 | // 先只展示一个卡片 iPhone 的 preview 有些小
59 | // 由于 preview 不能展示 hover 效果, 我们改为 mac app 展示
60 | ForEach(allProducts, id: \.title) { product in
61 | cardView(product: product)
62 | }
63 | }
64 | }
65 | // 我们制造一个黑色主题的效果
66 | .colorScheme(.dark)
67 | }
68 |
69 | // 卡片视图
70 | @ViewBuilder
71 | func cardView(product: Product) -> some View {
72 | // 制造选中的效果
73 | let isSelected = product.title == selectedProduct?.title
74 | let isHovered = product.title == hoveredProduct?.title
75 | VStack(alignment: .leading, spacing: 12) {
76 | // 调整下 UI
77 | Text(product.title)
78 | .frame(height: 30)
79 | Text(product.price)
80 | .font(.title)
81 | .fontWeight(.bold)
82 | Text(product.description)
83 | .font(.footnote)
84 | .foregroundStyle(.secondary)
85 | // 支付按钮, 我们只需要在选中/Hover 的时候展示`购买按钮`
86 | Button {
87 | //
88 | } label: {
89 | Text("购买")
90 | .foregroundStyle(.black)
91 | .font(.callout)
92 | .frame(maxWidth: .infinity)
93 | .padding(.vertical, 6)
94 | .background(Color.primary)
95 | .cornerRadius(8)
96 | }
97 | .buttonStyle(.plain)
98 | .opacity(isHovered || isSelected ? 1 : 0)
99 | }
100 | .frame(width: 100)
101 | // 制造卡片效果
102 | .padding()
103 | .background {
104 | RoundedRectangle(cornerRadius: 8)
105 | .fill(isSelected ? Color.indigo : Color.black.opacity(0.3))
106 | .stroke(
107 | // Hover 时候改为紫色
108 | isHovered ? Color.indigo : Color.white,
109 | lineWidth: 2
110 | )
111 | }
112 | .onTapGesture {
113 | selectedProduct = product
114 | }
115 | // 监听 hover 状态
116 | .onHover { isHovered in
117 | hoveredProduct = isHovered ? product : nil
118 | }
119 | }
120 | }
121 |
122 | #Preview {
123 | PurchaseCardView()
124 | }
125 |
--------------------------------------------------------------------------------
/SwiftUICodeSnippets/025-BackgroundAnimation/BackgroundColorAnimationView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BackgroundColorAnimationView.swift
3 | // SwiftUICodeSnippets
4 | //
5 | // Created by Mason Sun on 2024/12/30.
6 | //
7 |
8 | import SwiftUI
9 |
10 | // 我们创建一个 struct 控制 wave 的 height 和 range
11 | struct AnimationValues {
12 | // 一开始为 -100, 也就是凹进去的效果
13 | var waveRange: CGFloat = -100
14 | // 一开始是 0 的 height, 动画开始后变为 0.3 的屏幕高度
15 | var waveHeight: CGFloat = 0
16 | }
17 |
18 | enum AnimationPhase {
19 | case phase1
20 | case phase2
21 | case phase3
22 | }
23 |
24 | struct BackgroundColorAnimationView: View {
25 | @State private var triggerFirstAnimation = false
26 | // 控制是否开始第二阶段动画
27 | @State private var triggerSecondAnimation = false
28 | // 用 enum 维护 animation 进行的阶段
29 | @State private var animationPhase: AnimationPhase = .phase1
30 |
31 | var body: some View {
32 | ZStack {
33 | Color.white
34 | // 老的 View (第一阶段的)
35 | // 绘制一个从顶部到底部红色的渐变背景
36 | LinearGradient(
37 | colors: [
38 | Color.red.opacity(0.5),
39 | Color.red.opacity(0.8)
40 | ],
41 | startPoint: .top,
42 | endPoint: .bottom
43 | )
44 | // 忽略安全区
45 | .ignoresSafeArea()
46 | // 利用 keyframeAnimator 实现动画效果
47 | .keyframeAnimator(
48 | initialValue: AnimationValues(),
49 | trigger: triggerFirstAnimation,
50 | content: { content, value in
51 | // 我们使用绘制好的 waveShape 裁剪红色渐变背景
52 | content.mask {
53 | WaveShape(
54 | waveRange: value.waveRange,
55 | waveHeight: value.waveHeight
56 | )
57 | // 忽略 mask 的安全区
58 | .ignoresSafeArea()
59 | }
60 | },
61 | keyframes: { _ in
62 | // 设置动画
63 | // 根据 AnimationValues 中的 key 来做动画
64 | // 1. waveRange (也就是顶部的曲线变化)
65 | KeyframeTrack(\.waveRange) {
66 | // 改为 100 -> -50 -> 25 -> 0 制造波动的效果
67 | SpringKeyframe(100, duration: 0.3)
68 | SpringKeyframe(-50, duration: 0.3)
69 | SpringKeyframe(25, duration: 0.3)
70 | SpringKeyframe(0, duration: 0.3)
71 | }
72 | // 2. waveHeight
73 | // 我们希望高度从 0 改为 0.3 的位置
74 | KeyframeTrack(\.waveHeight) {
75 | CubicKeyframe(0.3, duration: 0.3)
76 | }
77 | }
78 | )
79 | // 只有第一阶段才展示 View 1
80 | .opacity(animationPhase == .phase1 ? 1 : 0)
81 | // 老视图也 disabled 掉
82 | .animation(nil, value: animationPhase)
83 | // 第一阶段的动画完成了, 由于 keyframeAnimator 不能连续给 2 个, 我这边
84 | // 使用一个新的 View 盖住老的 View, 让第一阶段动画接受后, 新的 View 出现
85 | // 老的 View 消失
86 | // 新的 View
87 | LinearGradient(
88 | colors: [
89 | Color.red.opacity(0.5),
90 | Color.red.opacity(0.8)
91 | ],
92 | startPoint: .top,
93 | endPoint: .bottom
94 | )
95 | // 忽略安全区
96 | .ignoresSafeArea()
97 | .opacity(animationPhase == .phase2 ? 1 : 0)
98 | // 我们发现还有些小问题, animationPhase 改为 phase 2 的时候, 它会有动画
99 | .animation(nil, value: animationPhase)
100 | // 第二阶段的 View 充满全屏了 我们也要让他被裁减, 把同样的 keyframeAnimator 粘过来
101 | .keyframeAnimator(
102 | // 这里默认值就不能为 0 了, 我们希望它在第一阶段的基础上开始动画
103 | // 也就是第一阶段结束后的 Shape 形状
104 | initialValue: AnimationValues(
105 | waveRange: 0,
106 | waveHeight: 0.3
107 | ),
108 | trigger: triggerSecondAnimation,
109 | content: { content, value in
110 | // 我们使用绘制好的 waveShape 裁剪红色渐变背景
111 | content.mask {
112 | WaveShape(
113 | waveRange: value.waveRange,
114 | waveHeight: value.waveHeight
115 | )
116 | // 忽略 mask 的安全区
117 | .ignoresSafeArea()
118 | }
119 | },
120 | keyframes: { _ in
121 | // 做出充满全屏的动画效果
122 | KeyframeTrack(\.waveRange) {
123 | // wave 幅度直接变为凸起的效果
124 | SpringKeyframe(100, duration: 0.3)
125 | SpringKeyframe(200, duration: 0.3)
126 | }
127 |
128 | KeyframeTrack(\.waveHeight) {
129 | CubicKeyframe(1, duration: 0.3)
130 | // 防止盖不住全屏给一个 2 倍的 height
131 | CubicKeyframe(2, duration: 0.3)
132 | }
133 | }
134 | )
135 |
136 | // 文本信息也是在阶段1 之后才开始
137 | if animationPhase == .phase2 {
138 | VStack {
139 | Text("YOU REALLY WENT THE DISTANCE")
140 | .font(.title3)
141 | Text("101 KM")
142 | .font(.largeTitle)
143 | .fontWeight(.bold)
144 | }
145 | .transition(.opacity.combined(with: .slide))
146 | // 同时设置文本消失的动画
147 | .offset(y: triggerSecondAnimation ? -200 : 0)
148 | .opacity(triggerSecondAnimation ? 0 : 1)
149 | .animation(.smooth(duration: 0.6), value: triggerSecondAnimation)
150 | }
151 | }
152 | .onAppear {
153 | triggerFirstAnimation.toggle()
154 | Task {
155 | // 第一阶段需要 1.2s 的时间完成动画
156 | try await Task.sleep(for: .seconds(1.2))
157 | // 设置动画后, 展示我们的文本信息
158 | withAnimation {
159 | animationPhase = .phase2
160 | }
161 | }
162 | }
163 | // 增加点击事件, 执行动画
164 | .onTapGesture {
165 |
166 | triggerSecondAnimation = true
167 | }
168 | }
169 | }
170 |
171 | #Preview {
172 | BackgroundColorAnimationView()
173 | }
174 |
--------------------------------------------------------------------------------
/SwiftUICodeSnippets/025-BackgroundAnimation/WaveShape.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WaveShape.swift
3 | // SwiftUICodeSnippets
4 | //
5 | // Created by Mason Sun on 2024/12/30.
6 | //
7 |
8 | import SwiftUI
9 |
10 | /// 这个是让 Cursor 帮我绘制的一个 Shape
11 | /// 顶部是一条曲线, 然后剩下的 3 条边和 Rectangle 没有缺别
12 | struct WaveShape: Shape {
13 | // 控制曲线的幅度
14 | var waveRange: CGFloat
15 | // 0~1, 1 的话 Shape 填充整个屏幕, 0 则只有最底部会有一点点曲线
16 | var waveHeight: CGFloat
17 |
18 | var animatableData: AnimatablePair {
19 | get {
20 | AnimatablePair(waveRange, waveHeight)
21 | }
22 | set {
23 | waveRange = newValue.first
24 | waveHeight = newValue.second
25 | }
26 | }
27 |
28 | func path(in rect: CGRect) -> Path {
29 | var path = Path()
30 |
31 | // 计算实际高度(waveHeight 是 0~1 的比例)
32 | let actualHeight = rect.maxY * (1 - waveHeight)
33 |
34 | // 从左下角开始
35 | path.move(to: CGPoint(x: rect.minX, y: rect.maxY))
36 |
37 | // 画左边和上边的凹陷
38 | path.addLine(to: CGPoint(x: rect.minX, y: actualHeight))
39 |
40 | // 画凹进去的顶边(使用二次贝塞尔曲线)
41 | path.addQuadCurve(
42 | to: CGPoint(x: rect.maxX, y: actualHeight),
43 | control: CGPoint(x: rect.width / 2, y: actualHeight - waveRange)
44 | )
45 |
46 | // 画右边和底边
47 | path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY))
48 | path.addLine(to: CGPoint(x: rect.minX, y: rect.maxY))
49 |
50 | path.closeSubpath()
51 |
52 | return path
53 | }
54 | }
55 |
56 | #Preview {
57 | // 改个数字测试下看看效果
58 | WaveShape(waveRange: -50, waveHeight: 1)
59 | .fill(Color.indigo)
60 | .frame(maxHeight: .infinity)
61 | .ignoresSafeArea()
62 | }
63 |
--------------------------------------------------------------------------------
/SwiftUICodeSnippets/026-HorizontalPicker/HorizontalPickerView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HorizontalPickerView.swift
3 | // SwiftUICodeSnippets
4 | //
5 | // Created by Mason Sun on 2025/1/2.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct HorizontalPickerView: View {
11 | // 用来存储所有 Text 的 frame
12 | @State private var viewFrames: ViewFrames = .init()
13 | var contentViewSpacer = NamedCoordinateSpace.named("contentViewSpacer")
14 | // 3. 每次页面出现的时候自动滚到默认 age 的位置
15 | // 使用 iOS 18 的 API scroll position 来让视图自动滚到指定的 view id
16 | @State var scrollPosition = ScrollPosition(id: 20)
17 | @State var isScrolling = false
18 |
19 | var body: some View {
20 | // 我们先将实现拆分成 3 部, 依次完成它们
21 | // 1. 实现 ScrollView 中的内容滚动后, 接近最中间的元素自动到中间位置
22 | // 2. 将远离中间位置的元素缩小, 同时颜色变灰
23 | // 3. 每次页面出现的时候自动滚到默认 age 的位置
24 | ZStack {
25 | Color.darkBlue
26 | .ignoresSafeArea()
27 | // ScrollView
28 | ScrollView(.horizontal, showsIndicators: false) {
29 | HStack(spacing: 0) {
30 | // 随便给个区间 16 * 99 岁
31 | ForEach(16..<99) { index in
32 | Text("\(index)")
33 | .font(.system(size: 50, weight: .medium))
34 | // 改文本为白色
35 | .foregroundStyle(.white)
36 | .frame(width: 80, height: 80)
37 | .background {
38 | // 通过 preference 将所有 View 的 frame 拿到
39 | GeometryReader { proxy in
40 | Color.clear
41 | .preference(
42 | key: ViewFrames.self,
43 | value: .init(frames: [index: proxy.frame(in: contentViewSpacer)])
44 | )
45 | }
46 | }
47 | // 我们设置好 < 18 不允许选中, 并隐藏
48 | .opacity(index < 18 ? 0 : 1)
49 | // 2. 将远离中间位置的元素缩小, 同时颜色变灰
50 | // 我们利用 iOS 17 的 API visualEffect 来实现, 缩放效果
51 | // 这里我提前实现好了, 大家可以看一下
52 | .visualEffect { view, proxy in
53 | let frame = proxy.frame(in: .scrollView(axis: .horizontal))
54 | // 获取 scrollView width
55 | let parentBounds = proxy.bounds(of: .scrollView(axis: .horizontal)) ?? .zero
56 | let scrollViewWidth = parentBounds.width
57 | let distanceToOriginalX = min(
58 | max(0, frame.midX),
59 | scrollViewWidth
60 | )
61 | let centerX = scrollViewWidth / 2
62 | let distanceToMidX = abs(distanceToOriginalX - centerX)
63 | let normalizedDistance = distanceToMidX / centerX
64 | // 获取 view 缩小的比例
65 | let scaledValue = max(
66 | 0.3,
67 | 1 - normalizedDistance
68 | )
69 | // 原 App 中的效果会更丝滑一些, 他通过 offset 的偏移量动态计算了 view 消失的时机
70 | // 我们这里就不花时间去 copy 了
71 | // 毕竟想要做好一个组件, 需要投入大量的时间
72 | // 我这里给大家提供思路, 可以发散一下
73 | return view
74 | .scaleEffect(
75 | CGSize(width: scaledValue, height: scaledValue)
76 | )
77 | .brightness(scaledValue - 0.8)
78 | }
79 | .id(index)
80 | }
81 | }
82 | // 设置好 NamedCoordinateSpace 才能让 37 行计算的 frame 是准确的
83 | // 效果是 ok 的了
84 | .coordinateSpace(contentViewSpacer)
85 | .onAppear {
86 | // 默认滚动到 20 的位置
87 | scrollPosition.scrollTo(id: 20, anchor: .center)
88 | }
89 | }
90 | // 设置外部圆角选中边框
91 | .overlay {
92 | // 我们还需要增加一个效果, 当用户停止滚动时, 边框出现
93 | // 滚动时边框消失
94 | RoundedRectangle(cornerRadius: 24)
95 | .stroke(Color.white, lineWidth: 0.25)
96 | .frame(width: 80, height: 80)
97 | .opacity(isScrolling ? 0 : 1 )
98 | .animation(.interactiveSpring, value: isScrolling)
99 | }
100 | }
101 | .onScrollPhaseChange { _, newPhase in
102 | isScrolling = newPhase.isScrolling
103 | }
104 | // 同时需要设置下 position
105 | .scrollPosition($scrollPosition, anchor: .center)
106 | // 这里获取所有子 View 的 frame 并存储起来
107 | .onPreferenceChange(ViewFrames.self) {
108 | viewFrames = $0
109 | }
110 | // 通过设置我们提前写好的 CenteringScrollTargetBehavior 来让 ScrollView 实现吸附中间元素的效果
111 | .scrollTargetBehavior(
112 | CenteringScrollTargetBehavior(viewFrames: viewFrames.frames) {
113 | print("Selected index: \($0)")
114 | }
115 | )
116 | }
117 | }
118 |
119 | // 我们先来实现第一步, ScrollView 自动吸附最中间的元素
120 | // 这里我把代码粘过来, 原理是使用 iOS 17 的 ScrollTargetBehavior
121 |
122 | struct ViewFrames: Equatable {
123 | var frames: [Int: CGRect] = [:]
124 | }
125 |
126 | extension ViewFrames: PreferenceKey {
127 | static var defaultValue: Self { .init() }
128 |
129 | static func reduce(
130 | value: inout ViewFrames,
131 | nextValue: () -> ViewFrames
132 | ) {
133 | value.frames.merge(nextValue().frames) { $1 }
134 | }
135 | }
136 |
137 | struct CenteringScrollTargetBehavior: ScrollTargetBehavior {
138 | let viewFrames: [Int: CGRect]
139 | var centerViewIndex: (Int) -> Void
140 |
141 | func updateTarget(
142 | _ target: inout ScrollTarget,
143 | context: TargetContext
144 | ) {
145 | let centerXProposed = target.rect.midX
146 | guard let nearestViewFrame = viewFrames.min(by: {
147 | ($0.value.midX - centerXProposed).magnitude < ($1.value.midX - centerXProposed).magnitude }
148 | ) else {
149 | return
150 | }
151 | centerViewIndex(nearestViewFrame.key)
152 | target.rect.origin.x = nearestViewFrame.value.midX - 0.5 * target.rect.size.width
153 | }
154 | }
155 |
156 | #Preview {
157 | HorizontalPickerView()
158 | }
159 |
--------------------------------------------------------------------------------
/SwiftUICodeSnippets/027-StarsBackgroundView/StartsBackgroundView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StartsBackgroundView.swift
3 | // SwiftUICodeSnippets
4 | //
5 | // Created by Mason Sun on 2025/2/9.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct StartsBackgroundView: View {
11 | // 创建随机星星位置
12 | private let stars: [CGPoint] = (0..<50).map { _ in
13 | CGPoint(
14 | x: CGFloat.random(in: 0...1),
15 | y: CGFloat.random(in: 0...1)
16 | )
17 | }
18 |
19 | var body: some View {
20 | GeometryReader { geometry in
21 | ZStack {
22 | // 深色渐变背景
23 | LinearGradient(
24 | colors: [
25 | Color(red: 0.1, green: 0.1, blue: 0.2),
26 | Color(red: 0.2, green: 0.2, blue: 0.3)
27 | ],
28 | startPoint: .topLeading,
29 | endPoint: .bottomTrailing
30 | )
31 | // 星星层
32 | ForEach(0.. CGFloat {
48 | CGFloat.random(in: 1...3)
49 | }
50 | }
51 |
52 | // 星星闪烁动画修饰器
53 | private struct StarPulseModifier: ViewModifier {
54 | @State private var isAnimating = false
55 |
56 | func body(content: Content) -> some View {
57 | content
58 | .opacity(isAnimating ? 0.5 : 1.0)
59 | .onAppear {
60 | withAnimation(
61 | .easeInOut(duration: Double.random(in: 1.5...3.0))
62 | .repeatForever(autoreverses: true)
63 | ) {
64 | isAnimating.toggle()
65 | }
66 | }
67 | }
68 | }
69 |
70 | struct GoProSectionView: View {
71 | var body: some View {
72 | VStack(alignment: .leading, spacing: 24) {
73 | Text("Upgrade to Pro and unlock all features")
74 | .foregroundStyle(.white)
75 | .font(.title3)
76 | .fontWeight(.medium)
77 | Text("Learn More")
78 | .font(.headline)
79 | .foregroundStyle(Color.white)
80 | .padding(.horizontal, 16)
81 | .padding(.vertical, 8)
82 | .background(Color.black)
83 | .clipShape(Capsule())
84 | }
85 | .frame(maxWidth: .infinity, alignment: .leading)
86 | .padding(20)
87 | .background {
88 | StartsBackgroundView()
89 | }
90 | }
91 | }
92 |
93 | #Preview {
94 | GoProSectionView()
95 | }
96 |
--------------------------------------------------------------------------------
/SwiftUICodeSnippets/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/SwiftUICodeSnippets/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "platform" : "ios",
6 | "size" : "1024x1024"
7 | },
8 | {
9 | "appearances" : [
10 | {
11 | "appearance" : "luminosity",
12 | "value" : "dark"
13 | }
14 | ],
15 | "idiom" : "universal",
16 | "platform" : "ios",
17 | "size" : "1024x1024"
18 | },
19 | {
20 | "appearances" : [
21 | {
22 | "appearance" : "luminosity",
23 | "value" : "tinted"
24 | }
25 | ],
26 | "idiom" : "universal",
27 | "platform" : "ios",
28 | "size" : "1024x1024"
29 | }
30 | ],
31 | "info" : {
32 | "author" : "xcode",
33 | "version" : 1
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/SwiftUICodeSnippets/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/SwiftUICodeSnippets/Assets.xcassets/DarkBlue.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0x44",
9 | "green" : "0x30",
10 | "red" : "0x2B"
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" : "0x44",
27 | "green" : "0x30",
28 | "red" : "0x2B"
29 | }
30 | },
31 | "idiom" : "universal"
32 | }
33 | ],
34 | "info" : {
35 | "author" : "xcode",
36 | "version" : 1
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/SwiftUICodeSnippets/Assets.xcassets/images/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/SwiftUICodeSnippets/Assets.xcassets/images/background-1.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "image (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 |
--------------------------------------------------------------------------------
/SwiftUICodeSnippets/Assets.xcassets/images/background-1.imageset/image (2).jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mas0nSun/SwiftUICodeSnippets/2dcf104ed16fb79a01a9a802d6c63236d74de5b2/SwiftUICodeSnippets/Assets.xcassets/images/background-1.imageset/image (2).jpg
--------------------------------------------------------------------------------
/SwiftUICodeSnippets/Assets.xcassets/images/background-2.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "image (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 |
--------------------------------------------------------------------------------
/SwiftUICodeSnippets/Assets.xcassets/images/background-2.imageset/image (3).jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mas0nSun/SwiftUICodeSnippets/2dcf104ed16fb79a01a9a802d6c63236d74de5b2/SwiftUICodeSnippets/Assets.xcassets/images/background-2.imageset/image (3).jpg
--------------------------------------------------------------------------------
/SwiftUICodeSnippets/Assets.xcassets/images/background.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "image (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 |
--------------------------------------------------------------------------------
/SwiftUICodeSnippets/Assets.xcassets/images/background.imageset/image (1).jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mas0nSun/SwiftUICodeSnippets/2dcf104ed16fb79a01a9a802d6c63236d74de5b2/SwiftUICodeSnippets/Assets.xcassets/images/background.imageset/image (1).jpg
--------------------------------------------------------------------------------
/SwiftUICodeSnippets/Assets.xcassets/images/car-image-1.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "image1.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 |
--------------------------------------------------------------------------------
/SwiftUICodeSnippets/Assets.xcassets/images/car-image-1.imageset/image1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mas0nSun/SwiftUICodeSnippets/2dcf104ed16fb79a01a9a802d6c63236d74de5b2/SwiftUICodeSnippets/Assets.xcassets/images/car-image-1.imageset/image1.jpg
--------------------------------------------------------------------------------
/SwiftUICodeSnippets/Assets.xcassets/images/car-image-2.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "image2.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 |
--------------------------------------------------------------------------------
/SwiftUICodeSnippets/Assets.xcassets/images/car-image-2.imageset/image2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mas0nSun/SwiftUICodeSnippets/2dcf104ed16fb79a01a9a802d6c63236d74de5b2/SwiftUICodeSnippets/Assets.xcassets/images/car-image-2.imageset/image2.jpg
--------------------------------------------------------------------------------
/SwiftUICodeSnippets/Assets.xcassets/images/image-1.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "image-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 |
--------------------------------------------------------------------------------
/SwiftUICodeSnippets/Assets.xcassets/images/image-1.imageset/image-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mas0nSun/SwiftUICodeSnippets/2dcf104ed16fb79a01a9a802d6c63236d74de5b2/SwiftUICodeSnippets/Assets.xcassets/images/image-1.imageset/image-1.jpg
--------------------------------------------------------------------------------
/SwiftUICodeSnippets/Assets.xcassets/images/image-2.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "image-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 |
--------------------------------------------------------------------------------
/SwiftUICodeSnippets/Assets.xcassets/images/image-2.imageset/image-2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mas0nSun/SwiftUICodeSnippets/2dcf104ed16fb79a01a9a802d6c63236d74de5b2/SwiftUICodeSnippets/Assets.xcassets/images/image-2.imageset/image-2.jpg
--------------------------------------------------------------------------------
/SwiftUICodeSnippets/Assets.xcassets/images/image-3.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "image-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 |
--------------------------------------------------------------------------------
/SwiftUICodeSnippets/Assets.xcassets/images/image-3.imageset/image-3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mas0nSun/SwiftUICodeSnippets/2dcf104ed16fb79a01a9a802d6c63236d74de5b2/SwiftUICodeSnippets/Assets.xcassets/images/image-3.imageset/image-3.jpg
--------------------------------------------------------------------------------
/SwiftUICodeSnippets/Assets.xcassets/images/image-4.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "image-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 |
--------------------------------------------------------------------------------
/SwiftUICodeSnippets/Assets.xcassets/images/image-4.imageset/image-4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mas0nSun/SwiftUICodeSnippets/2dcf104ed16fb79a01a9a802d6c63236d74de5b2/SwiftUICodeSnippets/Assets.xcassets/images/image-4.imageset/image-4.jpg
--------------------------------------------------------------------------------
/SwiftUICodeSnippets/Assets.xcassets/images/image-5.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "image-5.jpg",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/SwiftUICodeSnippets/Assets.xcassets/images/image-5.imageset/image-5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mas0nSun/SwiftUICodeSnippets/2dcf104ed16fb79a01a9a802d6c63236d74de5b2/SwiftUICodeSnippets/Assets.xcassets/images/image-5.imageset/image-5.jpg
--------------------------------------------------------------------------------
/SwiftUICodeSnippets/Assets.xcassets/images/image-6.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "image.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 |
--------------------------------------------------------------------------------
/SwiftUICodeSnippets/Assets.xcassets/images/image-6.imageset/image.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mas0nSun/SwiftUICodeSnippets/2dcf104ed16fb79a01a9a802d6c63236d74de5b2/SwiftUICodeSnippets/Assets.xcassets/images/image-6.imageset/image.jpg
--------------------------------------------------------------------------------
/SwiftUICodeSnippets/ContentView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ContentView.swift
3 | // SwiftUICodeSnippets
4 | //
5 | // Created by Mason Sun on 2024/12/22.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct ContentView: View {
11 | @Namespace private var list
12 |
13 | var body: some View {
14 | NavigationStack {
15 | List {
16 | ForEach(AllComponents.allCases, id: \.self) { component in
17 | NavigationLink {
18 | component.makeView()
19 | .navigationBarBackButtonHidden()
20 | .navigationTransition(.zoom(sourceID: component, in: list))
21 | } label: {
22 | Text(component.rawValue)
23 | }
24 | .matchedTransitionSource(
25 | id: component,
26 | in: list
27 | )
28 | }
29 | }
30 | }
31 | }
32 | }
33 |
34 | #Preview {
35 | ContentView()
36 | }
37 |
38 | enum AllComponents: String, CaseIterable {
39 | case rainbowText = "RainbowText"
40 | case scaledButton = "ScaledButton"
41 | case buttonRotateAnimation = "ButtonRotateAnimation"
42 | case segmentedControl = "SegmentedControl"
43 | case symbolImageAnimation = "SymbolImageAnimation"
44 | case popoverTip = "PopoverTip"
45 | case polishAnimation = "PolishAnimation"
46 | case navigationTransition = "NavigationTransition"
47 | case imageTransition = "ImageTransition"
48 | case stickyHeader = "StickyHeader"
49 | case rainbowButton = "RainbowButton"
50 | case animatedBorder = "AnimatedBorder"
51 | case funnyToggle = "FunnyToggle"
52 | case todoCell = "TodoCell"
53 | case scrollTransition = "ScrollTransition"
54 | case meshGradient = "MeshGradient"
55 | case loginPage = "LoginPage"
56 | case subscribePage = "SubscribePage"
57 | case numericAnimation = "NumericAnimation"
58 | case waveShader = "WaveShader"
59 | case snowShader = "SnowShader"
60 | case daysPostponement = "DaysPostponement"
61 | case calendarCard = "CalendarCard"
62 | case purchaseCard = "PurchaseCad"
63 | case backgroundColor = "BackgroundColor"
64 | case horizontalPicker = "HorizontalPicker"
65 | case starsBackgroundView = "StarsBackgroundView"
66 |
67 | @ViewBuilder
68 | func makeView() -> some View {
69 | switch self {
70 | case .rainbowText:
71 | RainbowTextNumericView()
72 | case .scaledButton:
73 | ScaledButtonView()
74 | case .buttonRotateAnimation:
75 | ButtonRotateAnimation()
76 | case .segmentedControl:
77 | CustomSegmentedControl()
78 | case .symbolImageAnimation:
79 | SymbolImageAnimation()
80 | case .popoverTip:
81 | PopoverTipView()
82 | case .polishAnimation:
83 | PolishView()
84 | case .navigationTransition:
85 | NavigationTransitionAnimationView()
86 | case .imageTransition:
87 | ImageTransitionAnimationView()
88 | case .stickyHeader:
89 | StickyHeaderView()
90 | case .rainbowButton:
91 | RainbowButton()
92 | case .animatedBorder:
93 | AnimatedBorderView()
94 | case .funnyToggle:
95 | FunnyToggleView()
96 | case .todoCell:
97 | TodoCellsView()
98 | case .scrollTransition:
99 | ScrollViewTransition()
100 | case .meshGradient:
101 | MeshGradientView()
102 | case .loginPage:
103 | LoginPage()
104 | case .subscribePage:
105 | SubscribePage()
106 | case .numericAnimation:
107 | NumericAnimationView()
108 | case .waveShader:
109 | WaveShaderView()
110 | case .snowShader:
111 | SnowShaderView()
112 | case .daysPostponement:
113 | DaysPostponementView()
114 | case .calendarCard:
115 | CalendarCardView()
116 | case .purchaseCard:
117 | PurchaseCardView()
118 | case .backgroundColor:
119 | BackgroundColorAnimationView()
120 | case .horizontalPicker:
121 | HorizontalPickerView()
122 | case .starsBackgroundView:
123 | GoProSectionView()
124 | }
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/SwiftUICodeSnippets/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/SwiftUICodeSnippets/SwiftUICodeSnippetsApp.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwiftUICodeSnippetsApp.swift
3 | // SwiftUICodeSnippets
4 | //
5 | // Created by Mason Sun on 2024/12/22.
6 | //
7 |
8 | import SwiftUI
9 |
10 | @main
11 | struct SwiftUICodeSnippetsApp: App {
12 | var body: some Scene {
13 | WindowGroup {
14 | ContentView()
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Widgets/AppIntent.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppIntent.swift
3 | // Widgets
4 | //
5 | // Created by Mason Sun on 2025/1/21.
6 | //
7 |
8 | import WidgetKit
9 | import AppIntents
10 |
11 | struct ConfigurationAppIntent: WidgetConfigurationIntent {
12 | static var title: LocalizedStringResource { "Configuration" }
13 | static var description: IntentDescription { "This is an example widget." }
14 |
15 | // An example configurable parameter.
16 | @Parameter(title: "Favorite Emoji", default: "😃")
17 | var favoriteEmoji: String
18 | }
19 |
--------------------------------------------------------------------------------
/Widgets/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Widgets/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "platform" : "ios",
6 | "size" : "1024x1024"
7 | },
8 | {
9 | "appearances" : [
10 | {
11 | "appearance" : "luminosity",
12 | "value" : "dark"
13 | }
14 | ],
15 | "idiom" : "universal",
16 | "platform" : "ios",
17 | "size" : "1024x1024"
18 | },
19 | {
20 | "appearances" : [
21 | {
22 | "appearance" : "luminosity",
23 | "value" : "tinted"
24 | }
25 | ],
26 | "idiom" : "universal",
27 | "platform" : "ios",
28 | "size" : "1024x1024"
29 | }
30 | ],
31 | "info" : {
32 | "author" : "xcode",
33 | "version" : 1
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Widgets/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Widgets/Assets.xcassets/WidgetBackground.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Widgets/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | NSExtension
6 |
7 | NSExtensionPointIdentifier
8 | com.apple.widgetkit-extension
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/Widgets/Widgets.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Widgets.swift
3 | // Widgets
4 | //
5 | // Created by Mason Sun on 2025/1/21.
6 | //
7 |
8 | import WidgetKit
9 | import SwiftUI
10 |
11 | struct Provider: AppIntentTimelineProvider {
12 | func placeholder(in context: Context) -> SimpleEntry {
13 | SimpleEntry(date: Date(), configuration: ConfigurationAppIntent())
14 | }
15 |
16 | func snapshot(for configuration: ConfigurationAppIntent, in context: Context) async -> SimpleEntry {
17 | SimpleEntry(date: Date(), configuration: configuration)
18 | }
19 |
20 | func timeline(for configuration: ConfigurationAppIntent, in context: Context) async -> Timeline {
21 | var entries: [SimpleEntry] = []
22 |
23 | // Generate a timeline consisting of five entries an hour apart, starting from the current date.
24 | let currentDate = Date()
25 | for hourOffset in 0 ..< 5 {
26 | let entryDate = Calendar.current.date(byAdding: .hour, value: hourOffset, to: currentDate)!
27 | let entry = SimpleEntry(date: entryDate, configuration: configuration)
28 | entries.append(entry)
29 | }
30 |
31 | return Timeline(entries: entries, policy: .atEnd)
32 | }
33 |
34 | // func relevances() async -> WidgetRelevances {
35 | // // Generate a list containing the contexts this widget is relevant in.
36 | // }
37 | }
38 |
39 | struct SimpleEntry: TimelineEntry {
40 | let date: Date
41 | let configuration: ConfigurationAppIntent
42 | }
43 |
44 | struct WidgetsEntryView : View {
45 | var entry: Provider.Entry
46 |
47 | var body: some View {
48 | VStack {
49 | Text("Time:")
50 | Text(entry.date, style: .time)
51 |
52 | Text("Favorite Emoji:")
53 | Text(entry.configuration.favoriteEmoji)
54 | }
55 | }
56 | }
57 |
58 | struct Widgets: Widget {
59 | let kind: String = "Widgets"
60 |
61 | var body: some WidgetConfiguration {
62 | AppIntentConfiguration(kind: kind, intent: ConfigurationAppIntent.self, provider: Provider()) { entry in
63 | WidgetsEntryView(entry: entry)
64 | .containerBackground(.fill.tertiary, for: .widget)
65 | }
66 | }
67 | }
68 |
69 | extension ConfigurationAppIntent {
70 | fileprivate static var smiley: ConfigurationAppIntent {
71 | let intent = ConfigurationAppIntent()
72 | intent.favoriteEmoji = "😀"
73 | return intent
74 | }
75 |
76 | fileprivate static var starEyes: ConfigurationAppIntent {
77 | let intent = ConfigurationAppIntent()
78 | intent.favoriteEmoji = "🤩"
79 | return intent
80 | }
81 | }
82 |
83 | #Preview(as: .systemSmall) {
84 | Widgets()
85 | } timeline: {
86 | SimpleEntry(date: .now, configuration: .smiley)
87 | SimpleEntry(date: .now, configuration: .starEyes)
88 | }
89 |
--------------------------------------------------------------------------------
/Widgets/WidgetsBundle.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WidgetsBundle.swift
3 | // Widgets
4 | //
5 | // Created by Mason Sun on 2025/1/21.
6 | //
7 |
8 | import WidgetKit
9 | import SwiftUI
10 |
11 | @main
12 | struct WidgetsBundle: WidgetBundle {
13 | var body: some Widget {
14 | Widgets()
15 | }
16 | }
17 |
--------------------------------------------------------------------------------