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