├── .gitignore
├── README.md
├── SwiftUIViews.xcodeproj
├── project.pbxproj
└── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ └── IDEWorkspaceChecks.plist
├── SwiftUIViews
├── Assets.xcassets
│ ├── AccentColor.colorset
│ │ └── Contents.json
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ └── Contents.json
├── ContentView.swift
├── Controls
│ ├── Button.swift
│ ├── ColorPicker.swift
│ ├── DatePicker.swift
│ ├── DisclosureGroup.swift
│ ├── EditButton.swift
│ ├── Form.swift
│ ├── GroupBox.swift
│ ├── Label.swift
│ ├── Link.swift
│ ├── List.swift
│ ├── NavigationLink.swift
│ ├── NavigationView.swift
│ ├── OutlineGroup.swift
│ ├── Picker.swift
│ ├── ProgressView.swift
│ ├── ScrollView.swift
│ ├── Section.swift
│ ├── SecureField.swift
│ ├── Slider.swift
│ ├── Stepper.swift
│ ├── TabView.swift
│ ├── Text.swift
│ ├── TextEditor.swift
│ ├── TextField.swift
│ └── Toggle.swift
├── Info.plist
├── Layout
│ ├── GeometryReader.swift
│ ├── HStack.swift
│ ├── LazyHGrid.swift
│ ├── LazyHStack.swift
│ ├── LazyVGrid.swift
│ ├── LazyVStack.swift
│ ├── ScrollViewReader.swift
│ ├── Spacer.swift
│ ├── VStack.swift
│ └── ZStack.swift
├── Other
│ ├── Divider.swift
│ ├── EmptyView.swift
│ ├── Menu.swift
│ └── Shapes.swift
├── Paints
│ ├── AngularGradient.swift
│ ├── LinearGradient.swift
│ └── RadialGradient.swift
├── Preview Content
│ └── Preview Assets.xcassets
│ │ └── Contents.json
└── SwiftUIViewsApp.swift
└── img
├── controls.gif
└── hierachical_list.gif
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## User settings
6 | xcuserdata/
7 |
8 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
9 | *.xcscmblueprint
10 | *.xccheckout
11 |
12 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
13 | build/
14 | DerivedData/
15 | *.moved-aside
16 | *.pbxuser
17 | !default.pbxuser
18 | *.mode1v3
19 | !default.mode1v3
20 | *.mode2v3
21 | !default.mode2v3
22 | *.perspectivev3
23 | !default.perspectivev3
24 |
25 | ## Obj-C/Swift specific
26 | *.hmap
27 |
28 | ## App packaging
29 | *.ipa
30 | *.dSYM.zip
31 | *.dSYM
32 |
33 | ## Playgrounds
34 | timeline.xctimeline
35 | playground.xcworkspace
36 |
37 | # Swift Package Manager
38 | #
39 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
40 | # Packages/
41 | # Package.pins
42 | # Package.resolved
43 | # *.xcodeproj
44 | #
45 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
46 | # hence it is not needed unless you have added a package configuration file to your project
47 | # .swiftpm
48 |
49 | .build/
50 |
51 | # CocoaPods
52 | #
53 | # We recommend against adding the Pods directory to your .gitignore. However
54 | # you should judge for yourself, the pros and cons are mentioned at:
55 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
56 | #
57 | # Pods/
58 | #
59 | # Add this line if you want to avoid checking in source code from the Xcode workspace
60 | # *.xcworkspace
61 |
62 | # Carthage
63 | #
64 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
65 | # Carthage/Checkouts
66 |
67 | Carthage/Build/
68 |
69 | # Accio dependency management
70 | Dependencies/
71 | .accio/
72 |
73 | # fastlane
74 | #
75 | # It is recommended to not store the screenshots in the git repo.
76 | # Instead, use fastlane to re-generate the screenshots whenever they are needed.
77 | # For more information about the recommended setup visit:
78 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
79 |
80 | fastlane/report.xml
81 | fastlane/Preview.html
82 | fastlane/screenshots/**/*.png
83 | fastlane/test_output
84 |
85 | # Code Injection
86 | #
87 | # After new code Injection tools there's a generated folder /iOSInjectionProject
88 | # https://github.com/johnno1962/injectionforxcode
89 |
90 | iOSInjectionProject/
91 |
92 | **/.DS_Store
93 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # SwiftUI 全部视图文档用例及解释
2 |
3 | > 想学习 SwiftUI 但是英文文档读得头大?开发项目不知道 SwiftUI 的就能完成任务,还是需要回头用 UIKit?好像在某些地方看到过一个控件的用法,但是怎么也记不起来了?想解决这些问题,你就找对地方了!
4 |
5 | 本项目基于 SwiftUI 官方文档实例,对所有的 SwiftUI 控件视图的用例进行了总结。在对应的文件夹中总结了对应的组件,例如 `Controls` 文件夹中,就有目前所有的 25 个控件的常规用法总结。
6 |
7 |
8 |
9 |
10 |
11 | 对于每个控件,我们都尽量提供全面的用例,同时配合中文注释。同时,配合 SwiftUI 的预览功能,你可以一边看代码,一边对照到动态效果。
12 |
13 |
14 |
15 |
16 |
17 | ## Contributing
18 |
19 | 欢迎大家纠正项目中可能出现的纰漏和问题,也欢迎大家补充控件的新用法~ 让我们一起把这个项目变得更加完善!
20 |
21 | ## TODO
22 |
23 | - [x] 添加控件视图 (25/25)
24 | - [x] 添加布局视图 (10/10)
25 | - [x] 添加颜色视图 (3/3)
26 | - [ ] 添加其他视图 (8/13)
27 | - [ ] 添加 macOS 例子
28 |
29 | ---
30 |
31 | 如果本项目给你带来了方便,不妨点个 star 再走~ 如果 README 中的图片无法加载,可能是因为 github 中的图片被墙了。你也可以看看这篇[掘金博文](https://juejin.cn/post/6926711998461935623)了解相关的信息。
32 |
--------------------------------------------------------------------------------
/SwiftUIViews.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 50;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | B302755225DFB79F001D7703 /* GeometryReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = B302755125DFB79F001D7703 /* GeometryReader.swift */; };
11 | B302755525DFC621001D7703 /* HStack.swift in Sources */ = {isa = PBXBuildFile; fileRef = B302755425DFC621001D7703 /* HStack.swift */; };
12 | B3396FAA25DD17510025C7E3 /* ZStack.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3396FA925DD17510025C7E3 /* ZStack.swift */; };
13 | B34D2AAD25CEE84D000361B0 /* SwiftUIViewsApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = B34D2AAC25CEE84D000361B0 /* SwiftUIViewsApp.swift */; };
14 | B34D2AAF25CEE84D000361B0 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B34D2AAE25CEE84D000361B0 /* ContentView.swift */; };
15 | B34D2AB125CEE84F000361B0 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B34D2AB025CEE84F000361B0 /* Assets.xcassets */; };
16 | B34D2AB425CEE84F000361B0 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B34D2AB325CEE84F000361B0 /* Preview Assets.xcassets */; };
17 | B34D2AC325CEE9EC000361B0 /* Button.swift in Sources */ = {isa = PBXBuildFile; fileRef = B34D2AC225CEE9EC000361B0 /* Button.swift */; };
18 | B3527D5525CFC5FC0022E1B9 /* Stepper.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3527D5425CFC5FC0022E1B9 /* Stepper.swift */; };
19 | B3527D5825CFCA8B0022E1B9 /* TabView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3527D5725CFCA8B0022E1B9 /* TabView.swift */; };
20 | B3527D5C25CFCDDC0022E1B9 /* OutlineGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3527D5B25CFCDDC0022E1B9 /* OutlineGroup.swift */; };
21 | B3527D6325CFD13B0022E1B9 /* TextEditor.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3527D6225CFD13B0022E1B9 /* TextEditor.swift */; };
22 | B3527D6625CFD3F60022E1B9 /* TextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3527D6525CFD3F60022E1B9 /* TextField.swift */; };
23 | B3527D6925CFE6450022E1B9 /* Section.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3527D6825CFE6450022E1B9 /* Section.swift */; };
24 | B3527D6C25CFEA480022E1B9 /* ScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3527D6B25CFEA480022E1B9 /* ScrollView.swift */; };
25 | B3527D6F25CFEF030022E1B9 /* NavigationLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3527D6E25CFEF030022E1B9 /* NavigationLink.swift */; };
26 | B3527D7425CFFF360022E1B9 /* NavigationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3527D7325CFFF360022E1B9 /* NavigationView.swift */; };
27 | B376D44A25DFF8CF0083488A /* LazyHStack.swift in Sources */ = {isa = PBXBuildFile; fileRef = B376D44925DFF8CF0083488A /* LazyHStack.swift */; };
28 | B376D44D25DFFA230083488A /* LazyVStack.swift in Sources */ = {isa = PBXBuildFile; fileRef = B376D44C25DFFA230083488A /* LazyVStack.swift */; };
29 | B376D45025DFFABE0083488A /* Spacer.swift in Sources */ = {isa = PBXBuildFile; fileRef = B376D44F25DFFABE0083488A /* Spacer.swift */; };
30 | B376D45325DFFD4B0083488A /* VStack.swift in Sources */ = {isa = PBXBuildFile; fileRef = B376D45225DFFD4B0083488A /* VStack.swift */; };
31 | B376D45625DFFF8B0083488A /* ScrollViewReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = B376D45525DFFF8B0083488A /* ScrollViewReader.swift */; };
32 | B376D46025E1026F0083488A /* AngularGradient.swift in Sources */ = {isa = PBXBuildFile; fileRef = B376D45F25E1026F0083488A /* AngularGradient.swift */; };
33 | B376D46425E1033F0083488A /* LinearGradient.swift in Sources */ = {isa = PBXBuildFile; fileRef = B376D46325E1033F0083488A /* LinearGradient.swift */; };
34 | B376D46725E1039C0083488A /* RadialGradient.swift in Sources */ = {isa = PBXBuildFile; fileRef = B376D46625E1039C0083488A /* RadialGradient.swift */; };
35 | B3B52E9725CF82EF00F71D03 /* ColorPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3B52E9625CF82EF00F71D03 /* ColorPicker.swift */; };
36 | B3B52E9A25CF856D00F71D03 /* DatePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3B52E9925CF856D00F71D03 /* DatePicker.swift */; };
37 | B3B52E9D25CF8A5300F71D03 /* DisclosureGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3B52E9C25CF8A5300F71D03 /* DisclosureGroup.swift */; };
38 | B3B52EA025CF8C5400F71D03 /* EditButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3B52E9F25CF8C5400F71D03 /* EditButton.swift */; };
39 | B3B52EA325CF908F00F71D03 /* Form.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3B52EA225CF908F00F71D03 /* Form.swift */; };
40 | B3B52EA725CF942F00F71D03 /* GroupBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3B52EA625CF942F00F71D03 /* GroupBox.swift */; };
41 | B3B52EAA25CF96B500F71D03 /* Label.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3B52EA925CF96B500F71D03 /* Label.swift */; };
42 | B3B52EAD25CFA30B00F71D03 /* Link.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3B52EAC25CFA30B00F71D03 /* Link.swift */; };
43 | B3B52EB025CFA67500F71D03 /* List.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3B52EAF25CFA67500F71D03 /* List.swift */; };
44 | B3B52EB325CFA69B00F71D03 /* Toggle.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3B52EB225CFA69B00F71D03 /* Toggle.swift */; };
45 | B3B52EB625CFA8C600F71D03 /* Text.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3B52EB525CFA8C600F71D03 /* Text.swift */; };
46 | B3B52EB925CFABF300F71D03 /* Picker.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3B52EB825CFABF300F71D03 /* Picker.swift */; };
47 | B3B52EBC25CFB42400F71D03 /* ProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3B52EBB25CFB42400F71D03 /* ProgressView.swift */; };
48 | B3B52EBF25CFB79A00F71D03 /* SecureField.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3B52EBE25CFB79A00F71D03 /* SecureField.swift */; };
49 | B3B52EC325CFBA6B00F71D03 /* Slider.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3B52EC225CFBA6B00F71D03 /* Slider.swift */; };
50 | B3D66B5825E3C1AE0026DAF9 /* Menu.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3D66B5725E3C1AE0026DAF9 /* Menu.swift */; };
51 | B3E1BBBB25E3E77300D3515B /* Shapes.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3E1BBBA25E3E77300D3515B /* Shapes.swift */; };
52 | B3E1BBBE25E3EBE100D3515B /* Divider.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3E1BBBD25E3EBE100D3515B /* Divider.swift */; };
53 | B3E1BBC225E3ED7100D3515B /* EmptyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3E1BBC125E3ED7100D3515B /* EmptyView.swift */; };
54 | B3E1BBC525E3F28600D3515B /* LazyHGrid.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3E1BBC425E3F28600D3515B /* LazyHGrid.swift */; };
55 | B3E1BBC925E3FE7F00D3515B /* LazyVGrid.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3E1BBC825E3FE7F00D3515B /* LazyVGrid.swift */; };
56 | /* End PBXBuildFile section */
57 |
58 | /* Begin PBXFileReference section */
59 | B302755125DFB79F001D7703 /* GeometryReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeometryReader.swift; sourceTree = ""; };
60 | B302755425DFC621001D7703 /* HStack.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HStack.swift; sourceTree = ""; };
61 | B3396FA925DD17510025C7E3 /* ZStack.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZStack.swift; sourceTree = ""; };
62 | B34D2AA925CEE84D000361B0 /* SwiftUIViews.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwiftUIViews.app; sourceTree = BUILT_PRODUCTS_DIR; };
63 | B34D2AAC25CEE84D000361B0 /* SwiftUIViewsApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUIViewsApp.swift; sourceTree = ""; };
64 | B34D2AAE25CEE84D000361B0 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; };
65 | B34D2AB025CEE84F000361B0 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
66 | B34D2AB325CEE84F000361B0 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; };
67 | B34D2AB525CEE84F000361B0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
68 | B34D2AC225CEE9EC000361B0 /* Button.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Button.swift; sourceTree = ""; };
69 | B3527D5425CFC5FC0022E1B9 /* Stepper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Stepper.swift; sourceTree = ""; };
70 | B3527D5725CFCA8B0022E1B9 /* TabView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabView.swift; sourceTree = ""; };
71 | B3527D5B25CFCDDC0022E1B9 /* OutlineGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OutlineGroup.swift; sourceTree = ""; };
72 | B3527D6225CFD13B0022E1B9 /* TextEditor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextEditor.swift; sourceTree = ""; };
73 | B3527D6525CFD3F60022E1B9 /* TextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextField.swift; sourceTree = ""; };
74 | B3527D6825CFE6450022E1B9 /* Section.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Section.swift; sourceTree = ""; };
75 | B3527D6B25CFEA480022E1B9 /* ScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollView.swift; sourceTree = ""; };
76 | B3527D6E25CFEF030022E1B9 /* NavigationLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationLink.swift; sourceTree = ""; };
77 | B3527D7325CFFF360022E1B9 /* NavigationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationView.swift; sourceTree = ""; };
78 | B376D44925DFF8CF0083488A /* LazyHStack.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LazyHStack.swift; sourceTree = ""; };
79 | B376D44C25DFFA230083488A /* LazyVStack.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LazyVStack.swift; sourceTree = ""; };
80 | B376D44F25DFFABE0083488A /* Spacer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Spacer.swift; sourceTree = ""; };
81 | B376D45225DFFD4B0083488A /* VStack.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VStack.swift; sourceTree = ""; };
82 | B376D45525DFFF8B0083488A /* ScrollViewReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollViewReader.swift; sourceTree = ""; };
83 | B376D45F25E1026F0083488A /* AngularGradient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AngularGradient.swift; sourceTree = ""; };
84 | B376D46325E1033F0083488A /* LinearGradient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinearGradient.swift; sourceTree = ""; };
85 | B376D46625E1039C0083488A /* RadialGradient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadialGradient.swift; sourceTree = ""; };
86 | B3B52E9625CF82EF00F71D03 /* ColorPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorPicker.swift; sourceTree = ""; };
87 | B3B52E9925CF856D00F71D03 /* DatePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatePicker.swift; sourceTree = ""; };
88 | B3B52E9C25CF8A5300F71D03 /* DisclosureGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisclosureGroup.swift; sourceTree = ""; };
89 | B3B52E9F25CF8C5400F71D03 /* EditButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditButton.swift; sourceTree = ""; };
90 | B3B52EA225CF908F00F71D03 /* Form.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Form.swift; sourceTree = ""; };
91 | B3B52EA625CF942F00F71D03 /* GroupBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupBox.swift; sourceTree = ""; };
92 | B3B52EA925CF96B500F71D03 /* Label.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Label.swift; sourceTree = ""; };
93 | B3B52EAC25CFA30B00F71D03 /* Link.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Link.swift; sourceTree = ""; };
94 | B3B52EAF25CFA67500F71D03 /* List.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = List.swift; sourceTree = ""; };
95 | B3B52EB225CFA69B00F71D03 /* Toggle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Toggle.swift; sourceTree = ""; };
96 | B3B52EB525CFA8C600F71D03 /* Text.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Text.swift; sourceTree = ""; };
97 | B3B52EB825CFABF300F71D03 /* Picker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Picker.swift; sourceTree = ""; };
98 | B3B52EBB25CFB42400F71D03 /* ProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressView.swift; sourceTree = ""; };
99 | B3B52EBE25CFB79A00F71D03 /* SecureField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureField.swift; sourceTree = ""; };
100 | B3B52EC225CFBA6B00F71D03 /* Slider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Slider.swift; sourceTree = ""; };
101 | B3D66B5725E3C1AE0026DAF9 /* Menu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Menu.swift; sourceTree = ""; };
102 | B3E1BBBA25E3E77300D3515B /* Shapes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Shapes.swift; sourceTree = ""; };
103 | B3E1BBBD25E3EBE100D3515B /* Divider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Divider.swift; sourceTree = ""; };
104 | B3E1BBC125E3ED7100D3515B /* EmptyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyView.swift; sourceTree = ""; };
105 | B3E1BBC425E3F28600D3515B /* LazyHGrid.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LazyHGrid.swift; sourceTree = ""; };
106 | B3E1BBC825E3FE7F00D3515B /* LazyVGrid.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LazyVGrid.swift; sourceTree = ""; };
107 | /* End PBXFileReference section */
108 |
109 | /* Begin PBXFrameworksBuildPhase section */
110 | B34D2AA625CEE84D000361B0 /* Frameworks */ = {
111 | isa = PBXFrameworksBuildPhase;
112 | buildActionMask = 2147483647;
113 | files = (
114 | );
115 | runOnlyForDeploymentPostprocessing = 0;
116 | };
117 | /* End PBXFrameworksBuildPhase section */
118 |
119 | /* Begin PBXGroup section */
120 | B3396FA825DD171F0025C7E3 /* Layout */ = {
121 | isa = PBXGroup;
122 | children = (
123 | B302755125DFB79F001D7703 /* GeometryReader.swift */,
124 | B302755425DFC621001D7703 /* HStack.swift */,
125 | B3E1BBC425E3F28600D3515B /* LazyHGrid.swift */,
126 | B376D44925DFF8CF0083488A /* LazyHStack.swift */,
127 | B3E1BBC825E3FE7F00D3515B /* LazyVGrid.swift */,
128 | B376D44C25DFFA230083488A /* LazyVStack.swift */,
129 | B376D45525DFFF8B0083488A /* ScrollViewReader.swift */,
130 | B376D44F25DFFABE0083488A /* Spacer.swift */,
131 | B376D45225DFFD4B0083488A /* VStack.swift */,
132 | B3396FA925DD17510025C7E3 /* ZStack.swift */,
133 | );
134 | path = Layout;
135 | sourceTree = "";
136 | };
137 | B34D2AA025CEE84D000361B0 = {
138 | isa = PBXGroup;
139 | children = (
140 | B34D2AAB25CEE84D000361B0 /* SwiftUIViews */,
141 | B34D2AAA25CEE84D000361B0 /* Products */,
142 | );
143 | sourceTree = "";
144 | };
145 | B34D2AAA25CEE84D000361B0 /* Products */ = {
146 | isa = PBXGroup;
147 | children = (
148 | B34D2AA925CEE84D000361B0 /* SwiftUIViews.app */,
149 | );
150 | name = Products;
151 | sourceTree = "";
152 | };
153 | B34D2AAB25CEE84D000361B0 /* SwiftUIViews */ = {
154 | isa = PBXGroup;
155 | children = (
156 | B34D2AAC25CEE84D000361B0 /* SwiftUIViewsApp.swift */,
157 | B34D2AAE25CEE84D000361B0 /* ContentView.swift */,
158 | B34D2AC525CEEA4A000361B0 /* Controls */,
159 | B3396FA825DD171F0025C7E3 /* Layout */,
160 | B376D45E25E102480083488A /* Paints */,
161 | B3D66B5625E3C18E0026DAF9 /* Other */,
162 | B34D2AB025CEE84F000361B0 /* Assets.xcassets */,
163 | B34D2AB525CEE84F000361B0 /* Info.plist */,
164 | B34D2AB225CEE84F000361B0 /* Preview Content */,
165 | );
166 | path = SwiftUIViews;
167 | sourceTree = "";
168 | };
169 | B34D2AB225CEE84F000361B0 /* Preview Content */ = {
170 | isa = PBXGroup;
171 | children = (
172 | B34D2AB325CEE84F000361B0 /* Preview Assets.xcassets */,
173 | );
174 | path = "Preview Content";
175 | sourceTree = "";
176 | };
177 | B34D2AC525CEEA4A000361B0 /* Controls */ = {
178 | isa = PBXGroup;
179 | children = (
180 | B34D2AC225CEE9EC000361B0 /* Button.swift */,
181 | B3B52E9625CF82EF00F71D03 /* ColorPicker.swift */,
182 | B3B52E9925CF856D00F71D03 /* DatePicker.swift */,
183 | B3B52E9C25CF8A5300F71D03 /* DisclosureGroup.swift */,
184 | B3B52E9F25CF8C5400F71D03 /* EditButton.swift */,
185 | B3B52EA225CF908F00F71D03 /* Form.swift */,
186 | B3B52EA625CF942F00F71D03 /* GroupBox.swift */,
187 | B3B52EA925CF96B500F71D03 /* Label.swift */,
188 | B3B52EAC25CFA30B00F71D03 /* Link.swift */,
189 | B3B52EAF25CFA67500F71D03 /* List.swift */,
190 | B3527D6E25CFEF030022E1B9 /* NavigationLink.swift */,
191 | B3527D7325CFFF360022E1B9 /* NavigationView.swift */,
192 | B3527D5B25CFCDDC0022E1B9 /* OutlineGroup.swift */,
193 | B3B52EB825CFABF300F71D03 /* Picker.swift */,
194 | B3B52EBB25CFB42400F71D03 /* ProgressView.swift */,
195 | B3527D6B25CFEA480022E1B9 /* ScrollView.swift */,
196 | B3527D6825CFE6450022E1B9 /* Section.swift */,
197 | B3B52EBE25CFB79A00F71D03 /* SecureField.swift */,
198 | B3B52EC225CFBA6B00F71D03 /* Slider.swift */,
199 | B3527D5425CFC5FC0022E1B9 /* Stepper.swift */,
200 | B3527D5725CFCA8B0022E1B9 /* TabView.swift */,
201 | B3B52EB525CFA8C600F71D03 /* Text.swift */,
202 | B3527D6225CFD13B0022E1B9 /* TextEditor.swift */,
203 | B3527D6525CFD3F60022E1B9 /* TextField.swift */,
204 | B3B52EB225CFA69B00F71D03 /* Toggle.swift */,
205 | );
206 | path = Controls;
207 | sourceTree = "";
208 | };
209 | B376D45E25E102480083488A /* Paints */ = {
210 | isa = PBXGroup;
211 | children = (
212 | B376D45F25E1026F0083488A /* AngularGradient.swift */,
213 | B376D46325E1033F0083488A /* LinearGradient.swift */,
214 | B376D46625E1039C0083488A /* RadialGradient.swift */,
215 | );
216 | path = Paints;
217 | sourceTree = "";
218 | };
219 | B3D66B5625E3C18E0026DAF9 /* Other */ = {
220 | isa = PBXGroup;
221 | children = (
222 | B3E1BBBD25E3EBE100D3515B /* Divider.swift */,
223 | B3E1BBC125E3ED7100D3515B /* EmptyView.swift */,
224 | B3D66B5725E3C1AE0026DAF9 /* Menu.swift */,
225 | B3E1BBBA25E3E77300D3515B /* Shapes.swift */,
226 | );
227 | path = Other;
228 | sourceTree = "";
229 | };
230 | /* End PBXGroup section */
231 |
232 | /* Begin PBXNativeTarget section */
233 | B34D2AA825CEE84D000361B0 /* SwiftUIViews */ = {
234 | isa = PBXNativeTarget;
235 | buildConfigurationList = B34D2AB825CEE84F000361B0 /* Build configuration list for PBXNativeTarget "SwiftUIViews" */;
236 | buildPhases = (
237 | B34D2AA525CEE84D000361B0 /* Sources */,
238 | B34D2AA625CEE84D000361B0 /* Frameworks */,
239 | B34D2AA725CEE84D000361B0 /* Resources */,
240 | );
241 | buildRules = (
242 | );
243 | dependencies = (
244 | );
245 | name = SwiftUIViews;
246 | productName = SwiftUIViews;
247 | productReference = B34D2AA925CEE84D000361B0 /* SwiftUIViews.app */;
248 | productType = "com.apple.product-type.application";
249 | };
250 | /* End PBXNativeTarget section */
251 |
252 | /* Begin PBXProject section */
253 | B34D2AA125CEE84D000361B0 /* Project object */ = {
254 | isa = PBXProject;
255 | attributes = {
256 | LastSwiftUpdateCheck = 1240;
257 | LastUpgradeCheck = 1240;
258 | TargetAttributes = {
259 | B34D2AA825CEE84D000361B0 = {
260 | CreatedOnToolsVersion = 12.4;
261 | };
262 | };
263 | };
264 | buildConfigurationList = B34D2AA425CEE84D000361B0 /* Build configuration list for PBXProject "SwiftUIViews" */;
265 | compatibilityVersion = "Xcode 9.3";
266 | developmentRegion = en;
267 | hasScannedForEncodings = 0;
268 | knownRegions = (
269 | en,
270 | Base,
271 | );
272 | mainGroup = B34D2AA025CEE84D000361B0;
273 | productRefGroup = B34D2AAA25CEE84D000361B0 /* Products */;
274 | projectDirPath = "";
275 | projectRoot = "";
276 | targets = (
277 | B34D2AA825CEE84D000361B0 /* SwiftUIViews */,
278 | );
279 | };
280 | /* End PBXProject section */
281 |
282 | /* Begin PBXResourcesBuildPhase section */
283 | B34D2AA725CEE84D000361B0 /* Resources */ = {
284 | isa = PBXResourcesBuildPhase;
285 | buildActionMask = 2147483647;
286 | files = (
287 | B34D2AB425CEE84F000361B0 /* Preview Assets.xcassets in Resources */,
288 | B34D2AB125CEE84F000361B0 /* Assets.xcassets in Resources */,
289 | );
290 | runOnlyForDeploymentPostprocessing = 0;
291 | };
292 | /* End PBXResourcesBuildPhase section */
293 |
294 | /* Begin PBXSourcesBuildPhase section */
295 | B34D2AA525CEE84D000361B0 /* Sources */ = {
296 | isa = PBXSourcesBuildPhase;
297 | buildActionMask = 2147483647;
298 | files = (
299 | B34D2AAF25CEE84D000361B0 /* ContentView.swift in Sources */,
300 | B3B52EA325CF908F00F71D03 /* Form.swift in Sources */,
301 | B3527D6625CFD3F60022E1B9 /* TextField.swift in Sources */,
302 | B3B52E9725CF82EF00F71D03 /* ColorPicker.swift in Sources */,
303 | B3E1BBC525E3F28600D3515B /* LazyHGrid.swift in Sources */,
304 | B376D46425E1033F0083488A /* LinearGradient.swift in Sources */,
305 | B3E1BBC925E3FE7F00D3515B /* LazyVGrid.swift in Sources */,
306 | B3B52EA025CF8C5400F71D03 /* EditButton.swift in Sources */,
307 | B376D45025DFFABE0083488A /* Spacer.swift in Sources */,
308 | B3B52EC325CFBA6B00F71D03 /* Slider.swift in Sources */,
309 | B3B52EAD25CFA30B00F71D03 /* Link.swift in Sources */,
310 | B3D66B5825E3C1AE0026DAF9 /* Menu.swift in Sources */,
311 | B3B52EB025CFA67500F71D03 /* List.swift in Sources */,
312 | B376D46725E1039C0083488A /* RadialGradient.swift in Sources */,
313 | B3B52E9D25CF8A5300F71D03 /* DisclosureGroup.swift in Sources */,
314 | B376D44A25DFF8CF0083488A /* LazyHStack.swift in Sources */,
315 | B34D2AC325CEE9EC000361B0 /* Button.swift in Sources */,
316 | B3B52EBF25CFB79A00F71D03 /* SecureField.swift in Sources */,
317 | B3527D5C25CFCDDC0022E1B9 /* OutlineGroup.swift in Sources */,
318 | B34D2AAD25CEE84D000361B0 /* SwiftUIViewsApp.swift in Sources */,
319 | B3B52EB325CFA69B00F71D03 /* Toggle.swift in Sources */,
320 | B376D46025E1026F0083488A /* AngularGradient.swift in Sources */,
321 | B302755225DFB79F001D7703 /* GeometryReader.swift in Sources */,
322 | B3527D5825CFCA8B0022E1B9 /* TabView.swift in Sources */,
323 | B3B52EB625CFA8C600F71D03 /* Text.swift in Sources */,
324 | B3B52E9A25CF856D00F71D03 /* DatePicker.swift in Sources */,
325 | B3B52EAA25CF96B500F71D03 /* Label.swift in Sources */,
326 | B376D45625DFFF8B0083488A /* ScrollViewReader.swift in Sources */,
327 | B3527D6325CFD13B0022E1B9 /* TextEditor.swift in Sources */,
328 | B3E1BBBB25E3E77300D3515B /* Shapes.swift in Sources */,
329 | B3527D7425CFFF360022E1B9 /* NavigationView.swift in Sources */,
330 | B376D44D25DFFA230083488A /* LazyVStack.swift in Sources */,
331 | B3B52EBC25CFB42400F71D03 /* ProgressView.swift in Sources */,
332 | B3E1BBBE25E3EBE100D3515B /* Divider.swift in Sources */,
333 | B3B52EB925CFABF300F71D03 /* Picker.swift in Sources */,
334 | B3527D6925CFE6450022E1B9 /* Section.swift in Sources */,
335 | B376D45325DFFD4B0083488A /* VStack.swift in Sources */,
336 | B3396FAA25DD17510025C7E3 /* ZStack.swift in Sources */,
337 | B3527D6F25CFEF030022E1B9 /* NavigationLink.swift in Sources */,
338 | B3527D5525CFC5FC0022E1B9 /* Stepper.swift in Sources */,
339 | B302755525DFC621001D7703 /* HStack.swift in Sources */,
340 | B3527D6C25CFEA480022E1B9 /* ScrollView.swift in Sources */,
341 | B3E1BBC225E3ED7100D3515B /* EmptyView.swift in Sources */,
342 | B3B52EA725CF942F00F71D03 /* GroupBox.swift in Sources */,
343 | );
344 | runOnlyForDeploymentPostprocessing = 0;
345 | };
346 | /* End PBXSourcesBuildPhase section */
347 |
348 | /* Begin XCBuildConfiguration section */
349 | B34D2AB625CEE84F000361B0 /* Debug */ = {
350 | isa = XCBuildConfiguration;
351 | buildSettings = {
352 | ALWAYS_SEARCH_USER_PATHS = NO;
353 | CLANG_ANALYZER_NONNULL = YES;
354 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
355 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
356 | CLANG_CXX_LIBRARY = "libc++";
357 | CLANG_ENABLE_MODULES = YES;
358 | CLANG_ENABLE_OBJC_ARC = YES;
359 | CLANG_ENABLE_OBJC_WEAK = YES;
360 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
361 | CLANG_WARN_BOOL_CONVERSION = YES;
362 | CLANG_WARN_COMMA = YES;
363 | CLANG_WARN_CONSTANT_CONVERSION = YES;
364 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
365 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
366 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
367 | CLANG_WARN_EMPTY_BODY = YES;
368 | CLANG_WARN_ENUM_CONVERSION = YES;
369 | CLANG_WARN_INFINITE_RECURSION = YES;
370 | CLANG_WARN_INT_CONVERSION = YES;
371 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
372 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
373 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
374 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
375 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
376 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
377 | CLANG_WARN_STRICT_PROTOTYPES = YES;
378 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
379 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
380 | CLANG_WARN_UNREACHABLE_CODE = YES;
381 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
382 | COPY_PHASE_STRIP = NO;
383 | DEBUG_INFORMATION_FORMAT = dwarf;
384 | ENABLE_STRICT_OBJC_MSGSEND = YES;
385 | ENABLE_TESTABILITY = YES;
386 | GCC_C_LANGUAGE_STANDARD = gnu11;
387 | GCC_DYNAMIC_NO_PIC = NO;
388 | GCC_NO_COMMON_BLOCKS = YES;
389 | GCC_OPTIMIZATION_LEVEL = 0;
390 | GCC_PREPROCESSOR_DEFINITIONS = (
391 | "DEBUG=1",
392 | "$(inherited)",
393 | );
394 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
395 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
396 | GCC_WARN_UNDECLARED_SELECTOR = YES;
397 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
398 | GCC_WARN_UNUSED_FUNCTION = YES;
399 | GCC_WARN_UNUSED_VARIABLE = YES;
400 | IPHONEOS_DEPLOYMENT_TARGET = 14.4;
401 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
402 | MTL_FAST_MATH = YES;
403 | ONLY_ACTIVE_ARCH = YES;
404 | SDKROOT = iphoneos;
405 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
406 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
407 | };
408 | name = Debug;
409 | };
410 | B34D2AB725CEE84F000361B0 /* Release */ = {
411 | isa = XCBuildConfiguration;
412 | buildSettings = {
413 | ALWAYS_SEARCH_USER_PATHS = NO;
414 | CLANG_ANALYZER_NONNULL = YES;
415 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
416 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
417 | CLANG_CXX_LIBRARY = "libc++";
418 | CLANG_ENABLE_MODULES = YES;
419 | CLANG_ENABLE_OBJC_ARC = YES;
420 | CLANG_ENABLE_OBJC_WEAK = YES;
421 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
422 | CLANG_WARN_BOOL_CONVERSION = YES;
423 | CLANG_WARN_COMMA = YES;
424 | CLANG_WARN_CONSTANT_CONVERSION = YES;
425 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
426 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
427 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
428 | CLANG_WARN_EMPTY_BODY = YES;
429 | CLANG_WARN_ENUM_CONVERSION = YES;
430 | CLANG_WARN_INFINITE_RECURSION = YES;
431 | CLANG_WARN_INT_CONVERSION = YES;
432 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
433 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
434 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
435 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
436 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
437 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
438 | CLANG_WARN_STRICT_PROTOTYPES = YES;
439 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
440 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
441 | CLANG_WARN_UNREACHABLE_CODE = YES;
442 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
443 | COPY_PHASE_STRIP = NO;
444 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
445 | ENABLE_NS_ASSERTIONS = NO;
446 | ENABLE_STRICT_OBJC_MSGSEND = YES;
447 | GCC_C_LANGUAGE_STANDARD = gnu11;
448 | GCC_NO_COMMON_BLOCKS = YES;
449 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
450 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
451 | GCC_WARN_UNDECLARED_SELECTOR = YES;
452 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
453 | GCC_WARN_UNUSED_FUNCTION = YES;
454 | GCC_WARN_UNUSED_VARIABLE = YES;
455 | IPHONEOS_DEPLOYMENT_TARGET = 14.4;
456 | MTL_ENABLE_DEBUG_INFO = NO;
457 | MTL_FAST_MATH = YES;
458 | SDKROOT = iphoneos;
459 | SWIFT_COMPILATION_MODE = wholemodule;
460 | SWIFT_OPTIMIZATION_LEVEL = "-O";
461 | VALIDATE_PRODUCT = YES;
462 | };
463 | name = Release;
464 | };
465 | B34D2AB925CEE84F000361B0 /* Debug */ = {
466 | isa = XCBuildConfiguration;
467 | buildSettings = {
468 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
469 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
470 | CODE_SIGN_STYLE = Automatic;
471 | DEVELOPMENT_ASSET_PATHS = "\"SwiftUIViews/Preview Content\"";
472 | DEVELOPMENT_TEAM = CLLJ89U35Q;
473 | ENABLE_PREVIEWS = YES;
474 | INFOPLIST_FILE = SwiftUIViews/Info.plist;
475 | IPHONEOS_DEPLOYMENT_TARGET = 14.0;
476 | LD_RUNPATH_SEARCH_PATHS = (
477 | "$(inherited)",
478 | "@executable_path/Frameworks",
479 | );
480 | PRODUCT_BUNDLE_IDENTIFIER = com.swift.from.zero.SwiftUIViews;
481 | PRODUCT_NAME = "$(TARGET_NAME)";
482 | SWIFT_VERSION = 5.0;
483 | TARGETED_DEVICE_FAMILY = "1,2";
484 | };
485 | name = Debug;
486 | };
487 | B34D2ABA25CEE84F000361B0 /* Release */ = {
488 | isa = XCBuildConfiguration;
489 | buildSettings = {
490 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
491 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
492 | CODE_SIGN_STYLE = Automatic;
493 | DEVELOPMENT_ASSET_PATHS = "\"SwiftUIViews/Preview Content\"";
494 | DEVELOPMENT_TEAM = CLLJ89U35Q;
495 | ENABLE_PREVIEWS = YES;
496 | INFOPLIST_FILE = SwiftUIViews/Info.plist;
497 | IPHONEOS_DEPLOYMENT_TARGET = 14.0;
498 | LD_RUNPATH_SEARCH_PATHS = (
499 | "$(inherited)",
500 | "@executable_path/Frameworks",
501 | );
502 | PRODUCT_BUNDLE_IDENTIFIER = com.swift.from.zero.SwiftUIViews;
503 | PRODUCT_NAME = "$(TARGET_NAME)";
504 | SWIFT_VERSION = 5.0;
505 | TARGETED_DEVICE_FAMILY = "1,2";
506 | };
507 | name = Release;
508 | };
509 | /* End XCBuildConfiguration section */
510 |
511 | /* Begin XCConfigurationList section */
512 | B34D2AA425CEE84D000361B0 /* Build configuration list for PBXProject "SwiftUIViews" */ = {
513 | isa = XCConfigurationList;
514 | buildConfigurations = (
515 | B34D2AB625CEE84F000361B0 /* Debug */,
516 | B34D2AB725CEE84F000361B0 /* Release */,
517 | );
518 | defaultConfigurationIsVisible = 0;
519 | defaultConfigurationName = Release;
520 | };
521 | B34D2AB825CEE84F000361B0 /* Build configuration list for PBXNativeTarget "SwiftUIViews" */ = {
522 | isa = XCConfigurationList;
523 | buildConfigurations = (
524 | B34D2AB925CEE84F000361B0 /* Debug */,
525 | B34D2ABA25CEE84F000361B0 /* Release */,
526 | );
527 | defaultConfigurationIsVisible = 0;
528 | defaultConfigurationName = Release;
529 | };
530 | /* End XCConfigurationList section */
531 | };
532 | rootObject = B34D2AA125CEE84D000361B0 /* Project object */;
533 | }
534 |
--------------------------------------------------------------------------------
/SwiftUIViews.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/SwiftUIViews.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/SwiftUIViews/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 |
--------------------------------------------------------------------------------
/SwiftUIViews/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "scale" : "2x",
6 | "size" : "20x20"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "scale" : "3x",
11 | "size" : "20x20"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "scale" : "2x",
16 | "size" : "29x29"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "scale" : "3x",
21 | "size" : "29x29"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "scale" : "2x",
26 | "size" : "40x40"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "scale" : "3x",
31 | "size" : "40x40"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "scale" : "2x",
36 | "size" : "60x60"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "scale" : "3x",
41 | "size" : "60x60"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "scale" : "1x",
46 | "size" : "20x20"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "scale" : "2x",
51 | "size" : "20x20"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "scale" : "1x",
56 | "size" : "29x29"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "scale" : "2x",
61 | "size" : "29x29"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "scale" : "1x",
66 | "size" : "40x40"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "scale" : "2x",
71 | "size" : "40x40"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "scale" : "1x",
76 | "size" : "76x76"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "scale" : "2x",
81 | "size" : "76x76"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "scale" : "2x",
86 | "size" : "83.5x83.5"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "scale" : "1x",
91 | "size" : "1024x1024"
92 | }
93 | ],
94 | "info" : {
95 | "author" : "xcode",
96 | "version" : 1
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/SwiftUIViews/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/SwiftUIViews/ContentView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ContentView.swift
3 | // SwiftUIViews
4 | //
5 | // Created by Zilin Zhu on 2021/2/6.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct ContentView: View {
11 | var body: some View {
12 | NavigationView {
13 | Form {
14 | // ViewBuilder supports only no more than 10 static views in one container.
15 | ControlSection()
16 | LayoutSection()
17 | PaintsSection()
18 | OtherSection()
19 | }
20 | .navigationTitle("SwiftUIViews")
21 | }
22 | }
23 | }
24 |
25 | // MARK: Control
26 | struct ControlSection: View {
27 | var body: some View {
28 | Section(header: Text("Controls")) {
29 | NavigationLink(destination: ControlListOne()) {
30 | Text("Button ... List")
31 | }
32 | NavigationLink(destination: ControlListTwo()) {
33 | Text("NavigationLink ... TabView")
34 | }
35 | NavigationLink(destination: ControlListThree()) {
36 | Text("Text ... Toggle")
37 | }
38 | }
39 | }
40 | }
41 |
42 | struct ControlListOne: View {
43 | var body: some View {
44 | Form {
45 | Section(header: Text("Button")) {
46 | NavigationLink(destination: BasicButton()) {
47 | Text("BasicButton")
48 | }
49 | NavigationLink(destination: ListButton()) {
50 | Text("ListButton")
51 | }
52 | NavigationLink(destination: ContextMenuButton()) {
53 | Text("ContextMenuButton")
54 | }
55 | NavigationLink(destination: StylingButton()) {
56 | Text("StylingButton")
57 | }
58 | }
59 | .textCase(nil)
60 |
61 | Section(header: Text("ColorPicker")) {
62 | NavigationLink(destination: BasicColorPicker()) {
63 | Text("BasicColorPicker")
64 | }
65 | }
66 | .textCase(nil)
67 |
68 | Section(header: Text("DatePicker")) {
69 | NavigationLink(destination: BasicDatePicker()) {
70 | Text("BasicDatePicker")
71 | }
72 | NavigationLink(destination: RangeDatePicker()) {
73 | Text("RangeDatePicker")
74 | }
75 | NavigationLink(destination: StylingDatePicker()) {
76 | Text("StylingDatePicker")
77 | }
78 | }
79 | .textCase(nil)
80 |
81 | Section(header: Text("DisclosureGroup")) {
82 | NavigationLink(destination: BasicDisclosureGroup()) {
83 | Text("BasicDisclosureGroup")
84 | }
85 | }
86 | .textCase(nil)
87 |
88 | Section(header: Text("EditButton")) {
89 | NavigationLink(destination: BasicEditButton()) {
90 | Text("BasicEditButton")
91 | }
92 | NavigationLink(destination: CustomEditButton()) {
93 | Text("CustomEditButton")
94 | }
95 | }
96 | .textCase(nil)
97 |
98 | Section(header: Text("Form")) {
99 | NavigationLink(destination: BasicForm()) {
100 | Text("BasicForm")
101 | }
102 | }
103 | .textCase(nil)
104 |
105 | Section(header: Text("GroupBox")) {
106 | NavigationLink(destination: BasicGroupBox()) {
107 | Text("BasicGroupBox")
108 | }
109 | }
110 | .textCase(nil)
111 |
112 | Section(header: Text("Label")) {
113 | NavigationLink(destination: BasicLabel()) {
114 | Text("BasicLabel")
115 | }
116 | NavigationLink(destination: StylingLabel()) {
117 | Text("StylingLabel")
118 | }
119 | NavigationLink(destination: GroupStylingLabel()) {
120 | Text("GroupStylingLabel")
121 | }
122 | NavigationLink(destination: ProgrammableLabel()) {
123 | Text("ProgrammableLabel")
124 | }
125 | }
126 | .textCase(nil)
127 |
128 | Section(header: Text("Link")) {
129 | NavigationLink(destination: BasicLink()) {
130 | Text("BasicLink")
131 | }
132 | }
133 | .textCase(nil)
134 |
135 | Section(header: Text("List")) {
136 | NavigationLink(destination: BasicList()) {
137 | Text("BasicList")
138 | }
139 | NavigationLink(destination: SelectionList()) {
140 | Text("SelectionList")
141 | }
142 | NavigationLink(destination: HierachicalList()) {
143 | Text("HierachicalList")
144 | }
145 | NavigationLink(destination: StylingList()) {
146 | Text("StylingList")
147 | }
148 | }
149 | .textCase(nil)
150 |
151 | }
152 | .navigationTitle("Button ... List")
153 | }
154 | }
155 |
156 | struct ControlListTwo: View {
157 | var body: some View {
158 | Form {
159 | Section(header: Text("NavigationLink")) {
160 | NavigationLink(destination: BasicNavigationLink()) {
161 | Text("BasicNavigationLink")
162 | }
163 | }
164 | .textCase(nil)
165 |
166 | Section(header: Text("OutlineGroup")) {
167 | NavigationLink(destination: BasicOutlineGroup()) {
168 | Text("BasicOutlineGroup")
169 | }
170 | }
171 | .textCase(nil)
172 |
173 | Section(header: Text("Picker")) {
174 | NavigationLink(destination: BasicPicker()) {
175 | Text("BasicPicker")
176 | }
177 | NavigationLink(destination: IteratingPicker()) {
178 | Text("IteratingPicker")
179 | }
180 | NavigationLink(destination: StylingPicker()) {
181 | Text("StylingPicker")
182 | }
183 | }
184 | .textCase(nil)
185 |
186 | Section(header: Text("ProgressView")) {
187 | NavigationLink(destination: BasicProgressView()) {
188 | Text("BasicProgressView")
189 | }
190 | NavigationLink(destination: StylingProgressView()) {
191 | Text("StylingProgressView")
192 | }
193 | }
194 | .textCase(nil)
195 |
196 | Section(header: Text("ScrollView")) {
197 | NavigationLink(destination: BasicScrollView()) {
198 | Text("BasicScrollView")
199 | }
200 | }
201 | .textCase(nil)
202 |
203 | Section(header: Text("Section")) {
204 | NavigationLink(destination: BasicSection()) {
205 | Text("BasicSection")
206 | }
207 | }
208 | .textCase(nil)
209 |
210 | Section(header: Text("SecureField")) {
211 | NavigationLink(destination: BasicSecureField()) {
212 | Text("BasicSecureField")
213 | }
214 | }
215 | .textCase(nil)
216 |
217 | Section(header: Text("Slider")) {
218 | NavigationLink(destination: BasicSlider()) {
219 | Text("BasicSlider")
220 | }
221 | NavigationLink(destination: StepSlider()) {
222 | Text("StepSlider")
223 | }
224 | }
225 | .textCase(nil)
226 |
227 | Section(header: Text("Stepper")) {
228 | NavigationLink(destination: BasicStepper()) {
229 | Text("BasicStepper")
230 | }
231 | }
232 | .textCase(nil)
233 |
234 | Section(header: Text("TabView")) {
235 | NavigationLink(destination: BasicTabView()) {
236 | Text("BasicTabView")
237 | }
238 | NavigationLink(destination: SelectionTabView()) {
239 | Text("SelectionTabView")
240 | }
241 | }
242 | .textCase(nil)
243 |
244 | }
245 | .navigationTitle("NavigationLink ... TabView")
246 | }
247 | }
248 |
249 | struct ControlListThree: View {
250 | var body: some View {
251 | Form {
252 | Section(header: Text("Text")) {
253 | NavigationLink(destination: BasicText()) {
254 | Text("BasicText")
255 | } }
256 | .textCase(nil)
257 |
258 | Section(header: Text("TextEditor")) {
259 | NavigationLink(destination: BasicTextEditor()) {
260 | Text("BasicTextEditor")
261 | }
262 | }
263 | .textCase(nil)
264 |
265 | Section(header: Text("TextField")) {
266 | NavigationLink(destination: BasicTextField()) {
267 | Text("BasicTextField")
268 | }
269 | }
270 | .textCase(nil)
271 |
272 | Section(header: Text("Toggle")) {
273 | NavigationLink(destination: BasicToggle()) {
274 | Text("BasicToggle")
275 | }
276 | NavigationLink(destination: StylingToggle()) {
277 | Text("StylingToggle")
278 | }
279 | }
280 | .textCase(nil)
281 | }
282 | .navigationTitle("Text ... Toggle")
283 | }
284 | }
285 |
286 | // MARK: Layout
287 | struct LayoutSection: View {
288 | var body: some View {
289 | Section(header: Text("Layout")) {
290 | NavigationLink(
291 | destination: LayoutList(),
292 | label: {
293 | Text("GeometryReader ... ZStack")
294 | })
295 | }
296 | }
297 | }
298 |
299 | struct LayoutList: View {
300 | var body: some View {
301 | Form {
302 | Section(header: Text("GeometryReader")) {
303 | NavigationLink(destination: BasicGeometryReader()) {
304 | Text("BasicGeometryReader")
305 | }
306 | NavigationLink(destination: SafeAreaGeometryReader()) {
307 | Text("SafeAreaGeometryReader")
308 | }
309 | NavigationLink(destination: FrameGeometryReader()) {
310 | Text("FrameGeometryReader")
311 | }
312 | }
313 | .textCase(nil)
314 |
315 | Section(header: Text("HStack")) {
316 | NavigationLink(destination: BasicHStack()) {
317 | Text("BasicHStack")
318 | }
319 | NavigationLink(destination: LayerPriorityBasicHStack()) {
320 | Text("LayerPriorityBasicHStack")
321 | }
322 | }
323 | .textCase(nil)
324 |
325 | Section(header: Text("LazyHGrid")) {
326 | NavigationLink(destination: BasicLazyHGrid()) {
327 | Text("BasicLazyHGrid")
328 | }
329 | NavigationLink(destination: PinnedLazyHGrid()) {
330 | Text("PinnedLazyHGrid")
331 | }
332 | }
333 | .textCase(nil)
334 |
335 | Section(header: Text("LazyHStack")) {
336 | NavigationLink(destination: BasicLazyHStack()) {
337 | Text("BasicLazyHStack")
338 | }
339 | }
340 | .textCase(nil)
341 |
342 | Section(header: Text("LazyVGrid")) {
343 | NavigationLink(destination: BasicLazyVGrid()) {
344 | Text("BasicLazyVGrid")
345 | }
346 | NavigationLink(destination: PinnedLazyVGrid()) {
347 | Text("PinnedLazyVGrid")
348 | }
349 | }
350 | .textCase(nil)
351 |
352 | Section(header: Text("LazyVStack")) {
353 | NavigationLink(destination: BasicLazyVStack()) {
354 | Text("BasicLazyVStack")
355 | }
356 | }
357 | .textCase(nil)
358 |
359 | Section(header: Text("ScrollViewReader")) {
360 | NavigationLink(destination: BasicScrollViewReader()) {
361 | Text("BasicScrollViewReader")
362 | }
363 | }
364 | .textCase(nil)
365 |
366 | Section(header: Text("Spacer")) {
367 | NavigationLink(destination: BasicSpacer()) {
368 | Text("BasicSpacer")
369 | }
370 | }
371 | .textCase(nil)
372 |
373 | Section(header: Text("VStack")) {
374 | NavigationLink(destination: BasicVStack()) {
375 | Text("BasicVStack")
376 | }
377 | NavigationLink(destination: LayerPriorityBasicVStack()) {
378 | Text("LayerPriorityBasicVStack")
379 | }
380 | }
381 | .textCase(nil)
382 |
383 | Section(header: Text("ZStack")) {
384 | NavigationLink(destination: BasicZStack()) {
385 | Text("BasicZStack")
386 | }
387 | NavigationLink(destination: IgnoreSafeZStack()) {
388 | Text("IgnoreSafeZStack")
389 | }
390 | NavigationLink(destination: ZIndexZStack()) {
391 | Text("ZIndexZStack")
392 | }
393 | }
394 | .textCase(nil)
395 | }
396 | .navigationTitle("GeometryReader ... ZStack")
397 | }
398 | }
399 |
400 | // MARK: Paints
401 | struct PaintsSection: View {
402 | var body: some View {
403 | Section(header: Text("Paints")) {
404 | NavigationLink(
405 | destination: PaintsList(),
406 | label: {
407 | Text("Angular ... Radial")
408 | })
409 | }
410 | }
411 | }
412 |
413 | struct PaintsList: View {
414 | var body: some View {
415 | Form {
416 | Section(header: Text("AngularGradient")) {
417 | NavigationLink(destination: BasicAngularGradient()) {
418 | Text("BasicAngularGradient")
419 | }
420 | }
421 | .textCase(nil)
422 |
423 | Section(header: Text("LinearGradient")) {
424 | NavigationLink(destination: BasicLinearGradient()) {
425 | Text("BasicLinearGradient")
426 | }
427 | }
428 | .textCase(nil)
429 |
430 | Section(header: Text("RadialGradient")) {
431 | NavigationLink(destination: BasicRadialGradient()) {
432 | Text("RadialGradient")
433 | }
434 | }
435 | .textCase(nil)
436 | }
437 | .navigationTitle("Angular ... Radial")
438 | }
439 | }
440 |
441 | // MARK: Other
442 | struct OtherSection: View {
443 | var body: some View {
444 | Section(header: Text("Other")) {
445 | NavigationLink(
446 | destination: OtherList(),
447 | label: {
448 | Text("Divider ... Shapes")
449 | })
450 | }
451 | }
452 | }
453 |
454 | struct OtherList: View {
455 | var body: some View {
456 | Form {
457 | Section(header: Text("Divider")) {
458 | NavigationLink(destination: BasicDivider()) {
459 | Text("BasicDivider")
460 | }
461 | }
462 | .textCase(nil)
463 | Section(header: Text("EmptyView")) {
464 | NavigationLink(destination: BasicEmptyView()) {
465 | Text("BasicEmptyView")
466 | }
467 | }
468 | .textCase(nil)
469 | Section(header: Text("Menu")) {
470 | NavigationLink(destination: BasicMenu()) {
471 | Text("BasicMenu")
472 | }
473 | NavigationLink(destination: StylingMenu()) {
474 | Text("StylingMenu")
475 | }
476 | }
477 | .textCase(nil)
478 | Section(header: Text("Shapes")) {
479 | NavigationLink(destination: BasicShapes()) {
480 | Text("BasicShapes")
481 | }
482 | }
483 | .textCase(nil)
484 | }
485 | .navigationTitle("Divider ... Shapes")
486 | }
487 | }
488 |
489 | struct ContentView_Previews: PreviewProvider {
490 | static var previews: some View {
491 | ContentView()
492 | }
493 | }
494 |
--------------------------------------------------------------------------------
/SwiftUIViews/Controls/Button.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Button.swift
3 | // SwiftUIViews
4 | //
5 | // Created by Zilin Zhu on 2021/2/6.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct BasicButton: View {
11 | var body: some View {
12 | VStack {
13 | // Button 需要一个 action 和一个 label,
14 | // 前者的类型是 () -> Void,也就是点击会回调的函数
15 | // 后者是个表示 Button 的样式的视图
16 | Button(action: signIn) {
17 | Text("登录")
18 | }
19 | // 对于视图仅仅是文字的情况,也可以在第一个参数里传入 String
20 | Button("登录", action: signIn)
21 | }
22 | }
23 |
24 | func signIn() { print("已登录") }
25 | }
26 |
27 | struct ListButton: View {
28 | var body: some View {
29 | List {
30 | ForEach(items) { item in
31 | Text(item.title)
32 | }
33 | // 容器中的按钮的样式会随容器的样式变化。
34 | // 例如,如果我们把按钮放在 List 中,
35 | // 按钮的样子就会类似于 List 里面的一个单元
36 | Button("添加", action: addItem)
37 | }
38 | }
39 |
40 | @State var items: [Item] = [Item(title: "A"), Item(title: "B")]
41 |
42 | struct Item: Identifiable {
43 | let id = UUID()
44 | let title: String
45 | }
46 |
47 | func addItem() { items.append(Item(title: "C")) }
48 | }
49 |
50 | struct ContextMenuButton: View {
51 | var body: some View {
52 | Form {
53 | TextField("用户名", text: $username)
54 | }
55 | .contextMenu {
56 | // Button 还可以配合 .contextMenu 使用
57 | // 在这个例子中,如果长按输入框,就会弹出剪切、复制、粘贴三个选项
58 | // 点击就会调用对应的函数
59 | Button("剪切", action: cut)
60 | Button("复制", action: copy)
61 | Button("粘贴", action: paste)
62 | }
63 | }
64 |
65 | @State var username: String = ""
66 | @State var tmp: String = ""
67 |
68 | func cut() {
69 | tmp = username
70 | username = ""
71 | }
72 | func copy() { tmp = username }
73 | func paste() { username += tmp }
74 | }
75 |
76 | struct StylingButton: View {
77 | var body: some View {
78 | HStack {
79 | Button("登录", action: signIn)
80 | Button("注册", action: register)
81 | }
82 | // 可以使用 .buttonStyle 这个 modifier 修改按钮的样式
83 | // 注意 SwiftUI 默认提供的一些样式不一定能够在所有平台上使用
84 | // 如果要自定义按钮样式,需要遵从 PrimitiveButtonStyle 协议
85 | .buttonStyle(PlainButtonStyle())
86 | }
87 |
88 | func signIn() { print("已登录") }
89 | func register() { print("注册新账号") }
90 | }
91 |
92 | struct Button_Previews: PreviewProvider {
93 | static var previews: some View {
94 | Group {
95 | BasicButton()
96 | ListButton()
97 | ContextMenuButton()
98 | StylingButton()
99 | }
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/SwiftUIViews/Controls/ColorPicker.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ColorPicker.swift
3 | // SwiftUIViews
4 | //
5 | // Created by Zilin Zhu on 2021/2/7.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct BasicColorPicker: View {
11 | var body: some View {
12 | VStack {
13 | // 初始化颜色选择器只需要传入一个描述性的 String 和需要绑定的颜色即可
14 | // 点击右侧的圆圈就会弹出丰富的颜色选择框
15 | ColorPicker("选择喜欢的颜色", selection: $bgColor)
16 | // 可以设置关闭透明度的控制
17 | ColorPicker("选择喜欢的颜色", selection: $bgColor, supportsOpacity: false)
18 | // 和 Button 类似,ColorPicker 可以在末尾传入一个视图作为标签视图
19 | ColorPicker(selection: $bgColor) {
20 | Text("选择喜欢的颜色")
21 | }
22 | }
23 | }
24 |
25 | @State var bgColor =
26 | Color(.sRGB, red: 0.98, green: 0.9, blue: 0.2)
27 | }
28 |
29 | struct ColorPicker_Previews: PreviewProvider {
30 | static var previews: some View {
31 | BasicColorPicker()
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/SwiftUIViews/Controls/DatePicker.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DatePicker.swift
3 | // SwiftUIViews
4 | //
5 | // Created by Zilin Zhu on 2021/2/7.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct BasicDatePicker: View {
11 | var body: some View {
12 | VStack {
13 | // DatePicker 的使用非常简单,
14 | // 类似 ColorPicker,只需要绑定上 Date 变量即可
15 | // 在使用时点击右侧的时间(日期),即可进行选择
16 | DatePicker(
17 | "起始日期",
18 | selection: $date,
19 | displayedComponents: [.date]
20 | )
21 | // 可以通过 displayedComponents 控制时间的精细程度
22 | // 可选的值为 DatePickerComponents.hourAndMinute 和
23 | // DatePickerComponents.date
24 | DatePicker(
25 | "起始日期",
26 | selection: $date,
27 | displayedComponents: [.hourAndMinute, .date]
28 | )
29 | }
30 | }
31 |
32 | @State private var date = Date()
33 | }
34 |
35 | struct RangeDatePicker: View {
36 | var body: some View {
37 | // 可以通过 in 参数控制选择的时间范围,本例就只能让用户在 2021 年中进行选择
38 | // 注意传入的时间范围必须是 CloseRange,也就是 ”...”,而不能是 "..<"
39 | DatePicker(
40 | "起始日期",
41 | selection: $date,
42 | in: dateRange,
43 | displayedComponents: [.date]
44 | )
45 | }
46 |
47 | @State private var date = Date()
48 | let dateRange: ClosedRange = {
49 | let calendar = Calendar.current
50 | let startComponents = DateComponents(year: 2021, month: 1, day: 1)
51 | let endComponents = DateComponents(year: 2021, month: 12, day: 31)
52 | return calendar.date(from:startComponents)!
53 | ...
54 | calendar.date(from:endComponents)!
55 | }()
56 | }
57 |
58 | struct StylingDatePicker: View {
59 | var body: some View {
60 | DatePicker(
61 | "起始时间",
62 | selection: $date,
63 | displayedComponents: [.date]
64 | )
65 | // 类似 Button 的 .buttonStyle,我们可以转换日期显示的样式
66 | // 除去这里使用的 GraphicalDatePickerStyle,
67 | // SwiftUI 还给我们提供了好几种有趣的方案,快去试试吧~
68 | .datePickerStyle(GraphicalDatePickerStyle())
69 | }
70 |
71 | @State private var date = Date()
72 | }
73 |
74 | struct DatePicker_Previews: PreviewProvider {
75 | static var previews: some View {
76 | Group {
77 | BasicDatePicker()
78 | RangeDatePicker()
79 | StylingDatePicker()
80 | }
81 |
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/SwiftUIViews/Controls/DisclosureGroup.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DisclosureGroup.swift
3 | // SwiftUIViews
4 | //
5 | // Created by Zilin Zhu on 2021/2/7.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct BasicDisclosureGroup: View {
11 | var body: some View {
12 | // DisclosureGroup 用来分组显示内容,并可以通过 isExpanded 参数
13 | // 来控制内容默认是展开的还是折叠起来的,默认是折叠起来的。
14 | DisclosureGroup("设置", isExpanded: $topExpanded) {
15 | Toggle("选项 1", isOn: $toggleStates.oneIsOn)
16 | Toggle("选项 2", isOn: $toggleStates.twoIsOn)
17 | // 可以嵌套 DisclosureGroup 来实现更复杂的树状结构
18 | DisclosureGroup("详细设置") {
19 | Text("详细设置说明")
20 | }
21 | }
22 | }
23 |
24 | struct ToggleStates {
25 | var oneIsOn: Bool = false
26 | var twoIsOn: Bool = true
27 | }
28 | @State private var toggleStates = ToggleStates()
29 | @State private var topExpanded: Bool = true
30 | }
31 |
32 | struct DisclosureGroup_Previews: PreviewProvider {
33 | static var previews: some View {
34 | BasicDisclosureGroup()
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/SwiftUIViews/Controls/EditButton.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EditButton.swift
3 | // SwiftUIViews
4 | //
5 | // Created by Zilin Zhu on 2021/2/7.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct BasicEditButton: View {
11 | var body: some View {
12 | // EditButton 负责控制打开和关闭编辑模式。
13 | // 当点击开发编辑模式时,SwiftUI 会为我们自动生成符合 iOS 习惯的
14 | // 删除和移动操作 UI(当然如果我们只绑定了删除,就只会显示删除 UI)。
15 | //
16 | // 注意这个编辑模式需要和 NavigationView 相结合
17 | NavigationView{
18 | List {
19 | ForEach(
20 | fruits,
21 | id: \.self
22 | ) { fruit in
23 | Text(fruit)
24 | }
25 | .onDelete { deleteFruit(at :$0) }
26 | .onMove { moveFruit(from: $0, to: $1) }
27 | }
28 | .navigationTitle("水果")
29 | // 绑定 EditButton 的方法就是使用 .toolbar
30 | // 或者 .navigationBarItems
31 | .toolbar { EditButton() }
32 | // .navigationBarItems 的样式略有不同,
33 | // 并且可以控制 EditButton 是在左(leading)还是右(trailing)
34 | // .navigationBarItems(leading: EditButton())
35 | // .navigationBarItems(trailing: EditButton())
36 | }
37 | }
38 |
39 | @State private var fruits = ["苹果", "香蕉", "木瓜", "芒果"]
40 |
41 | func deleteFruit(at offset: IndexSet) {
42 | fruits.remove(atOffsets: offset)
43 | }
44 |
45 | func moveFruit(from source: IndexSet, to destination: Int) {
46 | fruits.move(fromOffsets: source, toOffset: destination)
47 | }
48 | }
49 |
50 | struct CustomEditButton: View {
51 | // 可以通过下面的方法获取当前的 EditMode,并实现更复杂的编辑操作
52 | // 这个例子就是通过改变视图来实现编辑。
53 | @State var isEditMode: EditMode = .inactive
54 |
55 | @State var sampleData = ["苹果", "香蕉", "木瓜", "芒果"]
56 |
57 | var body: some View {
58 | NavigationView {
59 | List(0.. some View {
39 | Label(configuration)
40 | .border(Color.red)
41 | }
42 | }
43 | }
44 |
45 | struct GroupStylingLabel: View {
46 | var body: some View {
47 | VStack {
48 | Label("雨", systemImage: "cloud.rain")
49 | HStack {
50 | Label("雪", systemImage: "snow")
51 | Label("太阳", systemImage: "sun.max")
52 | }
53 | }
54 | // 对于成组的 Label,只需要在最外层使用 .labelStyle 就可以更改组内所有的样式了
55 | .labelStyle(IconOnlyLabelStyle())
56 | }
57 | }
58 |
59 | struct ProgrammableLabel: View {
60 | var body: some View {
61 | // Label 还支持我们自己绘制图标
62 | // 在这里例子里,我们就是画了一个根据用户名自动生成的图标
63 | // 另外,注意到展示的内容也可以是多个视图的组合。
64 | Label {
65 | Text(person.fullName)
66 | .font(.body)
67 | .foregroundColor(.primary)
68 | Text(person.title)
69 | .font(.subheadline)
70 | .foregroundColor(.secondary)
71 | } icon: {
72 | Circle()
73 | .fill(person.profileColor)
74 | .frame(width: 44, height: 44, alignment: .center)
75 | .overlay(Text(person.initials).foregroundColor(.white))
76 | }
77 | }
78 |
79 | struct Person {
80 | let profileColor: Color
81 | let firstName: String
82 | let lastName: String
83 | let title: String
84 |
85 | var fullName: String { "\(firstName) \(lastName)" }
86 | var initials: String { "\(firstName.first!)\(lastName.first!)" }
87 | }
88 |
89 | let person = Person(profileColor: .purple,
90 | firstName: "Zilin", lastName: "Zhu",
91 | title: "SDE")
92 | }
93 |
94 | struct Label_Previews: PreviewProvider {
95 | static var previews: some View {
96 | Group {
97 | BasicLabel()
98 | StylingLabel()
99 | GroupStylingLabel()
100 | ProgrammableLabel()
101 | }
102 |
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/SwiftUIViews/Controls/Link.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Link.swift
3 | // SwiftUIViews
4 | //
5 | // Created by Zilin Zhu on 2021/2/7.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct BasicLink: View {
11 | var body: some View {
12 | VStack {
13 | // 用链接的描述和地址来初始化 Link
14 | Link("zhuzilin 的 github",
15 | destination: URL(string: "https://github.com/zhuzilin")!)
16 | // Link 可以和 Text 一样调整文字的样式
17 | Link("zhuzilin 的 github",
18 | destination: URL(string: "https://github.com/zhuzilin")!)
19 | .font(.headline)
20 | .foregroundColor(.black)
21 | }
22 | }
23 | }
24 |
25 | struct Link_Previews: PreviewProvider {
26 | static var previews: some View {
27 | BasicLink()
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/SwiftUIViews/Controls/List.swift:
--------------------------------------------------------------------------------
1 | //
2 | // List.swift
3 | // SwiftUIViews
4 | //
5 | // Created by Zilin Zhu on 2021/2/7.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct BasicList: View {
11 | var body: some View {
12 | VStack {
13 | // List 就是随平台显示的列表控件,自动支持滑动。
14 | // 最简单的使用方法就是将需要传入的组件传给 List。
15 | List {
16 | Text("低俗小说")
17 | Text("落水狗")
18 | Text("杀死比尔")
19 | }
20 | // 也常常会结合 ForEach 进行列表的显示
21 | List {
22 | ForEach(movies) { movie in
23 | Text(movie.title)
24 | }
25 | }
26 | // 也可以直接把循环量直接传给 List
27 | List(movies) { movie in
28 | Text(movie.title)
29 | }
30 |
31 | }
32 | }
33 | }
34 |
35 | struct SelectionList: View {
36 | var body: some View {
37 | NavigationView {
38 | // List 还支持传入 selection 参数,是用来配合 EditButton
39 | // 使用的。添加了 selection 参数后,点击 Edit 就会在左侧
40 | // 出现选择框了。
41 | List(movies, id: \.self, selection: $selection) { movie in
42 | Text(movie.title)
43 | }
44 | .navigationBarItems(trailing: EditButton())
45 | .navigationBarTitle(Text("Select \(selection.count) movies"))
46 | }
47 | }
48 |
49 | @State var selection = Set()
50 | }
51 |
52 | struct HierachicalList: View {
53 | var body: some View {
54 | // List 还可以像 OutlineGroup 一样显示树状数据
55 | // 这种模式的使用方法请参照 OutlineGroup
56 | List(data, children: \.children) { item in
57 | Text("\(item.description)")
58 | }
59 | }
60 |
61 | // 这里的 FileItem 是在 OutlineGroup 中定义的
62 | let data = [FileItem(name: "user1234", children:
63 | [FileItem(name: "Photos", children:
64 | [FileItem(name: "photo001.jpg"),
65 | FileItem(name: "photo002.jpg")]),
66 | FileItem(name: "Movies", children:
67 | [FileItem(name: "movie001.mp4")]),
68 | FileItem(name: "Documents", children: [])
69 | ]),
70 | FileItem(name: "newuser", children:
71 | [FileItem(name: "Documents", children: [])
72 | ])
73 | ]
74 | }
75 |
76 | struct StylingList: View {
77 | var body: some View {
78 | List {
79 | // 用 Section 来给 List 内的成员分组。
80 | // 当采用了非默认的 List 的样式的情况下,Section
81 | // 的分组会显得更明显
82 | Section {
83 | Text("低俗小说")
84 | Text("落水狗")
85 | }
86 | Section {
87 | Text("杀死比尔")
88 | }
89 | }
90 | // 用 .listStyle 来设置 List 样式
91 | .listStyle(GroupedListStyle())
92 | }
93 | }
94 |
95 | struct Movie: Identifiable, Hashable {
96 | let id = UUID()
97 | let title: String
98 | }
99 |
100 | let movies = [Movie(title: "低俗小说"),
101 | Movie(title: "落水狗"),
102 | Movie(title: "杀死比尔")]
103 |
104 | struct List_Previews: PreviewProvider {
105 | static var previews: some View {
106 | BasicList()
107 | SelectionList()
108 | HierachicalList()
109 | StylingList()
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/SwiftUIViews/Controls/NavigationLink.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Navigation.swift
3 | // SwiftUIViews
4 | //
5 | // Created by Zilin Zhu on 2021/2/7.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct BasicNavigationLink: View {
11 | var body: some View {
12 | NavigationView {
13 | List(0..<3) { i in
14 | // NavigationLink 负责控制导航页的点击跳转
15 | // 其主要有两个参数:destination 是跳转的目的地;
16 | // label 导航栏中对应格子的标签
17 | NavigationLink(
18 | destination: Text("View \(i)")) {
19 | Text("Item \(i)")
20 | }
21 | }
22 | .navigationBarTitle("Navigation")
23 | }
24 | }
25 | }
26 |
27 | struct NavigationLink_Previews: PreviewProvider {
28 | static var previews: some View {
29 | BasicNavigationLink()
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/SwiftUIViews/Controls/NavigationView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NavigationView.swift
3 | // SwiftUIViews
4 | //
5 | // Created by Zilin Zhu on 2021/2/7.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct NavigationView_Previews: PreviewProvider {
11 | static var previews: some View {
12 | // NavigationView 本身没有什么需要配置的,关键在于如何和其内部的视图
13 | // 相配合。这些配合模式分别在其他的文件中介绍了:
14 | //
15 | // 如何在 NavigationView 中使用 NavigationLink
16 | // 详见 NavigationLink.swift
17 | BasicNavigationLink()
18 | // 如何在 NavigationView 中实现编辑模式的插入和移动顺序
19 | // 详见 EditButton.swift
20 | BasicEditButton()
21 | // 如何在 NavigationView 中实现编辑模式的选择
22 | // 详见 NavigationView.swift
23 | SelectionList()
24 | // 注意,嵌套 Navigation 的时候只需要一个 NavigationView,
25 | // 不然会导致在上方出现多个返回的视图。
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/SwiftUIViews/Controls/OutlineGroup.swift:
--------------------------------------------------------------------------------
1 | //
2 | // OutlineGroup.swift
3 | // SwiftUIViews
4 | //
5 | // Created by Zilin Zhu on 2021/2/7.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct BasicOutlineGroup: View {
11 | var body: some View {
12 | // OutlineGroup 是用来显示树形结构的数据的空间。
13 | // 在这个例子中,我们的数据类型是描述的文件,有文件和文件夹的区别
14 | // OutlineGroup 会自动遍历文件夹的所有子文件,从而绘制出嵌套的
15 | // 树形 UI。
16 | //
17 | // 注意,OutlineGroup 和 DisclosureGroup 的区别:OutlineGroup
18 | // 是传入的数据为树形接口,自动生成 UI,而DisclosureGroup 则是我们
19 | // 设计的树形 UI。
20 | OutlineGroup(data, children: \.children) { item in
21 | Text("\(item.description)")
22 | }
23 | }
24 |
25 | let data =
26 | FileItem(name: "users", children:
27 | [FileItem(name: "user1234", children:
28 | [FileItem(name: "Photos", children:
29 | [FileItem(name: "photo001.jpg"),
30 | FileItem(name: "photo002.jpg")]),
31 | FileItem(name: "Movies", children:
32 | [FileItem(name: "movie001.mp4")]),
33 | FileItem(name: "Documents", children: [])
34 | ]),
35 | FileItem(name: "newuser", children:
36 | [FileItem(name: "Documents", children: [])
37 | ])
38 | ])
39 | }
40 |
41 | struct FileItem: Hashable, Identifiable, CustomStringConvertible {
42 | var id: Self { self }
43 | var name: String
44 | var children: [FileItem]? = nil
45 | var description: String {
46 | switch children {
47 | case nil:
48 | return "📄 \(name)"
49 | case .some(let children):
50 | return children.isEmpty ? "📂 \(name)" : "📁 \(name)"
51 | }
52 | }
53 | }
54 |
55 | struct OutlineGroup_Previews: PreviewProvider {
56 | static var previews: some View {
57 | BasicOutlineGroup()
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/SwiftUIViews/Controls/Picker.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Picker.swift
3 | // SwiftUIViews
4 | //
5 | // Created by Zilin Zhu on 2021/2/7.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct BasicPicker: View {
11 | var body: some View {
12 | VStack {
13 | // 在 Picker 中通过 Text 加 .tag 的方式添加选项,
14 | // 并在 selection 输入中绑定用户选择的结果。
15 | Picker("口味", selection: $selectedFlavor) {
16 | Text("巧克力").tag(Flavor.chocolate)
17 | Text("香草").tag(Flavor.vanilla)
18 | Text("草莓").tag(Flavor.strawberry)
19 | }
20 | Text("您选择的是: \(selectedFlavor.value)")
21 | }
22 | }
23 |
24 | @State private var selectedFlavor = Flavor.chocolate
25 | }
26 |
27 | struct IteratingPicker: View {
28 | var body: some View {
29 | VStack {
30 | // Picker 也可以结合 ForEach,直接为某个枚举的所有情况制作选项
31 | //
32 | // 注意,在文档中提到,如果类型匹配的话,ForEach 会自动添加 tag
33 | // 这一点在我的实验中并不起作用...
34 | Picker("请选择一个配料:", selection: $suggestedTopping) {
35 | ForEach(Flavor.allCases) { flavor in
36 | Text(flavor.value)
37 | .tag(flavor.suggestedTopping)
38 | }
39 | }
40 | Text("推荐配料:\(suggestedTopping.value)")
41 | }
42 | }
43 |
44 | @State private var selectedFlavor = Flavor.chocolate
45 | @State var suggestedTopping: Topping = .cookies
46 | }
47 |
48 | struct StylingPicker: View {
49 | @State private var selectedFlavor = Flavor.chocolate
50 | @State private var selectedTopping = Topping.nuts
51 |
52 | var body: some View {
53 | VStack {
54 | Picker("口味", selection: $selectedFlavor) {
55 | ForEach(Flavor.allCases) { flavor in
56 | Text(flavor.value).tag(flavor)
57 | }
58 | }
59 | Picker("配料", selection: $selectedTopping) {
60 | ForEach(Topping.allCases) { flavor in
61 | Text(flavor.value).tag(flavor)
62 | }
63 | }
64 |
65 | Text("您选择的口味是:\(selectedFlavor.value)")
66 | Text("您选择的配料是:\(selectedTopping.value)")
67 | }
68 | // 可以通过 .pickerStyle 来改变 Picker 的样式
69 | .pickerStyle(SegmentedPickerStyle())
70 | }
71 | }
72 |
73 | enum Flavor: String, CaseIterable, Identifiable {
74 | case chocolate
75 | case vanilla
76 | case strawberry
77 |
78 | var id: String { self.rawValue }
79 |
80 | var value: String {
81 | switch self {
82 | case .chocolate:
83 | return "巧克力"
84 | case .vanilla:
85 | return "香草"
86 | case .strawberry:
87 | return "草莓"
88 | }
89 | }
90 |
91 | var suggestedTopping: Topping {
92 | switch self {
93 | case .chocolate: return .nuts
94 | case .vanilla: return .cookies
95 | case .strawberry: return .blueberries
96 | }
97 | }
98 | }
99 |
100 | enum Topping: String, CaseIterable, Identifiable {
101 | case nuts
102 | case cookies
103 | case blueberries
104 |
105 | var id: String { self.rawValue }
106 |
107 | var value: String {
108 | switch self {
109 | case .nuts:
110 | return "坚果"
111 | case .cookies:
112 | return "饼干碎"
113 | case .blueberries:
114 | return "蓝莓"
115 | }
116 | }
117 | }
118 |
119 | struct Picker_Previews: PreviewProvider {
120 | static var previews: some View {
121 | Group {
122 | BasicPicker()
123 | IteratingPicker()
124 | StylingPicker()
125 | }
126 | }
127 | }
128 |
--------------------------------------------------------------------------------
/SwiftUIViews/Controls/ProgressView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ProgressView.swift
3 | // SwiftUIViews
4 | //
5 | // Created by Zilin Zhu on 2021/2/7.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct BasicProgressView: View {
11 | var body: some View {
12 | VStack {
13 | // 显示当前的进度,在这个例子中,每点击一下下面的按钮
14 | // 进度条就会随着 progress 的增加而增加了。
15 | // value 需要的是一个 0 到 1 之间的数
16 | //
17 | // 注意 ProgressView 不会改变传入的值,所以这里传入的不是绑定值
18 | ProgressView(value: progress)
19 | Button("再来", action: { progress += 0.05 })
20 | // 如果不确定进度的话,可以不传入 value,这样就会显示一个
21 | // 空转的进度环了。
22 | ProgressView()
23 | }
24 | }
25 |
26 | @State private var progress = 0.5
27 | }
28 |
29 | struct StylingProgressView: View {
30 | var body: some View {
31 | VStack {
32 | ProgressView(value: 0.25)
33 | ProgressView(value: 0.75)
34 | }
35 | // 可以用 .progressViewStyle 来更改进度条样式
36 | .progressViewStyle(DarkBlueShadowProgressViewStyle())
37 | }
38 |
39 | struct DarkBlueShadowProgressViewStyle: ProgressViewStyle {
40 | func makeBody(configuration: Configuration) -> some View {
41 | ProgressView(configuration)
42 | .shadow(color: Color(red: 0, green: 0, blue: 0.6),
43 | radius: 4.0, x: 1.0, y: 2.0)
44 | }
45 | }
46 | }
47 |
48 | struct ProgressView_Previews: PreviewProvider {
49 | static var previews: some View {
50 | Group {
51 | BasicProgressView()
52 | StylingProgressView()
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/SwiftUIViews/Controls/ScrollView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ScrollView.swift
3 | // SwiftUIViews
4 | //
5 | // Created by Zilin Zhu on 2021/2/7.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct BasicScrollView: View {
11 | var body: some View {
12 | // ScrollView 用来把视图变得可以按指定方向滚动
13 | // 初始化的参数中:
14 | // 第一个值是用来指定滚动方向的,默认为 .vertical,
15 | // 第二个 showIndicators 控制是否显示滚动时的光标,默认是开启的
16 | //
17 | // 注意,普通的 VStack 是不能滚动的,可以把
18 | // ScrollView 换成 VStack 试一下
19 | ScrollView([.vertical, .horizontal],
20 | showsIndicators: true) {
21 | Text("起始")
22 | HStack {
23 | Text("左")
24 | Rectangle()
25 | .fill(Color.blue)
26 | .frame(width: 1000, height: 1500)
27 | Text("右")
28 | }
29 | Text("截止")
30 | }
31 | }
32 | }
33 |
34 | struct ScrollView_Previews: PreviewProvider {
35 | static var previews: some View {
36 | BasicScrollView()
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/SwiftUIViews/Controls/Section.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Section.swift
3 | // SwiftUIViews
4 | //
5 | // Created by Zilin Zhu on 2021/2/7.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct BasicSection: View {
11 | var body: some View {
12 | List {
13 | // Section 主要用来在 List、Form 这样的控件中把列表
14 | // 划分成小组,所以可以通过查看 List 和 Form 的对应例子
15 | // 查看 Section 的用法和效果
16 | // 这里主要补充一些 Section 的其他用法
17 | Section {
18 | Text("低俗小说")
19 | Text("落水狗")
20 | // 嵌套的 Section 不会有什么效果
21 | Section {
22 | Text("杀死比尔 1")
23 | Text("杀死比尔 2")
24 | }
25 | }
26 | // 还可以通过 header 与 footer 参数来设置标题和尾注
27 | Section(header: Label("其他电影", systemImage: "film"),
28 | footer: Text("尾注")) {
29 | Text("被解救的姜戈")
30 | }
31 | }
32 | .listStyle(GroupedListStyle())
33 | }
34 | }
35 |
36 | struct Section_Previews: PreviewProvider {
37 | static var previews: some View {
38 | BasicSection()
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/SwiftUIViews/Controls/SecureField.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SecureField.swift
3 | // SwiftUIViews
4 | //
5 | // Created by Zilin Zhu on 2021/2/7.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct BasicSecureField: View {
11 | var body: some View {
12 | VStack {
13 | TextField("用户名", text: $username)
14 | .autocapitalization(.none)
15 | .disableAutocorrection(true)
16 | .border(Color(UIColor.separator))
17 | // SecureField 类似 TextField,只是不明文显示输入内容,类似密码输入
18 | // 创建的方式是传入一个描述的 String,再绑定一个用户输入的 String
19 | // SecureField 也支持绑定一个 onCommit 的回调函数,可以在用户
20 | // 完成输入的时候(例如点击回车)就自动调用这个函数
21 | SecureField("密码", text: $password) {
22 | handleLogin(username: username, password: password)
23 | }
24 | .border(Color(UIColor.separator))
25 | }
26 | }
27 |
28 | @State private var username: String = ""
29 | @State private var password: String = ""
30 |
31 | func handleLogin(username: String, password: String) {
32 | print("user \(username) login.")
33 | }
34 | }
35 |
36 | struct SecureField_Previews: PreviewProvider {
37 | static var previews: some View {
38 | BasicSecureField()
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/SwiftUIViews/Controls/Slider.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Slider.swift
3 | // SwiftUIViews
4 | //
5 | // Created by Zilin Zhu on 2021/2/7.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct BasicSlider: View {
11 | var body: some View {
12 | VStack {
13 | // Slider 用于让用户在一个连续的范围中取值。
14 | // 构造 Slider 需要一个绑定的值和一个范围(需要是 CloseRange,
15 | // 也就是"...")
16 | //
17 | // 注意,Slider 的构造函数其实还支持传入一个 label 参数
18 | // 这个 label 参数在很多的样式中不会显示,但是 VoiceOver
19 | // 之类的功能会用这个 Label 来识别该 Slider
20 | Slider(value: $speed1, in: 0...100)
21 | Text("\(speed1)")
22 | // 还支持传入一个 onEditingChanged 参数,这个回调函数会在
23 | // 编辑开始和编辑结束的时候被调用
24 | Slider(value: $speed2, in: 0...100,
25 | onEditingChanged: { editing in
26 | isEditing = editing
27 | })
28 | Text("\(speed2)")
29 | .foregroundColor(isEditing ? .red : .blue)
30 | // 还可以用 minimumValueLabel 和 maximumValueLabel
31 | // 来显示 Slider 的最大值和最小值
32 | Slider(value: $speed3, in: 0...100,
33 | minimumValueLabel: Text("0"),
34 | maximumValueLabel: Text("100")
35 | ) {
36 | // 注意,显示最大值和最小值的情况下,必须要传入结尾的 label 参数
37 | Text("Speed")
38 | }
39 | }
40 | }
41 |
42 | @State private var speed1 = 50.0
43 | @State private var speed2 = 50.0
44 | @State private var speed3 = 50.0
45 | @State private var isEditing = false
46 | }
47 |
48 | struct StepSlider: View {
49 | var body: some View {
50 | VStack {
51 | // 还可以用 step 参数来控制滑动增减的单位量,
52 | // 这样就可以控制用户输入为离散的值了。
53 | // 例如这个例子就是控制拖动的大小为 5 或者 5 的倍数
54 | Slider(value: $speed, in: 0...100, step: 5)
55 | Text("\(speed)")
56 | }
57 | }
58 |
59 | @State private var speed = 50.0
60 | }
61 |
62 | struct Slider_Previews: PreviewProvider {
63 | static var previews: some View {
64 | Group {
65 | BasicSlider()
66 | StepSlider()
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/SwiftUIViews/Controls/Stepper.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Stepper.swift
3 | // SwiftUIViews
4 | //
5 | // Created by Zilin Zhu on 2021/2/7.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct BasicStepper: View {
11 | var body: some View {
12 | VStack {
13 | // Stepper 让用户可以对数量进行加减
14 | // 构造 Stepper 的时候需要绑定 onIncrement 和 onDecrement
15 | // Stepper 和 Slider 一样,恶友 onEditingChanged 参数,就不在此展示了
16 | Stepper(onIncrement: incrementStep,
17 | onDecrement: decrementStep) {
18 | Text("值: \(value) 颜色: \(colors[value].description)")
19 | }
20 | .padding(5)
21 | .background(colors[value])
22 | // 注意 onIncrement 和 onDecrement 可以为 nil,如果为 nil 就
23 | // 相当于不能进行加或减操作
24 | Stepper(onIncrement: incrementStep,
25 | onDecrement: nil) {
26 | Text("值: \(value) 颜色: \(colors[value].description)")
27 | }
28 | .padding(5)
29 | // Stepper 也可以设置 in 和 step,用于控制用户输入的范围和每一次增加的量
30 | // 注意,in 需要是 CloseRange,也就是"..."
31 | Stepper(value: $value2,
32 | in: 1...50,
33 | step: 5) {
34 | Text("值2: \(value2)")
35 | }
36 | .padding(10)
37 | }
38 | }
39 |
40 | @State private var value = 3
41 | @State private var value2 = 0
42 | let colors: [Color] = [.orange, .red, .gray, .blue,
43 | .green, .purple, .pink]
44 |
45 | func incrementStep() { value = (value + 1) % colors.count }
46 |
47 | func decrementStep() {
48 | value = (value + colors.count - 1) % colors.count
49 | }
50 | }
51 |
52 | struct Stepper_Previews: PreviewProvider {
53 | static var previews: some View {
54 | BasicStepper()
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/SwiftUIViews/Controls/TabView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TabView.swift
3 | // SwiftUIViews
4 | //
5 | // Created by Zilin Zhu on 2021/2/7.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct BasicTabView: View {
11 | var body: some View {
12 | // TabView 提供了适合平台的标签页切换 UI
13 | // 其中的每个子视图就是 TabView 的便签页的内容
14 | TabView {
15 | Text("第一个标签页")
16 | // .tabItem 是每个标签页的图标。
17 | // 不附上 .tabItem 的话,图标会为空,但是点击对应位置仍然可以实现跳转
18 | .tabItem {
19 | Image(systemName: "1.square.fill")
20 | Text("第一")
21 | }
22 | Text("又一个标签页")
23 | .tabItem {
24 | Image(systemName: "2.square.fill")
25 | Text("第二")
26 | }
27 | Text("最后一个标签页")
28 | .tabItem {
29 | Image(systemName: "3.square.fill")
30 | Text("第三")
31 | }
32 | }
33 | .font(.headline)
34 | }
35 | }
36 |
37 | struct SelectionTabView: View {
38 | var body: some View {
39 | VStack {
40 | // TabView 可以用和 Picker 类似的方式获取当前在哪个标签页
41 | Text("目前在第 \(selection + 1) 个标签页")
42 | TabView(selection: $selection) {
43 | Text("第一个标签页")
44 | .tabItem {
45 | Image(systemName: "1.square.fill")
46 | Text("第一")
47 | }
48 | // 获取的前提是给每个 tablItem 都加上 tag
49 | .tag(0)
50 | Text("又一个标签页")
51 | .tabItem {
52 | Image(systemName: "2.square.fill")
53 | Text("第二")
54 | }
55 | .tag(1)
56 | Text("最后一个标签页")
57 | .tabItem {
58 | Image(systemName: "3.square.fill")
59 | Text("第三")
60 | }
61 | .tag(2)
62 | }
63 | .font(.headline)
64 | }
65 | }
66 |
67 | @State var selection: Int = 0
68 | }
69 |
70 | struct TabView_Previews: PreviewProvider {
71 | static var previews: some View {
72 | Group {
73 | BasicTabView()
74 | SelectionTabView()
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/SwiftUIViews/Controls/Text.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Text.swift
3 | // SwiftUIViews
4 | //
5 | // Created by Zilin Zhu on 2021/2/7.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct BasicText: View {
11 | var body: some View {
12 | VStack {
13 | // Text 用来展示一段文字
14 | Text("Selected 名著精选")
15 | // 可以用类似 .title, .caption 这样的字号设置字体
16 | // SwiftUI 会自动为你匹配对应平台的合适字号
17 | Text("Hamlet 哈姆雷特")
18 | .font(.title)
19 | // 如果需要进行细致的调试,可以设置字体,字号,是否有衬线等等。
20 | // 也可以利用 .bold 或 .italic 进行加粗或使用斜体
21 | // 注意目前衬线功能对中文无效
22 | Text("by William Shakespeare 莎士比亚著")
23 | .font(.system(size: 12, weight: .light, design: .serif))
24 | .italic()
25 | // Text 会占据显示其内容所必需的空间。不过你可以调整内容的布局。
26 | // 例如,你可以用 .frame 来控制视图的某一个维度,Text 会自适应的调整布局
27 | Text("To be, or not to be, that is the question:")
28 | .frame(width: 100)
29 | // 你还可以用 .lineLimit, .allowsTightening, .minimumScaleFactor,
30 | // .trancationMode 等 modifier 控制显示的内容
31 | Text("Brevity is the soul of wit.")
32 | .frame(width: 100)
33 | .lineLimit(1)
34 | // 可以这样使用非系统默认的字体
35 | // 在 UIFont.familyNames 中可以查看所有可以使用的字体
36 | Text("Text(\"Hello World!\")")
37 | .font(.custom("Menlo", size: 17))
38 | }
39 |
40 | }
41 | }
42 |
43 | struct Text_Previews: PreviewProvider {
44 | static var previews: some View {
45 | BasicText()
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/SwiftUIViews/Controls/TextEditor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TextEditor.swift
3 | // SwiftUIViews
4 | //
5 | // Created by Zilin Zhu on 2021/2/7.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct BasicTextEditor: View {
11 | var body: some View {
12 | VStack {
13 | Spacer()
14 | // TextEditor 是一个多行、可滚动的文本编辑 UI,通过传入文字的绑定
15 | // 来获取用户输入或修改的内容。
16 | //
17 | // 注意,TextEditor 默认会使用流海部分,在排版的时候需要注意
18 | // (可以去掉上面的 Spacer 尝试一下)
19 | TextEditor(text: $fullText)
20 | // 可以像修饰 Text 一样改变 TextEditor 的样式
21 | TextEditor(text: $fullText)
22 | .foregroundColor(Color.gray)
23 | .font(.custom("HelveticaNeue", size: 13))
24 | // 可以用 .lineLimit, .lineSpacing, .minimumScaleFactor 等
25 | // modifier 修改行间距或字间距
26 | TextEditor(text: $fullText)
27 | .foregroundColor(Color.gray)
28 | .font(.custom("HelveticaNeue", size: 13))
29 | .lineSpacing(10)
30 | }
31 | }
32 |
33 | @State private var fullText: String =
34 | "这里有些可编辑的文字...\n第二行也有一些..."
35 | }
36 |
37 | struct TextEditor_Previews: PreviewProvider {
38 | static var previews: some View {
39 | BasicTextEditor()
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/SwiftUIViews/Controls/TextField.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TextField.swift
3 | // SwiftUIViews
4 | //
5 | // Created by Zilin Zhu on 2021/2/7.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct BasicTextField: View {
11 | var body: some View {
12 | VStack {
13 | // TextField 负责单行的文本输入。除去传入描述信息和绑定的字符串
14 | // text 之外,还可以传入 onEditingChanged 和 onCommit 这
15 | // 两个回调函数,前者会在用户开始编辑和结束编辑的时候被调用,后者
16 | // 则是会在用户点击回车的时候被调用。
17 | TextField("用户名", text: $username,
18 | onEditingChanged: { isEditing in
19 | self.isEditing = isEditing
20 | }, onCommit: {
21 | validate(name: username)
22 | })
23 | .autocapitalization(.none)
24 | .disableAutocorrection(true)
25 | .border(Color(UIColor.separator))
26 | Text(username)
27 | .foregroundColor(isEditing ? .red : .blue)
28 | }
29 | }
30 |
31 | @State private var username: String = ""
32 | @State private var isEditing = false
33 |
34 | func validate(name: String) { print("校验用户名") }
35 | }
36 |
37 | struct FormatTextField: View {
38 | var body: some View {
39 | VStack {
40 | // TextField 不止支持绑定 String。
41 | // 利用 formatter 参数,TextField 就可以用来绑定值了
42 | TextField("您的名字", value: $nameComponents,
43 | formatter: nameFormatter,
44 | onCommit: {
45 | validate(components: nameComponents)
46 | })
47 | .disableAutocorrection(true)
48 | .border(Color(UIColor.separator))
49 | Text(nameComponents.debugDescription)
50 | }
51 | }
52 |
53 | let nameFormatter = PersonNameComponentsFormatter()
54 | @State private var nameComponents = PersonNameComponents()
55 |
56 | func validate(components: PersonNameComponents) {
57 | print("校验名字")
58 | }
59 | }
60 |
61 | struct StylingTextField: View {
62 | var body: some View {
63 | VStack {
64 | TextField(
65 | "名",
66 | text: $givenName)
67 | .disableAutocorrection(true)
68 | TextField(
69 | "姓",
70 | text: $familyName)
71 | .disableAutocorrection(true)
72 | }
73 | // 可以用 .textFieldStyle 来改变 TextField 的样式
74 | // 这里我们把 TextField 的边框改为圆角了
75 | .textFieldStyle(RoundedBorderTextFieldStyle())
76 | }
77 |
78 | @State private var givenName: String = ""
79 | @State private var familyName: String = ""
80 | }
81 |
82 | struct TextField_Previews: PreviewProvider {
83 | static var previews: some View {
84 | Group {
85 | BasicTextField()
86 | FormatTextField()
87 | StylingTextField()
88 | }
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/SwiftUIViews/Controls/Toggle.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Toggle.swift
3 | // SwiftUIViews
4 | //
5 | // Created by Zilin Zhu on 2021/2/7.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct BasicToggle: View {
11 | var body: some View {
12 | // Toggle 需要绑定一个 Bool 用来记录其状态是开还是关
13 | Toggle(isOn: $vibrateOnRing) {
14 | Text("来电振动")
15 | }
16 | }
17 |
18 | @State private var vibrateOnRing = false
19 | }
20 |
21 | struct StylingToggle: View {
22 | var body: some View {
23 | VStack {
24 | Toggle("来电振动", isOn: $vibrateOnRing)
25 | Toggle("静音", isOn: $silentOnRing)
26 | }
27 | // 可以通过 .toggleStyle 改变 Toggle 样式
28 | // 注意,SwiftUI 提供的样式中,目前只有 SwitchToggleStyle 可以
29 | // 在 iOS 上使用,该样式和默认样式相同。
30 | .toggleStyle(SwitchToggleStyle())
31 | }
32 |
33 | @State private var vibrateOnRing = false
34 | @State private var silentOnRing = true
35 | }
36 |
37 | struct Toggle_Previews: PreviewProvider {
38 | static var previews: some View {
39 | Group {
40 | BasicToggle()
41 | StylingToggle()
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/SwiftUIViews/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UIApplicationSceneManifest
24 |
25 | UIApplicationSupportsMultipleScenes
26 |
27 |
28 | UIApplicationSupportsIndirectInputEvents
29 |
30 | UILaunchScreen
31 |
32 | UIRequiredDeviceCapabilities
33 |
34 | armv7
35 |
36 | UISupportedInterfaceOrientations
37 |
38 | UIInterfaceOrientationPortrait
39 | UIInterfaceOrientationLandscapeLeft
40 | UIInterfaceOrientationLandscapeRight
41 |
42 | UISupportedInterfaceOrientations~ipad
43 |
44 | UIInterfaceOrientationPortrait
45 | UIInterfaceOrientationPortraitUpsideDown
46 | UIInterfaceOrientationLandscapeLeft
47 | UIInterfaceOrientationLandscapeRight
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/SwiftUIViews/Layout/GeometryReader.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GeometryReader.swift
3 | // SwiftUIViews
4 | //
5 | // Created by Zilin Zhu on 2021/2/19.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct BasicGeometryReader: View {
11 | var body: some View {
12 | Rectangle()
13 | .stroke(lineWidth: 5)
14 | .frame(width: 200, height: 200, alignment: .center)
15 | .overlay(content)
16 | }
17 |
18 | var content: some View {
19 | // GeometryReader 用于获取父视图的大小和位置,
20 | // 从而让子视图可以随父视图的大小变化而灵活变化其形态和内容
21 | // 它的输入类型为 (GeometryProxy) -> Content
22 | // 这里的 GeometryProxy(也就是这里的 geometry)
23 | // 有 2 个属性和 1 个方法
24 | // 最常用的是 size: CGSzie,表示父视图的大小
25 | GeometryReader { geometry in
26 | Rectangle()
27 | .foregroundColor(.blue)
28 | .frame(width: geometry.size.width / 2, height: geometry.size.height / 2)
29 | .offset(x: geometry.size.width / 4,
30 | y: geometry.size.height / 4)
31 | }
32 | }
33 | }
34 |
35 | struct SafeAreaGeometryReader: View {
36 | var body: some View {
37 | Color.white.ignoresSafeArea()
38 | .overlay(content)
39 | }
40 |
41 | var content: some View {
42 | // GeometryProxy 的另一个属性是 safeAreaInsets,
43 | // 用来表示当前不可用的空间有多少。
44 | // 例如 safeAreaInsets.top 表示了上方刘海部分加上导航栏的高度
45 | // safeAreaInsets.bottom 表示下方圆角部分的高度
46 | GeometryReader { geometry in
47 | List {
48 | Text("在模拟器中翻转屏幕,来观察 4 条边上 safeArea 的数值变化").bold()
49 | Text("safeAreaInsets.top: \(geometry.safeAreaInsets.top)")
50 | Text("safeAreaInsets.bottom: \(geometry.safeAreaInsets.bottom)")
51 | Text("safeAreaInsets.leading: \(geometry.safeAreaInsets.leading)")
52 | Text("safeAreaInsets.trailing: \(geometry.safeAreaInsets.trailing)")
53 | }
54 | }
55 | }
56 | }
57 |
58 | struct FrameGeometryReader: View {
59 | var body: some View {
60 | VStack {
61 | Rectangle()
62 | .stroke(lineWidth: 5)
63 | .frame(width: 150, height: 150)
64 | .overlay(content)
65 | Spacer()
66 | }
67 | }
68 |
69 | var content: some View {
70 | // 除了上述的两个属性,GeometryProxy 还有 frame 这个方法
71 | // 这个方法会返回一个 CGRect,可以用来获取父视图相对某一坐标的位置
72 | // 例子中 .local 获得的就是相对父视图的坐标系,
73 | // 所以 x, y 都是 0,红色方块和黑框重叠
74 | // .global 就是父视图相对整体的位置,所以蓝方块也向右下进行了平移
75 | GeometryReader { geometry in
76 | ZStack {
77 | Rectangle()
78 | .path(in: geometry.frame(in: .local))
79 | .foregroundColor(.red)
80 | Rectangle()
81 | .path(in: geometry.frame(in: .global))
82 | .foregroundColor(.blue)
83 | }
84 | }
85 | }
86 | }
87 |
88 | struct BasicGeometryReader_Previews: PreviewProvider {
89 | static var previews: some View {
90 | Group {
91 | BasicGeometryReader()
92 | SafeAreaGeometryReader()
93 | FrameGeometryReader()
94 | }
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/SwiftUIViews/Layout/HStack.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HStack.swift
3 | // SwiftUIViews
4 | //
5 | // Created by Zilin Zhu on 2021/2/19.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct BasicHStack: View {
11 | var body: some View {
12 | // HStack 用于水平摆放视图。和 LazyHStack 不同的是 HStack 会直接
13 | // 渲染内部的所有视图。请在内容较少的时候才使用 HStack
14 | //
15 | // alignment 用于进行对齐,这里因为所有都是 Text 所以效果不明显
16 | // spacing 用于指定元素间隔
17 | HStack(alignment: .top, spacing: 10) {
18 | ForEach(1...5, id: \.self) {
19 | Text("\($0) 号选手")
20 | }
21 | }
22 | }
23 | }
24 |
25 | struct LayerPriorityBasicHStack: View {
26 | var body: some View {
27 | // 我们可以使用 .layoutPriority 来控制 HStack 中元素的布局优先级。
28 | // 优先级越高的视图会在空间不足的情况下尽量被满足。
29 | // 数字越高代表优先级越高,默认优先级为 0。
30 | HStack {
31 | Text("这个字符串为默认优先级。")
32 | .font(.largeTitle)
33 | .border(Color.gray)
34 |
35 | Spacer()
36 |
37 | Text("这个字符串的优先级更高。")
38 | .font(.largeTitle)
39 | // 注释这行试试?
40 | .layoutPriority(1)
41 | .border(Color.gray)
42 | }
43 | }
44 | }
45 |
46 | struct HStack_Previews: PreviewProvider {
47 | static var previews: some View {
48 | Group {
49 | BasicHStack()
50 | LayerPriorityBasicHStack()
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/SwiftUIViews/Layout/LazyHGrid.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LazyHGrid.swift
3 | // SwiftUIViews
4 | //
5 | // Created by Zilin Zhu on 2021/2/22.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct BasicLazyHGrid: View {
11 | var body: some View {
12 | ScrollView(.horizontal, showsIndicators: true) {
13 | // LazyHGrid 用于进行更为自由的横向栅格布局。
14 | // Lazy 意味着子视图会在即将出现在屏幕上的时候再被渲染。
15 | // Grid 的布局主要通过传入一个 [GridItem] 类型的 rows 参数控制,
16 | // GridItem 的种类在下面的注释中有讲解。
17 | //
18 | // 尝试在模拟器中转动屏幕,横屏和竖屏时有什么区别
19 | LazyHGrid(rows: rows) {
20 | ForEach(0..<100) { i in
21 | ColorRect(i: i)
22 | .frame(width: 80)
23 | }
24 | }
25 | }
26 | }
27 |
28 | // GridItem 用来控制具体的横向布局方式,具体来说,是每一行的高度是多少(因为宽度可以无限延伸)
29 | // 目前有 3 种 GridItem,fixed, flexible 和 .adaptive
30 | // - fixed: 表示这行的高度固定为传入的参数
31 | // - flexible: 一个在一定范围内灵活地取一个高度,默认的最小值为 10,最大值为 .infinity
32 | // - adaptive: 类似于 flexible,用 adaptive 表示的行的高度可以在指定的范围内灵活地变动。区别在于
33 | // 当 adaptive 会尽可能地表示为多行,从而把空间占满,而 flexible 只会表示一行。
34 | // 在进行布局的时候,会先防止 fixed 类型的行,然后将剩余空间平分给 flexible 和 adaptive,adaptive
35 | // 会根据空间转为多行。
36 | // 当空间不足的时候,会优先摆放 fixed,其次为 flexible,再其次为 adaptive。
37 | let rows: [GridItem] = [GridItem(.fixed(150)),
38 | GridItem(.flexible(minimum: 150, maximum: 200)),
39 | GridItem(.adaptive(minimum: 80, maximum: 120))]
40 | }
41 |
42 | struct PinnedLazyHGrid: View {
43 | var body: some View {
44 | ScrollView(.horizontal, showsIndicators: true) {
45 | // pinnedViews 可以让头注或脚注在滚动过程中保持在其位置上
46 | // spacing 用于控制列间距
47 | LazyHGrid(rows: rows, spacing: 40, pinnedViews: [.sectionHeaders]) {
48 | Section(header: Text("头注")) {
49 | ForEach(0..<20) { i in
50 | ColorRect(i: i)
51 | .frame(width: 80)
52 | }
53 | }
54 | }
55 | }
56 | }
57 | // 用 GridItem 中的 spacing 参数控制行间距
58 | let rows: [GridItem] = [GridItem(.fixed(200), spacing: 0),
59 | GridItem(.fixed(200), spacing: 10),
60 | GridItem(.fixed(200), spacing: 0)]
61 | }
62 |
63 | let colors: [Color] = [.red, .orange, .yellow, .green, .blue, .purple]
64 | struct ColorRect: View {
65 | let i: Int
66 | var horizontal: Bool = true
67 | var body: some View {
68 | RoundedRectangle(cornerRadius: 10)
69 | .foregroundColor(colors[i % colors.count])
70 | .overlay(GeometryReader { geometry in
71 | ZStack {
72 | VStack {
73 | Text("No. \(i)")
74 | if horizontal {
75 | Text("h: \(Int(geometry.size.height))")
76 | } else {
77 | Text("w: \(Int(geometry.size.width))")
78 | }
79 | }
80 | }
81 | .frame(width: geometry.size.width,
82 | height: geometry.size.height)
83 | })
84 | }
85 | }
86 |
87 | struct LazyHGrid_Previews: PreviewProvider {
88 | static var previews: some View {
89 | Group {
90 | BasicLazyHGrid()
91 | PinnedLazyHGrid()
92 | }
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/SwiftUIViews/Layout/LazyHStack.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LazyHStack.swift
3 | // SwiftUIViews
4 | //
5 | // Created by Zilin Zhu on 2021/2/19.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct BasicLazyHStack: View {
11 | var body: some View {
12 | ScrollView(.horizontal) {
13 | // LazyHStack 的用法和 HStack 相同,仅有的区别是它会“惰性初始化”
14 | // 也就是只有在子视图即将出现在屏幕上的时候,再初始化子视图
15 | LazyHStack(alignment: .top, spacing: 10) {
16 | ForEach(1...100, id: \.self) {
17 | Text("列 \($0)").font(.title)
18 | }
19 | }
20 | }
21 | }
22 | }
23 |
24 | struct LazyHStack_Previews: PreviewProvider {
25 | static var previews: some View {
26 | BasicLazyHStack()
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/SwiftUIViews/Layout/LazyVGrid.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LazyVGrid.swift
3 | // SwiftUIViews
4 | //
5 | // Created by Zilin Zhu on 2021/2/22.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct BasicLazyVGrid: View {
11 | var body: some View {
12 | ScrollView(.vertical, showsIndicators: true) {
13 | // LazyVGrid 用于进行更为自由的横向栅格布局。
14 | // Lazy 意味着子视图会在即将出现在屏幕上的时候再被渲染。
15 | // Grid 的布局主要通过传入一个 [GridItem] 类型的 columns 参数控制,
16 | // GridItem 的种类在下面的注释中有讲解。
17 | //
18 | // 尝试在模拟器中转动屏幕,横屏和竖屏时有什么区别
19 | LazyVGrid(columns: columns) {
20 | ForEach(0..<100) { i in
21 | ColorRect(i: i, horizontal: false)
22 | .frame(height: 100)
23 | }
24 | }
25 | }
26 | }
27 |
28 | // GridItem 用来控制具体的横向布局方式,具体来说,是每一行的高度是多少(因为宽度可以无限延伸)
29 | // 目前有 3 种 GridItem,fixed, flexible 和 .adaptive
30 | // - fixed: 表示这行的高度固定为传入的参数
31 | // - flexible: 一个在一定范围内灵活地取一个高度,默认的最小值为 10,最大值为 .infinity
32 | // - adaptive: 类似于 flexible,用 adaptive 表示的行的高度可以在指定的范围内灵活地变动。区别在于
33 | // 当 adaptive 会尽可能地表示为多行,从而把空间占满,而 flexible 只会表示一行。
34 | // 在进行布局的时候,会先防止 fixed 类型的行,然后将剩余空间平分给 flexible 和 adaptive,adaptive
35 | // 会根据空间转为多行。
36 | // 当空间不足的时候,会优先摆放 fixed,其次为 flexible,再其次为 adaptive。
37 | let columns: [GridItem] = [GridItem(.fixed(50)),
38 | GridItem(.flexible(minimum: 50, maximum: 120)),
39 | GridItem(.adaptive(minimum: 60, maximum: 80))]
40 | }
41 |
42 | struct PinnedLazyVGrid: View {
43 | var body: some View {
44 | ScrollView(.vertical, showsIndicators: true) {
45 | // pinnedViews 可以让头注或脚注在滚动过程中保持在其位置上
46 | // spacing 用于控制行间距
47 | LazyVGrid(columns: columns, spacing: 40,
48 | pinnedViews: [.sectionHeaders]) {
49 | Section(header: Text("头注")) {
50 | ForEach(0..<30) { i in
51 | ColorRect(i: i, horizontal: false)
52 | .frame(height: 100)
53 | }
54 | }
55 | }
56 | }
57 | }
58 | // 用 GridItem 中的 spacing 参数控制列间距
59 | let columns: [GridItem] = [GridItem(.fixed(80), spacing: 0),
60 | GridItem(.fixed(80), spacing: 10),
61 | GridItem(.fixed(80), spacing: 0)]
62 | }
63 |
64 |
65 | struct LazyVGrid_Previews: PreviewProvider {
66 | static var previews: some View {
67 | Group {
68 | BasicLazyVGrid()
69 | PinnedLazyVGrid()
70 | }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/SwiftUIViews/Layout/LazyVStack.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LazyVStack.swift
3 | // SwiftUIViews
4 | //
5 | // Created by Zilin Zhu on 2021/2/19.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct BasicLazyVStack: View {
11 | var body: some View {
12 | ScrollView {
13 | // LazyVStack 的用法和 VStack 相同,仅有的区别是它会“惰性初始化”
14 | // 也就是只有在子视图即将出现在屏幕上的时候,再初始化子视图
15 | LazyVStack(alignment: .leading, spacing: 5) {
16 | ForEach(1...100, id: \.self) {
17 | Text("行 \($0)").font(.title)
18 | }
19 | }
20 | }
21 | }
22 | }
23 |
24 | struct LazyVStack_Previews: PreviewProvider {
25 | static var previews: some View {
26 | BasicLazyVStack()
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/SwiftUIViews/Layout/ScrollViewReader.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ScrollViewReader.swift
3 | // SwiftUIViews
4 | //
5 | // Created by Zilin Zhu on 2021/2/19.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct BasicScrollViewReader: View {
11 | var body: some View {
12 | // ScrollViewReader 用于实现 ScrollView 中的快速跳转
13 | // 它的回调函数会提供一个 ScrollViewProxy 类型的参数,也就是例子中的 scrollView
14 | // 这个参数有一个 scrollTo 函数,可以通过锚点的值跳转到对应的位置
15 | ScrollViewReader { scrollView in
16 | HStack {
17 | Text("跳转到:")
18 | ForEach(0..<10) { i in
19 | Button("\(i * 10)") {
20 | // 这里第一个输入就是锚点的 id,第二个则是表明跳转后对应视图的位置
21 | // .center 表示跳转后视图在界面中央
22 | // 如果第二个参数使用默认的 nil 的话,就会进行最少的移动使视图出现,
23 | // 例如需要向下滑动的话,视图最终会在最下面,向上滑动的话,视图
24 | // 最终会在最上面。
25 | scrollView.scrollTo(i * 10, anchor: .center)
26 | }
27 | }
28 | }
29 | ScrollView {
30 | LazyVStack {
31 | ForEach(0..<100) { i in
32 | // 用 .id 标记锚点
33 | Text("\(i)").font(.title).id(i)
34 | }
35 | ForEach(0..<10) { i in
36 | // id 值可以重复,但是只会识别第一个
37 | Text("\(i)").id(i)
38 | }
39 | }
40 | }
41 | }
42 | }
43 | }
44 |
45 | struct ScrollViewReader_Previews: PreviewProvider {
46 | static var previews: some View {
47 | BasicScrollViewReader()
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/SwiftUIViews/Layout/Spacer.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Spacer.swift
3 | // SwiftUIViews
4 | //
5 | // Created by Zilin Zhu on 2021/2/19.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct BasicSpacer: View {
11 | var body: some View {
12 | VStack {
13 | // Spacer 用于占据 HStack (VStack) 在横(纵)向上
14 | // 布局完原有信息后剩余的空间
15 | HStack {
16 | Spacer()
17 | Text("右").font(.largeTitle)
18 | }
19 | HStack {
20 | Text("左").font(.largeTitle)
21 | Spacer()
22 | }
23 | HStack {
24 | Text("左").font(.largeTitle)
25 | Spacer()
26 | Text("右").font(.largeTitle)
27 | }
28 | // Spacer 对 Stack 来说和其他子视图一样
29 | HStack {
30 | Spacer()
31 | Text("左").font(.largeTitle)
32 | Spacer()
33 | Text("右").font(.largeTitle)
34 | Spacer()
35 | }
36 | // Spacer 上也可以设置 layoutPriority
37 | // 但是这会导致其他视图都消失....
38 | HStack {
39 | Text("左").font(.largeTitle)
40 | Spacer().layoutPriority(2)
41 | Text("右").font(.largeTitle)
42 | }
43 | }
44 | }
45 | }
46 |
47 | struct Spacer_Previews: PreviewProvider {
48 | static var previews: some View {
49 | BasicSpacer()
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/SwiftUIViews/Layout/VStack.swift:
--------------------------------------------------------------------------------
1 | //
2 | // VStack.swift
3 | // SwiftUIViews
4 | //
5 | // Created by Zilin Zhu on 2021/2/19.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct BasicVStack: View {
11 | var body: some View {
12 | // VStack 用于水平摆放视图。和 LazyVStack 不同的是 VStack 会直接
13 | // 渲染内部的所有视图。请在内容较少的时候才使用 VStack
14 | //
15 | // alignment 用于进行对齐,这里因为所有都是 Text 所以效果不明显
16 | // spacing 用于指定元素间隔
17 | VStack(alignment: .leading, spacing: 10) {
18 | ForEach(1...10, id: \.self) {
19 | Text("\($0) 号选手")
20 | }
21 | }
22 | }
23 | }
24 |
25 | struct LayerPriorityBasicVStack: View {
26 | var body: some View {
27 | // 我们可以使用 .layoutPriority 来控制 VStack 中元素的布局优先级。
28 | // 优先级越高的视图会在空间不足的情况下尽量被满足。
29 | // 数字越高代表优先级越高,默认优先级为 0。
30 | VStack {
31 | Text("这个字符串为默认优先级。")
32 | .font(.largeTitle)
33 | .border(Color.gray)
34 |
35 | Spacer()
36 |
37 | Text("这个字符串的优先级更高。")
38 | .font(.largeTitle)
39 | // 注释这行试试?
40 | .layoutPriority(1)
41 | .border(Color.gray)
42 | }
43 | .frame(maxWidth: 30)
44 | }
45 | }
46 |
47 | struct VStack_Previews: PreviewProvider {
48 | static var previews: some View {
49 | Group {
50 | BasicVStack()
51 | LayerPriorityBasicVStack()
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/SwiftUIViews/Layout/ZStack.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ZStack.swift
3 | // SwiftUIViews
4 | //
5 | // Created by Zilin Zhu on 2021/2/17.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct BasicZStack: View {
11 | var body: some View {
12 | // ZStack 的基本用法就是使用
13 | ZStack {
14 | Rectangle().fill(Color.blue)
15 | Circle().fill(Color.orange)
16 | Text("ZStack").font(.title)
17 | }
18 | }
19 | }
20 |
21 | struct IgnoreSafeZStack: View {
22 | var body: some View {
23 | ZStack {
24 | // 注意
25 | ZStack {
26 | Color.purple
27 | }
28 | // 这里默认 .ignoresSafeArea() 为
29 | // .ignoreSafeArea(.all, edges: .all)
30 | // 第一个个值可以为 .keyboard,它是指在打开键盘的时候,
31 | // 键盘后面是否隐约有颜色,用模拟器试试吧~
32 | .ignoresSafeArea(.all, edges: .bottom)
33 | Color.green
34 | // 在模拟器中用 command + K 打开键盘,应该可以看到
35 | // 键盘的下有隐约的紫色
36 | TextField("您的输入:", text: $fullText)
37 | }
38 | }
39 |
40 | @State var fullText: String = ""
41 | }
42 |
43 | struct ZIndexZStack: View {
44 | var body: some View {
45 | ZStack {
46 | // 可以用 zIndex 调整 ZStack 中视图的顺序,
47 | // zIndex 越大的视图会被放在越上面
48 | // zIndex 可以是负数,如果不标注的话 zIndex 为 0
49 | Text("ZStack").font(.title).zIndex(2)
50 | Circle().fill(Color.orange).zIndex(1)
51 | Rectangle().fill(Color.blue).zIndex(0)
52 | }
53 | }
54 | }
55 |
56 | struct ZStack_Previews: PreviewProvider {
57 | static var previews: some View {
58 | Group {
59 | BasicZStack()
60 | IgnoreSafeZStack()
61 | ZIndexZStack()
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/SwiftUIViews/Other/Divider.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Divider.swift
3 | // SwiftUIViews
4 | //
5 | // Created by Zilin Zhu on 2021/2/22.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct BasicDivider: View {
11 | var body: some View {
12 | // Divider 用作分隔符。
13 | // 在 Stack 中使用时,VStack 中为水平方向,HStack 中为竖直方向
14 | // 在其他视图中使用时,为水平方向
15 | VStack {
16 | HStack {
17 | Text("左").font(.title)
18 | Divider()
19 | Text("右").font(.title)
20 | }
21 | VStack {
22 | Text("上").font(.title)
23 | Divider()
24 | Text("下").font(.title)
25 | }
26 | List {
27 | Section {
28 | Button("按钮 1") {}
29 | Button("按钮 2") {}
30 | }
31 | Divider()
32 | Section {
33 | Button("按钮 3") {}
34 | }
35 | }
36 | .listStyle(InsetGroupedListStyle())
37 | }
38 | }
39 | }
40 |
41 | struct Divider_Previews: PreviewProvider {
42 | static var previews: some View {
43 | BasicDivider()
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/SwiftUIViews/Other/EmptyView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EmptyView.swift
3 | // SwiftUIViews
4 | //
5 | // Created by Zilin Zhu on 2021/2/22.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct BasicEmptyView: View {
11 | var body: some View {
12 | // EmptyView 表示空视图。
13 | // 很少会直接创建 EmptyView,在泛型中如果一个视图类型没有初始化,
14 | // 就会被当成 EmptyView
15 | VStack {
16 | EmptyView()
17 | }
18 | }
19 | }
20 |
21 | struct EmptyView_Previews: PreviewProvider {
22 | static var previews: some View {
23 | BasicEmptyView()
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/SwiftUIViews/Other/Menu.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Menu.swift
3 | // SwiftUIViews
4 | //
5 | // Created by Zilin Zhu on 2021/2/22.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct BasicMenu: View {
11 | var body: some View {
12 | VStack {
13 | // Menu 用来创建一个点击后会弹出若干选项的按钮
14 | Menu("目录") {
15 | Button("重命名", action: rename)
16 | Button("删除", action: delete)
17 | // 可以插入一般视图,不一定是按钮
18 | // Menu 可以进行嵌套
19 | Menu("复制") {
20 | Button("复制", action: copy)
21 | Button("格式复制", action: copyFormatted)
22 | Button("复制路径", action: copyPath)
23 | }
24 | Label("标签", systemImage: "pencil.slash")
25 | }
26 | // 除去用字符串作为 Menu 的标识,也可以传入一个 label 视图
27 | Menu {
28 | Button("在预览中打开", action: openInPreview)
29 | Button("保存为 PDF", action: saveAsPDF)
30 | } label: {
31 | Label("PDF", systemImage: "doc.fill")
32 | }
33 | }
34 |
35 | }
36 |
37 | func delete() {}
38 | func rename() {}
39 | func copy() {}
40 | func copyFormatted() {}
41 | func copyPath() {}
42 |
43 | func openInPreview() {}
44 | func saveAsPDF() {}
45 | }
46 |
47 | struct StylingMenu: View {
48 | var body: some View {
49 | Menu("编辑") {
50 | Button("出发地", action: setInPoint)
51 | Button("目的地", action: setOutPoint)
52 | }
53 | // 可以使用 .menuStyle 这个 modifier 修改 Menu 的样式
54 | // 注意 SwiftUI 默认提供的一些样式只有一种适用于 iOS
55 | .menuStyle(BorderlessButtonMenuStyle())
56 | }
57 |
58 | func setInPoint() {}
59 | func setOutPoint() {}
60 | }
61 |
62 | struct Menu_Previews: PreviewProvider {
63 | static var previews: some View {
64 | Group {
65 | BasicMenu()
66 | StylingMenu()
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/SwiftUIViews/Other/Shapes.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Shapes.swift
3 | // SwiftUIViews
4 | //
5 | // Created by Zilin Zhu on 2021/2/22.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct BasicShapes: View {
11 | let columns: [GridItem] = [GridItem(.adaptive(minimum: 100, maximum: 200))]
12 |
13 | var body: some View {
14 | LazyVGrid(columns: columns) {
15 | // 各个形状没有什么特别的地方,只需要根据文档中的构造函数进行初始化就好了
16 | // 形状会尽量充满父视图给予的空间,所以可以通过调整父视图或者 .frame 之类的
17 | // 方式调整他们的形状和尺寸
18 | //
19 | // 胶囊型是一种特殊的圆角矩形,其圆角的半径等于变长的一半
20 | ShapeWithLabel(shape: Capsule(), label: "胶囊型")
21 | ShapeWithLabel(shape: Circle(), label: "圆形")
22 | ShapeWithLabel(shape: Ellipse(), label: "椭圆")
23 | ShapeWithLabel(shape: Rectangle(), label: "长方形")
24 | ShapeWithLabel(shape: RoundedRectangle(cornerRadius: 25.0), label: "圆角矩形")
25 | ShapeWithLabel(shape: RoundedRectangle(cornerSize: CGSize(width: 25, height: 50)), label: "椭圆角矩形")
26 | }
27 | }
28 | }
29 |
30 | struct ShapeWithLabel: View where Content: View {
31 | let shape: Content
32 | let label: String
33 |
34 | var body: some View {
35 | VStack {
36 | Text(label)
37 | shape
38 | .foregroundColor(.blue)
39 | }
40 | .frame(height: 100)
41 | .padding()
42 | }
43 | }
44 |
45 | struct Shapes_Previews: PreviewProvider {
46 | static var previews: some View {
47 | BasicShapes()
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/SwiftUIViews/Paints/AngularGradient.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AngularGradient.swift
3 | // SwiftUIViews
4 | //
5 | // Created by Zilin Zhu on 2021/2/20.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct BasicAngularGradient: View {
11 | var body: some View {
12 | // AngularGradient 表示环装的颜色渐变
13 | // center 表示渐变的中心
14 | // startAngle 和 endAngle 表示渐变的起始角度和结束角度
15 | // 注意剩余的部分会被两端的颜色均分
16 | AngularGradient(
17 | gradient: Gradient(colors: [.red, .white]),
18 | center: .center,
19 | startAngle: Angle(degrees: 0),
20 | endAngle: Angle(degrees: 180)
21 | )
22 | }
23 | }
24 |
25 | struct AngularGradient_Previews: PreviewProvider {
26 | static var previews: some View {
27 | BasicAngularGradient()
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/SwiftUIViews/Paints/LinearGradient.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LinearGradient.swift
3 | // SwiftUIViews
4 | //
5 | // Created by Zilin Zhu on 2021/2/20.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct BasicLinearGradient: View {
11 | var body: some View {
12 | // LinearGradient 表示线性的颜色渐变
13 | // 用 startPoint 和 endPoint 表示渐变的起点和中间
14 | LinearGradient(
15 | gradient: Gradient(colors: [.red, .white]),
16 | startPoint: .top,
17 | endPoint: .bottom
18 | )
19 | }
20 | }
21 |
22 | struct LinearGradient_Previews: PreviewProvider {
23 | static var previews: some View {
24 | BasicLinearGradient()
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/SwiftUIViews/Paints/RadialGradient.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RadialGradient.swift
3 | // SwiftUIViews
4 | //
5 | // Created by Zilin Zhu on 2021/2/20.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct BasicRadialGradient: View {
11 | var body: some View {
12 | // RadialGradient 表示环装沿半径方向的渐变
13 | // center 表示环的中心
14 | // startRadius 和 endRadius 分别表示渐变起始和结束的半径
15 | RadialGradient(
16 | gradient: Gradient(colors: [.white, .orange, .white]),
17 | center: .center,
18 | startRadius: 100,
19 | endRadius: 200
20 | )
21 | }
22 | }
23 |
24 | struct RadialGradient_Previews: PreviewProvider {
25 | static var previews: some View {
26 | BasicRadialGradient()
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/SwiftUIViews/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/SwiftUIViews/SwiftUIViewsApp.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwiftUIViewsApp.swift
3 | // SwiftUIViews
4 | //
5 | // Created by Zilin Zhu on 2021/2/6.
6 | //
7 |
8 | import SwiftUI
9 |
10 | @main
11 | struct SwiftUIViewsApp: App {
12 | var body: some Scene {
13 | WindowGroup {
14 | ContentView()
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/img/controls.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/swiftui-from-zero/SwiftUIViews/d03cb1d8f206c7d84c9142b29f39ef901a03336e/img/controls.gif
--------------------------------------------------------------------------------
/img/hierachical_list.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/swiftui-from-zero/SwiftUIViews/d03cb1d8f206c7d84c9142b29f39ef901a03336e/img/hierachical_list.gif
--------------------------------------------------------------------------------