├── .gitignore
├── Makefile
├── README.md
├── SwiftUIPlayground.xcodeproj
├── project.pbxproj
└── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ ├── IDEWorkspaceChecks.plist
│ └── swiftpm
│ └── Package.resolved
├── SwiftUIPlayground
├── AppleSwiftUITutorials
│ └── Landmarks
│ │ ├── Hike
│ │ ├── GraphCapsule.swift
│ │ ├── HexagonParameters.swift
│ │ ├── HikeBadge.swift
│ │ ├── HikeDetail.swift
│ │ ├── HikeGraph.swift
│ │ └── HikeView.swift
│ │ ├── Home
│ │ ├── CategoryRow.swift
│ │ ├── Home.swift
│ │ ├── PageControl.swift
│ │ ├── PageView.swift
│ │ └── PageViewController.swift
│ │ ├── Landmark Views
│ │ ├── LandmarkDetail.swift
│ │ ├── LandmarkList.swift
│ │ └── LandmarkRow.swift
│ │ ├── Models
│ │ ├── Data.swift
│ │ ├── Hike.swift
│ │ ├── Landmark.swift
│ │ ├── Profile.swift
│ │ └── UserData.swift
│ │ ├── Profile
│ │ ├── ProfileEditor.swift
│ │ ├── ProfileHost.swift
│ │ └── ProfileSummary.swift
│ │ ├── Resources
│ │ ├── charleyrivers.jpg
│ │ ├── charleyrivers_feature.jpg
│ │ ├── chilkoottrail.jpg
│ │ ├── chincoteague.jpg
│ │ ├── hiddenlake.jpg
│ │ ├── hikeData.json
│ │ ├── icybay.jpg
│ │ ├── lakemcdonald.jpg
│ │ ├── landmarkData.json
│ │ ├── rainbowlake.jpg
│ │ ├── silversalmoncreek.jpg
│ │ ├── stmarylake.jpg
│ │ ├── stmarylake_feature.jpg
│ │ ├── turtlerock.jpg
│ │ ├── turtlerock_feature.jpg
│ │ ├── twinlake.jpg
│ │ └── umbagog.jpg
│ │ └── Supporting Views
│ │ ├── Badge.swift
│ │ ├── BadgeBackground.swift
│ │ ├── BadgeSymbol.swift
│ │ ├── CircleImage.swift
│ │ ├── FeatureCard.swift
│ │ ├── MapView.swift
│ │ └── RotatedBadgeSymbol.swift
├── Assets.xcassets
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ ├── ButtonColor.colorset
│ │ └── Contents.json
│ ├── ButtonColorGradient.colorset
│ │ └── Contents.json
│ ├── Contents.json
│ ├── cookies.imageset
│ │ ├── 1599px-Chocolate_chip_cookies.jpg
│ │ └── Contents.json
│ └── turtlerock.imageset
│ │ ├── Contents.json
│ │ └── turtlerock.jpg
├── Base.lproj
│ └── LaunchScreen.storyboard
├── ContentView.swift
├── Convenience
│ ├── Array+Indexed.swift
│ └── Date+Extensions.swift
├── Info.plist
├── Models
│ ├── CoreDataStack.swift
│ ├── PlaygroundModel.xcdatamodeld
│ │ └── PlaygroundModel.xcdatamodel
│ │ │ └── contents
│ └── Todo+CoreDataClass.swift
├── Preview Content
│ └── Preview Assets.xcassets
│ │ └── Contents.json
├── SwiftUIPlayground.swift
└── Views
│ ├── AccountView.swift
│ ├── ActivityIndicatorView.swift
│ ├── AlertExampleView.swift
│ ├── AllWidgetsView.swift
│ ├── ButtonExampleView.swift
│ ├── CameraButtonView.swift
│ ├── ChartExample.swift
│ ├── ChartExampleView.swift
│ ├── ClipImageSquareView.swift
│ ├── CloseButtonExampleView.swift
│ ├── CollectionViewExample.swift
│ ├── ColorCycleView.swift
│ ├── ConditionalDatePickerView.swift
│ ├── ContactFormView.swift
│ ├── CustomAlertView.swift
│ ├── DarkModeExampleView.swift
│ ├── DashboardView.swift
│ ├── DatePickerExampleView.swift
│ ├── DragGestureView.swift
│ ├── EditButtonExampleView.swift
│ ├── EditableListExample.swift
│ ├── EnumeratedListView.swift
│ ├── EnvironmentView.swift
│ ├── Frameworks
│ ├── Core Data
│ │ ├── Binding+OptionalFallback.swift
│ │ ├── TodoView.swift
│ │ └── TodosView.swift
│ ├── Core Image
│ │ └── ImageEffectView.swift
│ ├── Foundation
│ │ └── TodosURLSessionExampleView.swift
│ ├── MessageUI
│ │ └── MailView.swift
│ └── PencilKit
│ │ └── PencilKitView.swift
│ ├── ImageExampleView.swift
│ ├── ImagePickerView.swift
│ ├── KeyboardAwareView.swift
│ ├── Layout
│ ├── LayoutSameHeightView.swift
│ └── RelativePositionExampleView.swift
│ ├── LinkedStateChangeView.swift
│ ├── ListExampleView.swift
│ ├── MainView.swift
│ ├── MenuButtonExampleView.swift
│ ├── ModalPickerView.swift
│ ├── NavigationViewExample.swift
│ ├── OptionalsExampleView.swift
│ ├── PickerExampleView.swift
│ ├── PlayersProgressView.swift
│ ├── PlaygroundView.swift
│ ├── ProgrammaticNavigationExampleView.swift
│ ├── QRCodeScannerExampleView.swift
│ ├── SceneViewExample.swift
│ ├── ScrollViewExample.swift
│ ├── SelectionEffectView.swift
│ ├── ShadowsView.swift
│ ├── ShareSheetExampleView.swift
│ ├── SimpleGridViewExample.swift
│ ├── SizePreferenceExampleView.swift
│ ├── SizedUIViewRepresentableView.swift
│ ├── SliderExampleView.swift
│ ├── StateExampleView.swift
│ ├── StepperExampleView.swift
│ ├── StrangeStateGlitchExampleView copy.swift
│ ├── SwiftUISwitchView.swift
│ ├── TextExampleView.swift
│ ├── TextFieldExampleView.swift
│ ├── ToggleExampleView.swift
│ ├── UserDefaultsExampleView.swift
│ └── art.scnassets
│ ├── ship.scn
│ └── texture.png
└── docs
├── Button.png
├── DatePicker.png
├── EditButton.png
├── Form.png
├── Image.png
├── List.png
├── NavigationView.png
├── Picker.png
├── ScrollView.png
├── Section.png
├── SecureField.png
├── Slider.png
├── Stepper.png
├── TabView.png
├── Text.png
├── TextField.png
├── Toggle.png
├── overview.png
├── views.csv
└── views.rb
/.gitignore:
--------------------------------------------------------------------------------
1 | xcuserdata
2 | .DS_Store
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | FORMATTER_OPTIONS = --swiftversion 5.2 --stripunusedargs unnamed-only --self insert --disable blankLinesAtStartOfScope,blankLinesAtEndOfScope --ifdef no-indent
2 |
3 | format:
4 | swiftformat --header "// SwiftUIPlayground\n// https://github.com/ralfebert/SwiftUIPlayground/" $(FORMATTER_OPTIONS) --exclude SwiftUIPlayground/AppleSwiftUITutorials SwiftUIPlayground*
5 | swiftformat $(FORMATTER_OPTIONS) SwiftUIPlayground/AppleSwiftUITutorials
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # SwiftUIPlayground
2 |
3 | SwiftUIPlayground is a collection of SwiftUI example snippets:
4 |
5 | SwiftUI view | UIKit View | Example
6 | ---------------------------------------- | ------------------------------------------------------ | --------------------------------------------------------------------------
7 | [Button][swiftui-button] | [UIButton][uikit-uibutton] | [
][example-button]
8 | [DatePicker][swiftui-datepicker] | [UIDatePicker][uikit-uidatepicker] | [
][example-datepicker]
9 | [EditButton][swiftui-editbutton] | | [
][example-editbutton]
10 | [Form][swiftui-form] | | [
][example-form]
11 | [Image][swiftui-image] | [UIImageView][uikit-uiimageview] | [
][example-image]
12 | [List][swiftui-list] | [UITableView][uikit-uitableview] | [
][example-list]
13 | [NavigationView][swiftui-navigationview] | [UINavigationController][uikit-uinavigationcontroller] | [
][example-navigationview]
14 | [Picker][swiftui-picker] | [UIPickerView][uikit-uipickerview] | [
][example-picker]
15 | [ScrollView][swiftui-scrollview] | [UIScrollView][uikit-uiscrollview] | [
][example-scrollview]
16 | [Section][swiftui-section] | | [
][example-section]
17 | [SecureField][swiftui-securefield] | [UITextField][uikit-uitextfield] | [
][example-securefield]
18 | [Slider][swiftui-slider] | [UISlider][uikit-uislider] | [
][example-slider]
19 | [Stepper][swiftui-stepper] | [UIStepper][uikit-uistepper] | [
][example-stepper]
20 | [TabView][swiftui-tabview] | [UITabBarController][uikit-uitabbarcontroller] | [
][example-tabview]
21 | [Text][swiftui-text] | [UILabel][uikit-uilabel] | [
][example-text]
22 | [TextField][swiftui-textfield] | [UITextField][uikit-uitextfield] | [
][example-textfield]
23 | [Toggle][swiftui-toggle] | [UISwitch][uikit-uiswitch] | [
][example-toggle]
24 |
25 | [swiftui-button]: https://developer.apple.com/documentation/swiftui/button
26 | [example-button]: https://github.com/ralfebert/SwiftUIPlayground/blob/master/SwiftUIPlayground/Views/ButtonExampleView.swift
27 | [uikit-uibutton]: https://developer.apple.com/documentation/uikit/uibutton
28 | [swiftui-datepicker]: https://developer.apple.com/documentation/swiftui/datepicker
29 | [example-datepicker]: https://github.com/ralfebert/SwiftUIPlayground/blob/master/SwiftUIPlayground/Views/DatePickerExampleView.swift
30 | [uikit-uidatepicker]: https://developer.apple.com/documentation/uikit/uidatepicker
31 | [swiftui-editbutton]: https://developer.apple.com/documentation/swiftui/editbutton
32 | [example-editbutton]: https://github.com/ralfebert/SwiftUIPlayground/blob/master/SwiftUIPlayground/Views/EditButtonExampleView.swift
33 | [swiftui-form]: https://developer.apple.com/documentation/swiftui/form
34 | [example-form]: https://github.com/ralfebert/SwiftUIPlayground/blob/master/SwiftUIPlayground/Views/ContactFormView.swift
35 | [swiftui-image]: https://developer.apple.com/documentation/swiftui/image
36 | [example-image]: https://github.com/ralfebert/SwiftUIPlayground/blob/master/SwiftUIPlayground/Views/ImageExampleView.swift
37 | [uikit-uiimageview]: https://developer.apple.com/documentation/uikit/uiimageview
38 | [swiftui-list]: https://developer.apple.com/documentation/swiftui/list
39 | [example-list]: https://github.com/ralfebert/SwiftUIPlayground/blob/master/SwiftUIPlayground/Views/ListExampleView.swift
40 | [uikit-uitableview]: https://developer.apple.com/documentation/uikit/uitableview
41 | [swiftui-navigationview]: https://developer.apple.com/documentation/swiftui/navigationview
42 | [example-navigationview]: https://github.com/ralfebert/SwiftUIPlayground/blob/master/SwiftUIPlayground/Views/NavigationViewExample.swift
43 | [uikit-uinavigationcontroller]: https://developer.apple.com/documentation/uikit/uinavigationcontroller
44 | [swiftui-picker]: https://developer.apple.com/documentation/swiftui/picker
45 | [example-picker]: https://github.com/ralfebert/SwiftUIPlayground/blob/master/SwiftUIPlayground/Views/PickerExampleView.swift
46 | [uikit-uipickerview]: https://developer.apple.com/documentation/uikit/uipickerview
47 | [swiftui-scrollview]: https://developer.apple.com/documentation/swiftui/scrollview
48 | [example-scrollview]: https://github.com/ralfebert/SwiftUIPlayground/blob/master/SwiftUIPlayground/Views/ScrollViewExample.swift
49 | [uikit-uiscrollview]: https://developer.apple.com/documentation/uikit/uiscrollview
50 | [swiftui-section]: https://developer.apple.com/documentation/swiftui/section
51 | [example-section]: https://github.com/ralfebert/SwiftUIPlayground/blob/master/SwiftUIPlayground/Views/ContactFormView.swift
52 | [swiftui-securefield]: https://developer.apple.com/documentation/swiftui/securefield
53 | [example-securefield]: https://github.com/ralfebert/SwiftUIPlayground/blob/master/SwiftUIPlayground/Views/TextFieldExampleView.swift
54 | [uikit-uitextfield]: https://developer.apple.com/documentation/uikit/uitextfield
55 | [swiftui-slider]: https://developer.apple.com/documentation/swiftui/slider
56 | [example-slider]: https://github.com/ralfebert/SwiftUIPlayground/blob/master/SwiftUIPlayground/Views/SliderExampleView.swift
57 | [uikit-uislider]: https://developer.apple.com/documentation/uikit/uislider
58 | [swiftui-stepper]: https://developer.apple.com/documentation/swiftui/stepper
59 | [example-stepper]: https://github.com/ralfebert/SwiftUIPlayground/blob/master/SwiftUIPlayground/Views/StepperExampleView.swift
60 | [uikit-uistepper]: https://developer.apple.com/documentation/uikit/uistepper
61 | [swiftui-tabview]: https://developer.apple.com/documentation/swiftui/tabview
62 | [example-tabview]: https://github.com/ralfebert/SwiftUIPlayground/blob/master/SwiftUIPlayground/Views/MainView.swift
63 | [uikit-uitabbarcontroller]: https://developer.apple.com/documentation/uikit/uitabbarcontroller
64 | [swiftui-text]: https://developer.apple.com/documentation/swiftui/text
65 | [example-text]: https://github.com/ralfebert/SwiftUIPlayground/blob/master/SwiftUIPlayground/Views/TextExampleView.swift
66 | [uikit-uilabel]: https://developer.apple.com/documentation/uikit/uilabel
67 | [swiftui-textfield]: https://developer.apple.com/documentation/swiftui/textfield
68 | [example-textfield]: https://github.com/ralfebert/SwiftUIPlayground/blob/master/SwiftUIPlayground/Views/TextFieldExampleView.swift
69 | [uikit-uitextfield]: https://developer.apple.com/documentation/uikit/uitextfield
70 | [swiftui-toggle]: https://developer.apple.com/documentation/swiftui/toggle
71 | [example-toggle]: https://github.com/ralfebert/SwiftUIPlayground/blob/master/SwiftUIPlayground/Views/ToggleExampleView.swift
72 | [uikit-uiswitch]: https://developer.apple.com/documentation/uikit/uiswitch
73 |
74 | ## Image copyrights
75 |
76 | * Cookies:
77 | https://de.wikipedia.org/wiki/Datei:Chocolate_chip_cookies.jpg
78 |
--------------------------------------------------------------------------------
/SwiftUIPlayground.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/SwiftUIPlayground.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/SwiftUIPlayground.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved:
--------------------------------------------------------------------------------
1 | {
2 | "object": {
3 | "pins": [
4 | {
5 | "package": "CodeScanner",
6 | "repositoryURL": "https://github.com/twostraws/CodeScanner.git",
7 | "state": {
8 | "branch": null,
9 | "revision": "9d902dec616bd70b1a2a1b1e6d821c10c392ba9f",
10 | "version": "1.0.6"
11 | }
12 | },
13 | {
14 | "package": "ImagePickerView",
15 | "repositoryURL": "https://github.com/ralfebert/ImagePickerView.git",
16 | "state": {
17 | "branch": null,
18 | "revision": "b84dd1c85a65cddba40c89699cd63ba0cb248dbd",
19 | "version": "0.5.0"
20 | }
21 | },
22 | {
23 | "package": "KeyboardAware",
24 | "repositoryURL": "https://github.com/ralfebert/KeyboardAwareSwiftUI.git",
25 | "state": {
26 | "branch": null,
27 | "revision": "c76a2e1ee69b413885c604e62f39b796de97dcda",
28 | "version": "0.5.0"
29 | }
30 | }
31 | ]
32 | },
33 | "version": 1
34 | }
35 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/AppleSwiftUITutorials/Landmarks/Hike/GraphCapsule.swift:
--------------------------------------------------------------------------------
1 | /*
2 | See LICENSE folder for this sample’s licensing information.
3 |
4 | Abstract:
5 | A single line in the graph.
6 | */
7 |
8 | import SwiftUI
9 |
10 | struct GraphCapsule: View {
11 | var index: Int
12 | var height: CGFloat
13 | var range: Range
14 | var overallRange: Range
15 |
16 | var heightRatio: CGFloat {
17 | max(CGFloat(magnitude(of: self.range) / magnitude(of: self.overallRange)), 0.15)
18 | }
19 |
20 | var offsetRatio: CGFloat {
21 | CGFloat((self.range.lowerBound - self.overallRange.lowerBound) / magnitude(of: self.overallRange))
22 | }
23 |
24 | var body: some View {
25 | Capsule()
26 | .fill(Color.white)
27 | .frame(height: height * heightRatio)
28 | .offset(x: 0, y: height * -offsetRatio)
29 | }
30 | }
31 |
32 | struct GraphCapsule_Previews: PreviewProvider {
33 | static var previews: some View {
34 | GraphCapsule(index: 0, height: 150, range: 10 ..< 50, overallRange: 0 ..< 100)
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/AppleSwiftUITutorials/Landmarks/Hike/HexagonParameters.swift:
--------------------------------------------------------------------------------
1 | /*
2 | See LICENSE folder for this sample’s licensing information.
3 |
4 | Abstract:
5 | Size, position, and other information used to draw a badge.
6 | */
7 |
8 | import SwiftUI
9 |
10 | struct HexagonParameters {
11 | struct Segment {
12 | let useWidth: (CGFloat, CGFloat, CGFloat)
13 | let xFactors: (CGFloat, CGFloat, CGFloat)
14 | let useHeight: (CGFloat, CGFloat, CGFloat)
15 | let yFactors: (CGFloat, CGFloat, CGFloat)
16 | }
17 |
18 | static let adjustment: CGFloat = 0.085
19 | static let points = [
20 | Segment(
21 | useWidth: (1.00, 1.00, 1.00),
22 | xFactors: (0.60, 0.40, 0.50),
23 | useHeight: (1.00, 1.00, 0.00),
24 | yFactors: (0.05, 0.05, 0.00)
25 | ),
26 | Segment(
27 | useWidth: (1.00, 1.00, 0.00),
28 | xFactors: (0.05, 0.00, 0.00),
29 | useHeight: (1.00, 1.00, 1.00),
30 | yFactors: (0.20 + adjustment, 0.30 + adjustment, 0.25 + adjustment)
31 | ),
32 | Segment(
33 | useWidth: (1.00, 1.00, 0.00),
34 | xFactors: (0.00, 0.05, 0.00),
35 | useHeight: (1.00, 1.00, 1.00),
36 | yFactors: (0.70 - adjustment, 0.80 - adjustment, 0.75 - adjustment)
37 | ),
38 | Segment(
39 | useWidth: (1.00, 1.00, 1.00),
40 | xFactors: (0.40, 0.60, 0.50),
41 | useHeight: (1.00, 1.00, 1.00),
42 | yFactors: (0.95, 0.95, 1.00)
43 | ),
44 | Segment(
45 | useWidth: (1.00, 1.00, 1.00),
46 | xFactors: (0.95, 1.00, 1.00),
47 | useHeight: (1.00, 1.00, 1.00),
48 | yFactors: (0.80 - adjustment, 0.70 - adjustment, 0.75 - adjustment)
49 | ),
50 | Segment(
51 | useWidth: (1.00, 1.00, 1.00),
52 | xFactors: (1.00, 0.95, 1.00),
53 | useHeight: (1.00, 1.00, 1.00),
54 | yFactors: (0.30 + adjustment, 0.20 + adjustment, 0.25 + adjustment)
55 | ),
56 | ]
57 | }
58 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/AppleSwiftUITutorials/Landmarks/Hike/HikeBadge.swift:
--------------------------------------------------------------------------------
1 | /*
2 | See LICENSE folder for this sample’s licensing information.
3 |
4 | Abstract:
5 | A view that shows a badge for hiking.
6 | */
7 |
8 | import SwiftUI
9 |
10 | struct HikeBadge: View {
11 | var name: String
12 | var body: some View {
13 | VStack(alignment: .center) {
14 | Badge()
15 | .frame(width: 300, height: 300)
16 | .scaleEffect(1.0 / 3.0)
17 | .frame(width: 100, height: 100)
18 | Text(name)
19 | .font(.caption)
20 | .accessibility(label: Text("Badge for \(name)."))
21 | }
22 | }
23 | }
24 |
25 | struct HikeBadge_Previews: PreviewProvider {
26 | static var previews: some View {
27 | HikeBadge(name: "Preview Testing")
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/AppleSwiftUITutorials/Landmarks/Hike/HikeDetail.swift:
--------------------------------------------------------------------------------
1 | /*
2 | See LICENSE folder for this sample’s licensing information.
3 |
4 | Abstract:
5 | A view showing the details for a hike.
6 | */
7 |
8 | import SwiftUI
9 |
10 | struct HikeDetail: View {
11 | let hike: Hike
12 | @State var dataToShow = \Hike.Observation.elevation
13 |
14 | var buttons = [
15 | ("Elevation", \Hike.Observation.elevation),
16 | ("Heart Rate", \Hike.Observation.heartRate),
17 | ("Pace", \Hike.Observation.pace),
18 | ]
19 |
20 | var body: some View {
21 | VStack {
22 | HikeGraph(hike: hike, path: dataToShow)
23 | .frame(height: 200)
24 |
25 | HStack(spacing: 25) {
26 | ForEach(buttons, id: \.0) { value in
27 | Button(action: {
28 | self.dataToShow = value.1
29 | }) {
30 | Text(value.0)
31 | .font(.system(size: 15))
32 | .foregroundColor(value.1 == self.dataToShow
33 | ? Color.gray
34 | : Color.accentColor)
35 | .animation(nil)
36 | }
37 | }
38 | }
39 | }
40 | }
41 | }
42 |
43 | struct HikeDetail_Previews: PreviewProvider {
44 | static var previews: some View {
45 | HikeDetail(hike: hikeData[0])
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/AppleSwiftUITutorials/Landmarks/Hike/HikeGraph.swift:
--------------------------------------------------------------------------------
1 | /*
2 | See LICENSE folder for this sample’s licensing information.
3 |
4 | Abstract:
5 | The elevation, heart rate, and pace of a hike plotted on a graph.
6 | */
7 |
8 | import SwiftUI
9 |
10 | func rangeOfRanges(_ ranges: C) -> Range
11 | where C.Element == Range
12 | {
13 | guard !ranges.isEmpty else { return 0 ..< 0 }
14 | let low = ranges.lazy.map(\.lowerBound).min()!
15 | let high = ranges.lazy.map(\.upperBound).max()!
16 | return low ..< high
17 | }
18 |
19 | func magnitude(of range: Range) -> Double {
20 | range.upperBound - range.lowerBound
21 | }
22 |
23 | extension Animation {
24 | static func ripple(index: Int) -> Animation {
25 | Animation.spring(dampingFraction: 0.5)
26 | .speed(2)
27 | .delay(0.03 * Double(index))
28 | }
29 | }
30 |
31 | struct HikeGraph: View {
32 | var hike: Hike
33 | var path: KeyPath>
34 |
35 | var color: Color {
36 | switch self.path {
37 | case \.elevation:
38 | return .gray
39 | case \.heartRate:
40 | return Color(hue: 0, saturation: 0.5, brightness: 0.7)
41 | case \.pace:
42 | return Color(hue: 0.7, saturation: 0.4, brightness: 0.7)
43 | default:
44 | return .black
45 | }
46 | }
47 |
48 | var body: some View {
49 | let data = hike.observations
50 | let overallRange = rangeOfRanges(data.lazy.map { $0[keyPath: self.path] })
51 | let maxMagnitude = data.map { magnitude(of: $0[keyPath: path]) }.max()!
52 | let heightRatio = (1 - CGFloat(maxMagnitude / magnitude(of: overallRange))) / 2
53 |
54 | return GeometryReader { proxy in
55 | HStack(alignment: .bottom, spacing: proxy.size.width / 120) {
56 | ForEach(data.indices) { index in
57 | GraphCapsule(
58 | index: index,
59 | height: proxy.size.height,
60 | range: data[index][keyPath: self.path],
61 | overallRange: overallRange
62 | )
63 | .colorMultiply(self.color)
64 | .transition(.slide)
65 | .animation(.ripple(index: index))
66 | }
67 | .offset(x: 0, y: proxy.size.height * heightRatio)
68 | }
69 | }
70 | }
71 | }
72 |
73 | struct HikeGraph_Previews: PreviewProvider {
74 | static var previews: some View {
75 | Group {
76 | HikeGraph(hike: hikeData[0], path: \.elevation)
77 | .frame(height: 200)
78 | HikeGraph(hike: hikeData[0], path: \.heartRate)
79 | .frame(height: 200)
80 | HikeGraph(hike: hikeData[0], path: \.pace)
81 | .frame(height: 200)
82 | }
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/AppleSwiftUITutorials/Landmarks/Hike/HikeView.swift:
--------------------------------------------------------------------------------
1 | /*
2 | See LICENSE folder for this sample’s licensing information.
3 |
4 | Abstract:
5 | A view displaying information about a hike, including an elevation graph.
6 | */
7 |
8 | import SwiftUI
9 |
10 | struct HikeView: View {
11 | var hike: Hike
12 | @State private var showDetail = false
13 |
14 | var transition: AnyTransition {
15 | let insertion = AnyTransition.move(edge: .trailing)
16 | .combined(with: .opacity)
17 | let removal = AnyTransition.scale
18 | .combined(with: .opacity)
19 | return .asymmetric(insertion: insertion, removal: removal)
20 | }
21 |
22 | var body: some View {
23 | VStack {
24 | HStack {
25 | HikeGraph(hike: hike, path: \.elevation)
26 | .frame(width: 50, height: 30)
27 | .animation(nil)
28 |
29 | VStack(alignment: .leading) {
30 | Text(hike.name)
31 | .font(.headline)
32 | Text(hike.distanceText)
33 | }
34 |
35 | Spacer()
36 |
37 | Button(action: {
38 | withAnimation {
39 | self.showDetail.toggle()
40 | }
41 | }) {
42 | Image(systemName: "chevron.right.circle")
43 | .imageScale(.large)
44 | .rotationEffect(.degrees(showDetail ? 90 : 0))
45 | .scaleEffect(showDetail ? 1.5 : 1)
46 | .padding()
47 | }
48 | }
49 |
50 | if showDetail {
51 | HikeDetail(hike: hike)
52 | .transition(transition)
53 | }
54 | }
55 | }
56 | }
57 |
58 | struct HikeView_Previews: PreviewProvider {
59 | static var previews: some View {
60 | VStack {
61 | HikeView(hike: hikeData[0])
62 | .padding()
63 | Spacer()
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/AppleSwiftUITutorials/Landmarks/Home/CategoryRow.swift:
--------------------------------------------------------------------------------
1 | /*
2 | See LICENSE folder for this sample’s licensing information.
3 |
4 | Abstract:
5 | A view showing a scrollable list of landmarks.
6 | */
7 |
8 | import SwiftUI
9 |
10 | struct CategoryRow: View {
11 | var categoryName: String
12 | var items: [Landmark]
13 |
14 | var body: some View {
15 | VStack(alignment: .leading) {
16 | Text(self.categoryName)
17 | .font(.headline)
18 | .padding(.leading, 15)
19 | .padding(.top, 5)
20 |
21 | ScrollView(.horizontal, showsIndicators: false) {
22 | HStack(alignment: .top, spacing: 0) {
23 | ForEach(self.items) { landmark in
24 | NavigationLink(
25 | destination: LandmarkDetail(
26 | landmark: landmark
27 | )
28 | ) {
29 | CategoryItem(landmark: landmark)
30 | }
31 | }
32 | }
33 | }
34 | .frame(height: 185)
35 | }
36 | }
37 | }
38 |
39 | struct CategoryItem: View {
40 | var landmark: Landmark
41 | var body: some View {
42 | VStack(alignment: .leading) {
43 | landmark.image
44 | .renderingMode(.original)
45 | .resizable()
46 | .frame(width: 155, height: 155)
47 | .cornerRadius(5)
48 | Text(landmark.name)
49 | .foregroundColor(.primary)
50 | .font(.caption)
51 | }
52 | .padding(.leading, 15)
53 | }
54 | }
55 |
56 | struct CategoryRow_Previews: PreviewProvider {
57 | static var previews: some View {
58 | CategoryRow(
59 | categoryName: landmarkData[0].category.rawValue,
60 | items: Array(landmarkData.prefix(4))
61 | )
62 | .environmentObject(UserData())
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/AppleSwiftUITutorials/Landmarks/Home/Home.swift:
--------------------------------------------------------------------------------
1 | /*
2 | See LICENSE folder for this sample’s licensing information.
3 |
4 | Abstract:
5 | A view showing featured landmarks above a list of all of the landmarks.
6 | */
7 |
8 | import SwiftUI
9 |
10 | struct CategoryHome: View {
11 | var categories: [String: [Landmark]] {
12 | Dictionary(
13 | grouping: landmarkData,
14 | by: { $0.category.rawValue }
15 | )
16 | }
17 |
18 | var featured: [Landmark] {
19 | landmarkData.filter { $0.isFeatured }
20 | }
21 |
22 | @State var showingProfile = false
23 | @EnvironmentObject var userData: UserData
24 |
25 | var profileButton: some View {
26 | Button(action: { self.showingProfile.toggle() }) {
27 | Image(systemName: "person.crop.circle")
28 | .imageScale(.large)
29 | .accessibility(label: Text("User Profile"))
30 | .padding()
31 | }
32 | }
33 |
34 | var body: some View {
35 | NavigationView {
36 | List {
37 | FeaturedLandmarks(landmarks: featured)
38 | .scaledToFill()
39 | .frame(height: 200)
40 | .clipped()
41 | .listRowInsets(EdgeInsets())
42 |
43 | ForEach(categories.keys.sorted(), id: \.self) { key in
44 | CategoryRow(categoryName: key, items: self.categories[key]!)
45 | }
46 | .listRowInsets(EdgeInsets())
47 |
48 | NavigationLink(destination: LandmarkList()) {
49 | Text("See All")
50 | }
51 | }
52 | .navigationBarTitle(Text("Featured"))
53 | .navigationBarItems(trailing: profileButton)
54 | .sheet(isPresented: $showingProfile) {
55 | ProfileHost()
56 | .environmentObject(self.userData)
57 | }
58 | }
59 | }
60 | }
61 |
62 | struct FeaturedLandmarks: View {
63 | var landmarks: [Landmark]
64 | var body: some View {
65 | landmarks[0].image.resizable()
66 | }
67 | }
68 |
69 | struct CategoryHome_Previews: PreviewProvider {
70 | static var previews: some View {
71 | CategoryHome()
72 | .environmentObject(UserData())
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/AppleSwiftUITutorials/Landmarks/Home/PageControl.swift:
--------------------------------------------------------------------------------
1 | /*
2 | See LICENSE folder for this sample’s licensing information.
3 |
4 | Abstract:
5 | A view wrapping a UIPageControl.
6 | */
7 |
8 | import SwiftUI
9 |
10 | import UIKit
11 |
12 | struct PageControl: UIViewRepresentable {
13 | var numberOfPages: Int
14 | @Binding var currentPage: Int
15 |
16 | func makeCoordinator() -> Coordinator {
17 | Coordinator(self)
18 | }
19 |
20 | func makeUIView(context: Context) -> UIPageControl {
21 | let control = UIPageControl()
22 | control.numberOfPages = self.numberOfPages
23 | control.addTarget(
24 | context.coordinator,
25 | action: #selector(Coordinator.updateCurrentPage(sender:)),
26 | for: .valueChanged
27 | )
28 |
29 | return control
30 | }
31 |
32 | func updateUIView(_ uiView: UIPageControl, context: Context) {
33 | uiView.currentPage = self.currentPage
34 | }
35 |
36 | class Coordinator: NSObject {
37 | var control: PageControl
38 |
39 | init(_ control: PageControl) {
40 | self.control = control
41 | }
42 |
43 | @objc
44 | func updateCurrentPage(sender: UIPageControl) {
45 | self.control.currentPage = sender.currentPage
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/AppleSwiftUITutorials/Landmarks/Home/PageView.swift:
--------------------------------------------------------------------------------
1 | /*
2 | See LICENSE folder for this sample’s licensing information.
3 |
4 | Abstract:
5 | A view for bridging a UIPageViewController.
6 | */
7 |
8 | import SwiftUI
9 |
10 | struct PageView: View {
11 | var viewControllers: [UIHostingController]
12 | @State var currentPage = 0
13 |
14 | init(_ views: [Page]) {
15 | self.viewControllers = views.map { UIHostingController(rootView: $0) }
16 | }
17 |
18 | var body: some View {
19 | ZStack(alignment: .bottomTrailing) {
20 | PageViewController(controllers: viewControllers, currentPage: $currentPage)
21 | PageControl(numberOfPages: viewControllers.count, currentPage: $currentPage)
22 | .padding(.trailing)
23 | }
24 | }
25 | }
26 |
27 | struct PageView_Previews: PreviewProvider {
28 | static var previews: some View {
29 | PageView(features.map { FeatureCard(landmark: $0) })
30 | .aspectRatio(3 / 2, contentMode: .fit)
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/AppleSwiftUITutorials/Landmarks/Home/PageViewController.swift:
--------------------------------------------------------------------------------
1 | /*
2 | See LICENSE folder for this sample’s licensing information.
3 |
4 | Abstract:
5 | A view that wraps a UIPageViewController.
6 | */
7 |
8 | import SwiftUI
9 | import UIKit
10 |
11 | struct PageViewController: UIViewControllerRepresentable {
12 | var controllers: [UIViewController]
13 | @Binding var currentPage: Int
14 |
15 | func makeCoordinator() -> Coordinator {
16 | Coordinator(self)
17 | }
18 |
19 | func makeUIViewController(context: Context) -> UIPageViewController {
20 | let pageViewController = UIPageViewController(
21 | transitionStyle: .scroll,
22 | navigationOrientation: .horizontal
23 | )
24 | pageViewController.dataSource = context.coordinator
25 | pageViewController.delegate = context.coordinator
26 |
27 | return pageViewController
28 | }
29 |
30 | func updateUIViewController(_ pageViewController: UIPageViewController, context: Context) {
31 | pageViewController.setViewControllers(
32 | [self.controllers[self.currentPage]], direction: .forward, animated: true
33 | )
34 | }
35 |
36 | class Coordinator: NSObject, UIPageViewControllerDataSource, UIPageViewControllerDelegate {
37 | var parent: PageViewController
38 |
39 | init(_ pageViewController: PageViewController) {
40 | self.parent = pageViewController
41 | }
42 |
43 | func pageViewController(
44 | _ pageViewController: UIPageViewController,
45 | viewControllerBefore viewController: UIViewController
46 | ) -> UIViewController? {
47 | guard let index = parent.controllers.firstIndex(of: viewController) else {
48 | return nil
49 | }
50 | if index == 0 {
51 | return self.parent.controllers.last
52 | }
53 | return self.parent.controllers[index - 1]
54 | }
55 |
56 | func pageViewController(
57 | _ pageViewController: UIPageViewController,
58 | viewControllerAfter viewController: UIViewController
59 | ) -> UIViewController? {
60 | guard let index = parent.controllers.firstIndex(of: viewController) else {
61 | return nil
62 | }
63 | if index + 1 == self.parent.controllers.count {
64 | return self.parent.controllers.first
65 | }
66 | return self.parent.controllers[index + 1]
67 | }
68 |
69 | func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
70 | if completed,
71 | let visibleViewController = pageViewController.viewControllers?.first,
72 | let index = parent.controllers.firstIndex(of: visibleViewController)
73 | {
74 | self.parent.currentPage = index
75 | }
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/AppleSwiftUITutorials/Landmarks/Landmark Views/LandmarkDetail.swift:
--------------------------------------------------------------------------------
1 | /*
2 | See LICENSE folder for this sample’s licensing information.
3 |
4 | Abstract:
5 | A view showing the details for a landmark.
6 | */
7 |
8 | import SwiftUI
9 |
10 | struct LandmarkDetail: View {
11 | @EnvironmentObject var userData: UserData
12 | var landmark: Landmark
13 |
14 | var landmarkIndex: Int {
15 | self.userData.landmarks.firstIndex(where: { $0.id == landmark.id })!
16 | }
17 |
18 | var body: some View {
19 | VStack {
20 | MapView(coordinate: landmark.locationCoordinate)
21 | .edgesIgnoringSafeArea(.top)
22 | .frame(height: 300)
23 |
24 | CircleImage(image: landmark.image)
25 | .offset(x: 0, y: -130)
26 | .padding(.bottom, -130)
27 |
28 | VStack(alignment: .leading) {
29 | HStack {
30 | Text(landmark.name)
31 | .font(.title)
32 |
33 | Button(action: {
34 | self.userData.landmarks[self.landmarkIndex]
35 | .isFavorite.toggle()
36 | }) {
37 | if self.userData.landmarks[self.landmarkIndex].isFavorite {
38 | Image(systemName: "star.fill")
39 | .foregroundColor(Color.yellow)
40 | } else {
41 | Image(systemName: "star")
42 | .foregroundColor(Color.gray)
43 | }
44 | }
45 | }
46 |
47 | HStack(alignment: .top) {
48 | Text(landmark.park)
49 | .font(.subheadline)
50 | Spacer()
51 | Text(landmark.state)
52 | .font(.subheadline)
53 | }
54 | }
55 | .padding()
56 |
57 | Spacer()
58 | }
59 | }
60 | }
61 |
62 | struct LandmarkDetail_Previews: PreviewProvider {
63 | static var previews: some View {
64 | let userData = UserData()
65 | return LandmarkDetail(landmark: userData.landmarks[0])
66 | .environmentObject(userData)
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/AppleSwiftUITutorials/Landmarks/Landmark Views/LandmarkList.swift:
--------------------------------------------------------------------------------
1 | /*
2 | See LICENSE folder for this sample’s licensing information.
3 |
4 | Abstract:
5 | A view showing a list of landmarks.
6 | */
7 |
8 | import SwiftUI
9 |
10 | struct LandmarkList: View {
11 | @EnvironmentObject private var userData: UserData
12 |
13 | var body: some View {
14 | List {
15 | Toggle(isOn: $userData.showFavoritesOnly) {
16 | Text("Show Favorites Only")
17 | }
18 |
19 | ForEach(userData.landmarks) { landmark in
20 | if !self.userData.showFavoritesOnly || landmark.isFavorite {
21 | NavigationLink(
22 | destination: LandmarkDetail(landmark: landmark)
23 | ) {
24 | LandmarkRow(landmark: landmark)
25 | }
26 | }
27 | }
28 | }
29 | .navigationBarTitle(Text("Landmarks"))
30 | }
31 | }
32 |
33 | struct LandmarksList_Previews: PreviewProvider {
34 | static var previews: some View {
35 | ForEach(["iPhone SE", "iPhone XS Max"], id: \.self) { deviceName in
36 | NavigationView {
37 | LandmarkList()
38 | .previewDevice(PreviewDevice(rawValue: deviceName))
39 | .previewDisplayName(deviceName)
40 | }
41 | }
42 | .environmentObject(UserData())
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/AppleSwiftUITutorials/Landmarks/Landmark Views/LandmarkRow.swift:
--------------------------------------------------------------------------------
1 | /*
2 | See LICENSE folder for this sample’s licensing information.
3 |
4 | Abstract:
5 | A single row to be displayed in a list of landmarks.
6 | */
7 |
8 | import SwiftUI
9 |
10 | struct LandmarkRow: View {
11 | var landmark: Landmark
12 |
13 | var body: some View {
14 | HStack {
15 | landmark.image
16 | .resizable()
17 | .frame(width: 50, height: 50)
18 | Text(landmark.name)
19 | Spacer()
20 |
21 | if landmark.isFavorite {
22 | Image(systemName: "star.fill")
23 | .imageScale(.medium)
24 | .foregroundColor(.yellow)
25 | }
26 | }
27 | }
28 | }
29 |
30 | struct LandmarkRow_Previews: PreviewProvider {
31 | static var previews: some View {
32 | Group {
33 | LandmarkRow(landmark: landmarkData[0])
34 | LandmarkRow(landmark: landmarkData[1])
35 | }
36 | .previewLayout(.fixed(width: 300, height: 70))
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/AppleSwiftUITutorials/Landmarks/Models/Data.swift:
--------------------------------------------------------------------------------
1 | /*
2 | See LICENSE folder for this sample’s licensing information.
3 |
4 | Abstract:
5 | Helpers for loading images and data.
6 | */
7 |
8 | import CoreLocation
9 | import Foundation
10 | import SwiftUI
11 |
12 | let landmarkData: [Landmark] = load("landmarkData.json")
13 | let features = landmarkData.filter { $0.isFeatured }
14 | let hikeData: [Hike] = load("hikeData.json")
15 |
16 | func load(_ filename: String) -> T {
17 | let data: Data
18 |
19 | guard let file = Bundle.main.url(forResource: filename, withExtension: nil)
20 | else {
21 | fatalError("Couldn't find \(filename) in main bundle.")
22 | }
23 |
24 | do {
25 | data = try Data(contentsOf: file)
26 | } catch {
27 | fatalError("Couldn't load \(filename) from main bundle:\n\(error)")
28 | }
29 |
30 | do {
31 | let decoder = JSONDecoder()
32 | return try decoder.decode(T.self, from: data)
33 | } catch {
34 | fatalError("Couldn't parse \(filename) as \(T.self):\n\(error)")
35 | }
36 | }
37 |
38 | final class ImageStore {
39 | typealias _ImageDictionary = [String: CGImage]
40 | private var images: _ImageDictionary = [:]
41 |
42 | fileprivate static var scale = 2
43 |
44 | static var shared = ImageStore()
45 |
46 | func image(name: String) -> Image {
47 | let index = self._guaranteeImage(name: name)
48 |
49 | return Image(self.images.values[index], scale: CGFloat(ImageStore.scale), label: Text(name))
50 | }
51 |
52 | static func loadImage(name: String) -> CGImage {
53 | guard
54 | let url = Bundle.main.url(forResource: name, withExtension: "jpg"),
55 | let imageSource = CGImageSourceCreateWithURL(url as NSURL, nil),
56 | let image = CGImageSourceCreateImageAtIndex(imageSource, 0, nil)
57 | else {
58 | fatalError("Couldn't load image \(name).jpg from main bundle.")
59 | }
60 | return image
61 | }
62 |
63 | private func _guaranteeImage(name: String) -> _ImageDictionary.Index {
64 | if let index = images.index(forKey: name) { return index }
65 |
66 | self.images[name] = ImageStore.loadImage(name: name)
67 | return self.images.index(forKey: name)!
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/AppleSwiftUITutorials/Landmarks/Models/Hike.swift:
--------------------------------------------------------------------------------
1 | /*
2 | See LICENSE folder for this sample’s licensing information.
3 |
4 | Abstract:
5 | The model for a hike.
6 | */
7 |
8 | import SwiftUI
9 |
10 | struct Hike: Codable, Hashable, Identifiable {
11 | var name: String
12 | var id: Int
13 | var distance: Double
14 | var difficulty: Int
15 | var observations: [Observation]
16 |
17 | static var formatter = LengthFormatter()
18 |
19 | var distanceText: String {
20 | Hike.formatter
21 | .string(fromValue: self.distance, unit: .kilometer)
22 | }
23 |
24 | struct Observation: Codable, Hashable {
25 | var distanceFromStart: Double
26 |
27 | var elevation: Range
28 | var pace: Range
29 | var heartRate: Range
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/AppleSwiftUITutorials/Landmarks/Models/Landmark.swift:
--------------------------------------------------------------------------------
1 | /*
2 | See LICENSE folder for this sample’s licensing information.
3 |
4 | Abstract:
5 | The model for an individual landmark.
6 | */
7 |
8 | import CoreLocation
9 | import SwiftUI
10 |
11 | struct Landmark: Hashable, Codable, Identifiable {
12 | var id: Int
13 | var name: String
14 | fileprivate var imageName: String
15 | fileprivate var coordinates: Coordinates
16 | var state: String
17 | var park: String
18 | var category: Category
19 | var isFavorite: Bool
20 | var isFeatured: Bool
21 |
22 | var locationCoordinate: CLLocationCoordinate2D {
23 | CLLocationCoordinate2D(
24 | latitude: self.coordinates.latitude,
25 | longitude: self.coordinates.longitude
26 | )
27 | }
28 |
29 | var featureImage: Image? {
30 | guard self.isFeatured else { return nil }
31 |
32 | return Image(
33 | ImageStore.loadImage(name: "\(self.imageName)_feature"),
34 | scale: 2,
35 | label: Text(self.name)
36 | )
37 | }
38 |
39 | enum Category: String, CaseIterable, Codable, Hashable {
40 | case featured = "Featured"
41 | case lakes = "Lakes"
42 | case rivers = "Rivers"
43 | case mountains = "Mountains"
44 | }
45 | }
46 |
47 | extension Landmark {
48 | var image: Image {
49 | ImageStore.shared.image(name: self.imageName)
50 | }
51 | }
52 |
53 | struct Coordinates: Hashable, Codable {
54 | var latitude: Double
55 | var longitude: Double
56 | }
57 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/AppleSwiftUITutorials/Landmarks/Models/Profile.swift:
--------------------------------------------------------------------------------
1 | /*
2 | See LICENSE folder for this sample’s licensing information.
3 |
4 | Abstract:
5 | An object that models a user profile.
6 | */
7 | import Foundation
8 |
9 | struct Profile {
10 | var username: String
11 | var prefersNotifications: Bool
12 | var seasonalPhoto: Season
13 | var goalDate: Date
14 |
15 | static let `default` = Self(username: "g_kumar", prefersNotifications: true, seasonalPhoto: .winter)
16 |
17 | init(username: String, prefersNotifications: Bool = true, seasonalPhoto: Season = .winter) {
18 | self.username = username
19 | self.prefersNotifications = prefersNotifications
20 | self.seasonalPhoto = seasonalPhoto
21 | self.goalDate = Date()
22 | }
23 |
24 | enum Season: String, CaseIterable {
25 | case spring = "🌷"
26 | case summer = "🌞"
27 | case autumn = "🍂"
28 | case winter = "☃️"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/AppleSwiftUITutorials/Landmarks/Models/UserData.swift:
--------------------------------------------------------------------------------
1 | /*
2 | See LICENSE folder for this sample’s licensing information.
3 |
4 | Abstract:
5 | A model object that stores app data.
6 | */
7 |
8 | import Combine
9 | import SwiftUI
10 |
11 | final class UserData: ObservableObject {
12 | @Published var showFavoritesOnly = false
13 | @Published var landmarks = landmarkData
14 | @Published var profile = Profile.default
15 | }
16 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/AppleSwiftUITutorials/Landmarks/Profile/ProfileEditor.swift:
--------------------------------------------------------------------------------
1 | /*
2 | See LICENSE folder for this sample’s licensing information.
3 |
4 | Abstract:
5 | An editable profile view.
6 | */
7 |
8 | import SwiftUI
9 |
10 | struct ProfileEditor: View {
11 | @Binding var profile: Profile
12 |
13 | var dateRange: ClosedRange {
14 | let min = Calendar.current.date(byAdding: .year, value: -1, to: self.profile.goalDate)!
15 | let max = Calendar.current.date(byAdding: .year, value: 1, to: self.profile.goalDate)!
16 | return min ... max
17 | }
18 |
19 | var body: some View {
20 | List {
21 | HStack {
22 | Text("Username").bold()
23 | Divider()
24 | TextField("Username", text: $profile.username)
25 | }
26 |
27 | Toggle(isOn: $profile.prefersNotifications) {
28 | Text("Enable Notifications")
29 | }
30 |
31 | VStack(alignment: .leading, spacing: 20) {
32 | Text("Seasonal Photo").bold()
33 |
34 | Picker("Seasonal Photo", selection: $profile.seasonalPhoto) {
35 | ForEach(Profile.Season.allCases, id: \.self) { season in
36 | Text(season.rawValue).tag(season)
37 | }
38 | }
39 | .pickerStyle(SegmentedPickerStyle())
40 | }
41 | .padding(.top)
42 |
43 | VStack(alignment: .leading, spacing: 20) {
44 | Text("Goal Date").bold()
45 | DatePicker(
46 | "Goal Date",
47 | selection: $profile.goalDate,
48 | in: dateRange,
49 | displayedComponents: .date
50 | )
51 | }
52 | .padding(.top)
53 | }
54 | }
55 | }
56 |
57 | struct ProfileEditor_Previews: PreviewProvider {
58 | static var previews: some View {
59 | ProfileEditor(profile: .constant(.default))
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/AppleSwiftUITutorials/Landmarks/Profile/ProfileHost.swift:
--------------------------------------------------------------------------------
1 | /*
2 | See LICENSE folder for this sample’s licensing information.
3 |
4 | Abstract:
5 | A view that hosts the profile viewer and editor.
6 | */
7 |
8 | import SwiftUI
9 |
10 | struct ProfileHost: View {
11 | @Environment(\.editMode) var mode
12 | @EnvironmentObject var userData: UserData
13 | @State var draftProfile = Profile.default
14 |
15 | var body: some View {
16 | VStack(alignment: .leading, spacing: 20) {
17 | HStack {
18 | if self.mode?.wrappedValue == .active {
19 | Button("Cancel") {
20 | self.draftProfile = self.userData.profile
21 | self.mode?.animation().wrappedValue = .inactive
22 | }
23 | }
24 |
25 | Spacer()
26 |
27 | EditButton()
28 | }
29 | if self.mode?.wrappedValue == .inactive {
30 | ProfileSummary(profile: userData.profile)
31 | } else {
32 | ProfileEditor(profile: $draftProfile)
33 | .onAppear {
34 | self.draftProfile = self.userData.profile
35 | }
36 | .onDisappear {
37 | self.userData.profile = self.draftProfile
38 | }
39 | }
40 | }
41 | .padding()
42 | }
43 | }
44 |
45 | struct ProfileHost_Previews: PreviewProvider {
46 | static var previews: some View {
47 | ProfileHost().environmentObject(UserData())
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/AppleSwiftUITutorials/Landmarks/Profile/ProfileSummary.swift:
--------------------------------------------------------------------------------
1 | /*
2 | See LICENSE folder for this sample’s licensing information.
3 |
4 | Abstract:
5 | A view that summarizes a profile.
6 | */
7 |
8 | import SwiftUI
9 |
10 | struct ProfileSummary: View {
11 | var profile: Profile
12 |
13 | static let goalFormat: DateFormatter = {
14 | let formatter = DateFormatter()
15 | formatter.dateStyle = .long
16 | formatter.timeStyle = .none
17 | return formatter
18 | }()
19 |
20 | var body: some View {
21 | List {
22 | Text(profile.username)
23 | .bold()
24 | .font(.title)
25 |
26 | Text("Notifications: \(self.profile.prefersNotifications ? "On" : "Off")")
27 |
28 | Text("Seasonal Photos: \(self.profile.seasonalPhoto.rawValue)")
29 |
30 | Text("Goal Date: \(self.profile.goalDate, formatter: Self.goalFormat)")
31 |
32 | VStack(alignment: .leading) {
33 | Text("Completed Badges")
34 | .font(.headline)
35 | ScrollView {
36 | HStack {
37 | HikeBadge(name: "First Hike")
38 |
39 | HikeBadge(name: "Earth Day")
40 | .hueRotation(Angle(degrees: 90))
41 | HikeBadge(name: "Tenth Hike")
42 | .grayscale(0.5)
43 | .hueRotation(Angle(degrees: 45))
44 | }
45 | }
46 | .frame(height: 140)
47 | }
48 |
49 | VStack(alignment: .leading) {
50 | Text("Recent Hikes")
51 | .font(.headline)
52 |
53 | HikeView(hike: hikeData[0])
54 | }
55 | }
56 | }
57 | }
58 |
59 | struct ProfileSummary_Previews: PreviewProvider {
60 | static var previews: some View {
61 | ProfileSummary(profile: Profile.default)
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/AppleSwiftUITutorials/Landmarks/Resources/charleyrivers.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralfebert/SwiftUIPlayground/b9c3533aba05fa5412dcddcf37358e8960ccab72/SwiftUIPlayground/AppleSwiftUITutorials/Landmarks/Resources/charleyrivers.jpg
--------------------------------------------------------------------------------
/SwiftUIPlayground/AppleSwiftUITutorials/Landmarks/Resources/charleyrivers_feature.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralfebert/SwiftUIPlayground/b9c3533aba05fa5412dcddcf37358e8960ccab72/SwiftUIPlayground/AppleSwiftUITutorials/Landmarks/Resources/charleyrivers_feature.jpg
--------------------------------------------------------------------------------
/SwiftUIPlayground/AppleSwiftUITutorials/Landmarks/Resources/chilkoottrail.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralfebert/SwiftUIPlayground/b9c3533aba05fa5412dcddcf37358e8960ccab72/SwiftUIPlayground/AppleSwiftUITutorials/Landmarks/Resources/chilkoottrail.jpg
--------------------------------------------------------------------------------
/SwiftUIPlayground/AppleSwiftUITutorials/Landmarks/Resources/chincoteague.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralfebert/SwiftUIPlayground/b9c3533aba05fa5412dcddcf37358e8960ccab72/SwiftUIPlayground/AppleSwiftUITutorials/Landmarks/Resources/chincoteague.jpg
--------------------------------------------------------------------------------
/SwiftUIPlayground/AppleSwiftUITutorials/Landmarks/Resources/hiddenlake.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralfebert/SwiftUIPlayground/b9c3533aba05fa5412dcddcf37358e8960ccab72/SwiftUIPlayground/AppleSwiftUITutorials/Landmarks/Resources/hiddenlake.jpg
--------------------------------------------------------------------------------
/SwiftUIPlayground/AppleSwiftUITutorials/Landmarks/Resources/icybay.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralfebert/SwiftUIPlayground/b9c3533aba05fa5412dcddcf37358e8960ccab72/SwiftUIPlayground/AppleSwiftUITutorials/Landmarks/Resources/icybay.jpg
--------------------------------------------------------------------------------
/SwiftUIPlayground/AppleSwiftUITutorials/Landmarks/Resources/lakemcdonald.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralfebert/SwiftUIPlayground/b9c3533aba05fa5412dcddcf37358e8960ccab72/SwiftUIPlayground/AppleSwiftUITutorials/Landmarks/Resources/lakemcdonald.jpg
--------------------------------------------------------------------------------
/SwiftUIPlayground/AppleSwiftUITutorials/Landmarks/Resources/landmarkData.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "name": "Turtle Rock",
4 | "category": "Rivers",
5 | "city": "Twentynine Palms",
6 | "state": "California",
7 | "id": 1001,
8 | "isFeatured": true,
9 | "isFavorite": true,
10 | "park": "Joshua Tree National Park",
11 | "coordinates": {
12 | "longitude": -116.166868,
13 | "latitude": 34.011286
14 | },
15 | "imageName": "turtlerock"
16 | },
17 | {
18 | "name": "Silver Salmon Creek",
19 | "category": "Lakes",
20 | "city": "Port Alsworth",
21 | "state": "Alaska",
22 | "id": 1002,
23 | "isFeatured": false,
24 | "isFavorite": false,
25 | "park": "Lake Clark National Park and Preserve",
26 | "coordinates": {
27 | "longitude": -152.665167,
28 | "latitude": 59.980167
29 | },
30 | "imageName": "silversalmoncreek"
31 | },
32 | {
33 | "name": "Chilkoot Trail",
34 | "category": "Mountains",
35 | "city": "Skagway",
36 | "state": "Alaska",
37 | "id": 1003,
38 | "isFeatured": false,
39 | "isFavorite": true,
40 | "park": "Klondike Gold Rush National Historical Park",
41 | "coordinates": {
42 | "longitude": -135.334571,
43 | "latitude": 59.560551
44 | },
45 | "imageName": "chilkoottrail"
46 | },
47 | {
48 | "name": "St. Mary Lake",
49 | "category": "Lakes",
50 | "city": "Browning",
51 | "state": "Montana",
52 | "id": 1004,
53 | "isFeatured": true,
54 | "isFavorite": true,
55 | "park": "Glacier National Park",
56 | "coordinates": {
57 | "longitude": -113.536248,
58 | "latitude": 48.69423
59 | },
60 | "imageName": "stmarylake"
61 | },
62 | {
63 | "name": "Twin Lake",
64 | "category": "Lakes",
65 | "city": "Twin Lakes",
66 | "state": "Alaska",
67 | "id": 1005,
68 | "isFeatured": false,
69 | "isFavorite": false,
70 | "park": "Lake Clark National Park and Preserve",
71 | "coordinates": {
72 | "longitude": -153.849883,
73 | "latitude": 60.641684
74 | },
75 | "imageName": "twinlake"
76 | },
77 | {
78 | "name": "Lake McDonald",
79 | "category": "Mountains",
80 | "city": "West Glacier",
81 | "state": "Montana",
82 | "id": 1006,
83 | "isFeatured": false,
84 | "isFavorite": false,
85 | "park": "Glacier National Park",
86 | "coordinates": {
87 | "longitude": -113.934831,
88 | "latitude": 48.56002
89 | },
90 | "imageName": "lakemcdonald"
91 | },
92 | {
93 | "name": "Charley Rivers",
94 | "category": "Rivers",
95 | "city": "Eaking",
96 | "state": "Alaska",
97 | "id": 1007,
98 | "isFeatured": true,
99 | "isFavorite": false,
100 | "park": "Charley Rivers National Preserve",
101 | "coordinates": {
102 | "longitude": -143.122586,
103 | "latitude": 65.350021
104 | },
105 | "imageName": "charleyrivers",
106 | },
107 | {
108 | "name": "Icy Bay",
109 | "category": "Mountains",
110 | "city": "Icy Bay",
111 | "state": "Alaska",
112 | "id": 1008,
113 | "isFeatured": false,
114 | "isFavorite": false,
115 | "park": "Wrangell-St. Elias National Park and Preserve",
116 | "coordinates": {
117 | "longitude": -141.518167,
118 | "latitude": 60.089917
119 | },
120 | "imageName": "icybay"
121 | },
122 | {
123 | "name": "Rainbow Lake",
124 | "category": "Lakes",
125 | "city": "Willow",
126 | "state": "Alaska",
127 | "id": 1009,
128 | "isFeatured": false,
129 | "isFavorite": false,
130 | "park": "State Recreation Area",
131 | "coordinates": {
132 | "longitude": -150.086103,
133 | "latitude": 61.694334
134 | },
135 | "imageName": "rainbowlake"
136 | },
137 | {
138 | "name": "Hidden Lake",
139 | "category": "Lakes",
140 | "city": "Newhalem",
141 | "state": "Washington",
142 | "id": 1010,
143 | "isFeatured": false,
144 | "isFavorite": false,
145 | "park": "North Cascades National Park",
146 | "coordinates": {
147 | "longitude": -121.17799,
148 | "latitude": 48.495442
149 | },
150 | "imageName": "hiddenlake"
151 | },
152 | {
153 | "name": "Chincoteague",
154 | "category": "Rivers",
155 | "city": "Chincoteague",
156 | "state": "Virginia",
157 | "id": 1011,
158 | "isFeatured": false,
159 | "isFavorite": false,
160 | "park": "Chincoteague National Wildlife Refuge",
161 | "coordinates": {
162 | "longitude": -75.383212,
163 | "latitude": 37.91531
164 | },
165 | "imageName": "chincoteague"
166 | },
167 | {
168 | "name": "Lake Umbagog",
169 | "category": "Lakes",
170 | "city": "Errol",
171 | "state": "New Hampshire",
172 | "id": 1012,
173 | "isFeatured": false,
174 | "isFavorite": false,
175 | "park": "Umbagog National Wildlife Refuge",
176 | "coordinates": {
177 | "longitude": -71.056816,
178 | "latitude": 44.747408
179 | },
180 | "imageName": "umbagog"
181 | }
182 | ]
183 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/AppleSwiftUITutorials/Landmarks/Resources/rainbowlake.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralfebert/SwiftUIPlayground/b9c3533aba05fa5412dcddcf37358e8960ccab72/SwiftUIPlayground/AppleSwiftUITutorials/Landmarks/Resources/rainbowlake.jpg
--------------------------------------------------------------------------------
/SwiftUIPlayground/AppleSwiftUITutorials/Landmarks/Resources/silversalmoncreek.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralfebert/SwiftUIPlayground/b9c3533aba05fa5412dcddcf37358e8960ccab72/SwiftUIPlayground/AppleSwiftUITutorials/Landmarks/Resources/silversalmoncreek.jpg
--------------------------------------------------------------------------------
/SwiftUIPlayground/AppleSwiftUITutorials/Landmarks/Resources/stmarylake.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralfebert/SwiftUIPlayground/b9c3533aba05fa5412dcddcf37358e8960ccab72/SwiftUIPlayground/AppleSwiftUITutorials/Landmarks/Resources/stmarylake.jpg
--------------------------------------------------------------------------------
/SwiftUIPlayground/AppleSwiftUITutorials/Landmarks/Resources/stmarylake_feature.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralfebert/SwiftUIPlayground/b9c3533aba05fa5412dcddcf37358e8960ccab72/SwiftUIPlayground/AppleSwiftUITutorials/Landmarks/Resources/stmarylake_feature.jpg
--------------------------------------------------------------------------------
/SwiftUIPlayground/AppleSwiftUITutorials/Landmarks/Resources/turtlerock.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralfebert/SwiftUIPlayground/b9c3533aba05fa5412dcddcf37358e8960ccab72/SwiftUIPlayground/AppleSwiftUITutorials/Landmarks/Resources/turtlerock.jpg
--------------------------------------------------------------------------------
/SwiftUIPlayground/AppleSwiftUITutorials/Landmarks/Resources/turtlerock_feature.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralfebert/SwiftUIPlayground/b9c3533aba05fa5412dcddcf37358e8960ccab72/SwiftUIPlayground/AppleSwiftUITutorials/Landmarks/Resources/turtlerock_feature.jpg
--------------------------------------------------------------------------------
/SwiftUIPlayground/AppleSwiftUITutorials/Landmarks/Resources/twinlake.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralfebert/SwiftUIPlayground/b9c3533aba05fa5412dcddcf37358e8960ccab72/SwiftUIPlayground/AppleSwiftUITutorials/Landmarks/Resources/twinlake.jpg
--------------------------------------------------------------------------------
/SwiftUIPlayground/AppleSwiftUITutorials/Landmarks/Resources/umbagog.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralfebert/SwiftUIPlayground/b9c3533aba05fa5412dcddcf37358e8960ccab72/SwiftUIPlayground/AppleSwiftUITutorials/Landmarks/Resources/umbagog.jpg
--------------------------------------------------------------------------------
/SwiftUIPlayground/AppleSwiftUITutorials/Landmarks/Supporting Views/Badge.swift:
--------------------------------------------------------------------------------
1 | /*
2 | See LICENSE folder for this sample’s licensing information.
3 |
4 | Abstract:
5 | A view that displays a badge.
6 | */
7 |
8 | import SwiftUI
9 |
10 | struct Badge: View {
11 | static let rotationCount = 8
12 |
13 | var badgeSymbols: some View {
14 | ForEach(0 ..< Badge.rotationCount) { i in
15 | RotatedBadgeSymbol(
16 | angle: .degrees(Double(i) / Double(Badge.rotationCount)) * 360.0)
17 | }
18 | .opacity(0.5)
19 | }
20 |
21 | var body: some View {
22 | ZStack {
23 | BadgeBackground()
24 |
25 | GeometryReader { geometry in
26 | self.badgeSymbols
27 | .scaleEffect(1.0 / 4.0, anchor: .top)
28 | .position(x: geometry.size.width / 2.0, y: (3.0 / 4.0) * geometry.size.height)
29 | }
30 | }
31 | .scaledToFit()
32 | }
33 | }
34 |
35 | struct Badge_Previews: PreviewProvider {
36 | static var previews: some View {
37 | Badge()
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/AppleSwiftUITutorials/Landmarks/Supporting Views/BadgeBackground.swift:
--------------------------------------------------------------------------------
1 | /*
2 | See LICENSE folder for this sample’s licensing information.
3 |
4 | Abstract:
5 | A view that displays the background of a badge.
6 | */
7 |
8 | import SwiftUI
9 |
10 | struct BadgeBackground: View {
11 | var body: some View {
12 | GeometryReader { geometry in
13 | Path { path in
14 | var width: CGFloat = min(geometry.size.width, geometry.size.height)
15 | let height = width
16 | let xScale: CGFloat = 0.832
17 | let xOffset = (width * (1.0 - xScale)) / 2.0
18 | width *= xScale
19 | path.move(
20 | to: CGPoint(
21 | x: xOffset + width * 0.95,
22 | y: height * (0.20 + HexagonParameters.adjustment)
23 | )
24 | )
25 |
26 | HexagonParameters.points.forEach {
27 | path.addLine(
28 | to: CGPoint(
29 | x: xOffset + width * $0.useWidth.0 * $0.xFactors.0,
30 | y: height * $0.useHeight.0 * $0.yFactors.0
31 | )
32 | )
33 |
34 | path.addQuadCurve(
35 | to: CGPoint(
36 | x: xOffset + width * $0.useWidth.1 * $0.xFactors.1,
37 | y: height * $0.useHeight.1 * $0.yFactors.1
38 | ),
39 | control: CGPoint(
40 | x: xOffset + width * $0.useWidth.2 * $0.xFactors.2,
41 | y: height * $0.useHeight.2 * $0.yFactors.2
42 | )
43 | )
44 | }
45 | }
46 | .fill(LinearGradient(
47 | gradient: Gradient(
48 | colors: [Self.gradientStart, Self.gradientEnd]),
49 | startPoint: UnitPoint(x: 0.5, y: 0),
50 | endPoint: UnitPoint(x: 0.5, y: 0.6)
51 | ))
52 | .aspectRatio(1, contentMode: .fit)
53 | }
54 | }
55 |
56 | static let gradientStart = Color(red: 239.0 / 255, green: 120.0 / 255, blue: 221.0 / 255)
57 | static let gradientEnd = Color(red: 239.0 / 255, green: 172.0 / 255, blue: 120.0 / 255)
58 | }
59 |
60 | struct BadgeBackground_Previews: PreviewProvider {
61 | static var previews: some View {
62 | BadgeBackground()
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/AppleSwiftUITutorials/Landmarks/Supporting Views/BadgeSymbol.swift:
--------------------------------------------------------------------------------
1 | /*
2 | See LICENSE folder for this sample’s licensing information.
3 |
4 | Abstract:
5 | A view that display a symbol in a badge.
6 | */
7 |
8 | import SwiftUI
9 |
10 | struct BadgeSymbol: View {
11 | static let symbolColor = Color(red: 79.0 / 255, green: 79.0 / 255, blue: 191.0 / 255)
12 |
13 | var body: some View {
14 | GeometryReader { geometry in
15 | Path { path in
16 | let width = min(geometry.size.width, geometry.size.height)
17 | let height = width * 0.75
18 | let spacing = width * 0.030
19 | let middle = width / 2
20 | let topWidth = 0.226 * width
21 | let topHeight = 0.488 * height
22 |
23 | path.addLines([
24 | CGPoint(x: middle, y: spacing),
25 | CGPoint(x: middle - topWidth, y: topHeight - spacing),
26 | CGPoint(x: middle, y: topHeight / 2 + spacing),
27 | CGPoint(x: middle + topWidth, y: topHeight - spacing),
28 | CGPoint(x: middle, y: spacing),
29 | ])
30 |
31 | path.move(to: CGPoint(x: middle, y: topHeight / 2 + spacing * 3))
32 | path.addLines([
33 | CGPoint(x: middle - topWidth, y: topHeight + spacing),
34 | CGPoint(x: spacing, y: height - spacing),
35 | CGPoint(x: width - spacing, y: height - spacing),
36 | CGPoint(x: middle + topWidth, y: topHeight + spacing),
37 | CGPoint(x: middle, y: topHeight / 2 + spacing * 3),
38 | ])
39 | }
40 | .fill(Self.symbolColor)
41 | }
42 | }
43 | }
44 |
45 | struct BadgeSymbol_Previews: PreviewProvider {
46 | static var previews: some View {
47 | BadgeSymbol()
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/AppleSwiftUITutorials/Landmarks/Supporting Views/CircleImage.swift:
--------------------------------------------------------------------------------
1 | /*
2 | See LICENSE folder for this sample’s licensing information.
3 |
4 | Abstract:
5 | A view that clips an image to a circle and adds a stroke and shadow.
6 | */
7 |
8 | import SwiftUI
9 |
10 | struct CircleImage: View {
11 | var image: Image
12 |
13 | var body: some View {
14 | image
15 | .clipShape(Circle())
16 | .overlay(Circle().stroke(Color.white, lineWidth: 4))
17 | .shadow(radius: 10)
18 | }
19 | }
20 |
21 | struct CircleImage_Previews: PreviewProvider {
22 | static var previews: some View {
23 | CircleImage(image: Image("turtlerock"))
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/AppleSwiftUITutorials/Landmarks/Supporting Views/FeatureCard.swift:
--------------------------------------------------------------------------------
1 | /*
2 | See LICENSE folder for this sample’s licensing information.
3 |
4 | Abstract:
5 | A view that shows a featured landmark.
6 | */
7 |
8 | import SwiftUI
9 |
10 | struct FeatureCard: View {
11 | var landmark: Landmark
12 |
13 | var body: some View {
14 | landmark.featureImage?
15 | .resizable()
16 | .aspectRatio(3 / 2, contentMode: .fit)
17 | .overlay(TextOverlay(landmark: landmark))
18 | }
19 | }
20 |
21 | struct TextOverlay: View {
22 | var landmark: Landmark
23 |
24 | var gradient: LinearGradient {
25 | LinearGradient(
26 | gradient: Gradient(
27 | colors: [Color.black.opacity(0.6), Color.black.opacity(0)]),
28 | startPoint: .bottom,
29 | endPoint: .center
30 | )
31 | }
32 |
33 | var body: some View {
34 | ZStack(alignment: .bottomLeading) {
35 | Rectangle().fill(gradient)
36 | VStack(alignment: .leading) {
37 | Text(landmark.name)
38 | .font(.title)
39 | .bold()
40 | Text(landmark.park)
41 | }
42 | .padding()
43 | }
44 | .foregroundColor(.white)
45 | }
46 | }
47 |
48 | struct FeatureCard_Previews: PreviewProvider {
49 | static var previews: some View {
50 | FeatureCard(landmark: features[0])
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/AppleSwiftUITutorials/Landmarks/Supporting Views/MapView.swift:
--------------------------------------------------------------------------------
1 | /*
2 | See LICENSE folder for this sample’s licensing information.
3 |
4 | Abstract:
5 | A view that hosts an `MKMapView`.
6 | */
7 |
8 | import MapKit
9 | import SwiftUI
10 |
11 | struct MapView: UIViewRepresentable {
12 | var coordinate: CLLocationCoordinate2D
13 |
14 | func makeUIView(context: Context) -> MKMapView {
15 | MKMapView(frame: .zero)
16 | }
17 |
18 | func updateUIView(_ uiView: MKMapView, context: Context) {
19 | let span = MKCoordinateSpan(latitudeDelta: 0.02, longitudeDelta: 0.02)
20 | let region = MKCoordinateRegion(center: coordinate, span: span)
21 | uiView.setRegion(region, animated: true)
22 | }
23 | }
24 |
25 | struct MapView_Previews: PreviewProvider {
26 | static var previews: some View {
27 | MapView(coordinate: landmarkData[0].locationCoordinate)
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/AppleSwiftUITutorials/Landmarks/Supporting Views/RotatedBadgeSymbol.swift:
--------------------------------------------------------------------------------
1 | /*
2 | See LICENSE folder for this sample’s licensing information.
3 |
4 | Abstract:
5 | A view that displays a rotated version of a badge symbol.
6 | */
7 |
8 | import SwiftUI
9 |
10 | struct RotatedBadgeSymbol: View {
11 | let angle: Angle
12 |
13 | var body: some View {
14 | BadgeSymbol()
15 | .padding(-60)
16 | .rotationEffect(angle, anchor: .bottom)
17 | }
18 | }
19 |
20 | struct RotatedBadgeSymbol_Previews: PreviewProvider {
21 | static var previews: some View {
22 | RotatedBadgeSymbol(angle: Angle(degrees: 5))
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/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 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Assets.xcassets/ButtonColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0.000",
9 | "green" : "0.578",
10 | "red" : "1.000"
11 | }
12 | },
13 | "idiom" : "universal"
14 | },
15 | {
16 | "appearances" : [
17 | {
18 | "appearance" : "luminosity",
19 | "value" : "dark"
20 | }
21 | ],
22 | "color" : {
23 | "color-space" : "srgb",
24 | "components" : {
25 | "alpha" : "1.000",
26 | "blue" : "0x00",
27 | "green" : "0x11",
28 | "red" : "0xBA"
29 | }
30 | },
31 | "idiom" : "universal"
32 | }
33 | ],
34 | "info" : {
35 | "author" : "xcode",
36 | "version" : 1
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Assets.xcassets/ButtonColorGradient.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0.291",
9 | "green" : "0.434",
10 | "red" : "0.919"
11 | }
12 | },
13 | "idiom" : "universal"
14 | },
15 | {
16 | "appearances" : [
17 | {
18 | "appearance" : "luminosity",
19 | "value" : "dark"
20 | }
21 | ],
22 | "color" : {
23 | "color-space" : "srgb",
24 | "components" : {
25 | "alpha" : "1.000",
26 | "blue" : "0x00",
27 | "green" : "0x11",
28 | "red" : "0x6A"
29 | }
30 | },
31 | "idiom" : "universal"
32 | }
33 | ],
34 | "info" : {
35 | "author" : "xcode",
36 | "version" : 1
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Assets.xcassets/cookies.imageset/1599px-Chocolate_chip_cookies.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralfebert/SwiftUIPlayground/b9c3533aba05fa5412dcddcf37358e8960ccab72/SwiftUIPlayground/Assets.xcassets/cookies.imageset/1599px-Chocolate_chip_cookies.jpg
--------------------------------------------------------------------------------
/SwiftUIPlayground/Assets.xcassets/cookies.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "1599px-Chocolate_chip_cookies.jpg",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Assets.xcassets/turtlerock.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "turtlerock.jpg",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Assets.xcassets/turtlerock.imageset/turtlerock.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralfebert/SwiftUIPlayground/b9c3533aba05fa5412dcddcf37358e8960ccab72/SwiftUIPlayground/Assets.xcassets/turtlerock.imageset/turtlerock.jpg
--------------------------------------------------------------------------------
/SwiftUIPlayground/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/ContentView.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import SwiftUI
5 |
6 | /// Clip image to square in SwiftUI
7 | /// https://stackoverflow.com/questions/58290963/clip-image-to-square-in-swiftui?rq=1
8 | struct ClipImageView: View {
9 | var body: some View {
10 | HStack {
11 | ForEach(0 ..< 3, id: \.self) { _ in
12 | ZStack {
13 | Image("cookies")
14 | .resizable()
15 | .aspectRatio(contentMode: .fill)
16 | .layoutPriority(-1)
17 | VStack {
18 | Spacer()
19 | Text("Cookie")
20 | .frame(minWidth: 0, maxWidth: .infinity)
21 | .background(Color.white)
22 | }
23 | }
24 | .clipped()
25 | .aspectRatio(1, contentMode: .fit)
26 | .border(Color.red)
27 | }
28 | }
29 | }
30 | }
31 |
32 | struct ContentView_Previews: PreviewProvider {
33 | static var previews: some View {
34 | ClipImageView()
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Convenience/Array+Indexed.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | struct IndexedCollection: RandomAccessCollection {
5 | typealias Index = Base.Index
6 | typealias Element = (index: Index, element: Base.Element)
7 | let base: Base
8 | var startIndex: Index { self.base.startIndex }
9 | var endIndex: Index { self.base.endIndex }
10 | func index(after i: Index) -> Index {
11 | self.base.index(after: i)
12 | }
13 |
14 | func index(before i: Index) -> Index {
15 | self.base.index(before: i)
16 | }
17 |
18 | func index(_ i: Index, offsetBy distance: Int) -> Index {
19 | self.base.index(i, offsetBy: distance)
20 | }
21 |
22 | subscript(position: Index) -> Element {
23 | (index: position, element: self.base[position])
24 | }
25 | }
26 |
27 | extension RandomAccessCollection {
28 | func indexed() -> IndexedCollection {
29 | IndexedCollection(base: self)
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Convenience/Date+Extensions.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import Foundation
5 |
6 | extension Date {
7 |
8 | init(year: Int, month: Int, day: Int) {
9 | var dateComponents = DateComponents()
10 | dateComponents.year = year
11 | dateComponents.month = month
12 | dateComponents.day = day
13 | self = Calendar.current.date(from: dateComponents)!
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | NSCameraUsageDescription
6 | Scan barcodes
7 | CFBundleDevelopmentRegion
8 | $(DEVELOPMENT_LANGUAGE)
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | UIApplicationSceneManifest
26 |
27 | UIApplicationSupportsMultipleScenes
28 |
29 | UISceneConfigurations
30 |
31 | UIWindowSceneSessionRoleApplication
32 |
33 |
34 | UISceneConfigurationName
35 | Default Configuration
36 | UISceneDelegateClassName
37 | $(PRODUCT_MODULE_NAME).SceneDelegate
38 |
39 |
40 |
41 |
42 | UILaunchStoryboardName
43 | LaunchScreen
44 | UIRequiredDeviceCapabilities
45 |
46 | armv7
47 |
48 | UISupportedInterfaceOrientations
49 |
50 | UIInterfaceOrientationPortrait
51 | UIInterfaceOrientationLandscapeLeft
52 | UIInterfaceOrientationLandscapeRight
53 |
54 | UISupportedInterfaceOrientations~ipad
55 |
56 | UIInterfaceOrientationPortrait
57 | UIInterfaceOrientationPortraitUpsideDown
58 | UIInterfaceOrientationLandscapeLeft
59 | UIInterfaceOrientationLandscapeRight
60 |
61 | UIStatusBarHidden
62 |
63 | UIViewControllerBasedStatusBarAppearance
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Models/CoreDataStack.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import CoreData
5 | import Foundation
6 |
7 | class CoreDataStack {
8 |
9 | public static var shared = CoreDataStack()
10 |
11 | private init() {}
12 |
13 | lazy var persistentContainer: NSPersistentContainer = {
14 | /*
15 | The persistent container for the application. This implementation
16 | creates and returns a container, having loaded the store for the
17 | application to it. This property is optional since there are legitimate
18 | error conditions that could cause the creation of the store to fail.
19 | */
20 | let container = NSPersistentContainer(name: "PlaygroundModel")
21 | container.loadPersistentStores(completionHandler: { _, error in
22 | if let error = error as NSError? {
23 | // Replace this implementation with code to handle the error appropriately.
24 | // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
25 |
26 | /*
27 | Typical reasons for an error here include:
28 | * The parent directory does not exist, cannot be created, or disallows writing.
29 | * The persistent store is not accessible, due to permissions or data protection when the device is locked.
30 | * The device is out of space.
31 | * The store could not be migrated to the current model version.
32 | Check the error message to determine what the actual problem was.
33 | */
34 | fatalError("Unresolved error \(error), \(error.userInfo)")
35 | }
36 | })
37 | return container
38 | }()
39 |
40 | // MARK: - Core Data Saving support
41 |
42 | func saveContext() {
43 | let context = self.persistentContainer.viewContext
44 | if context.hasChanges {
45 | do {
46 | try context.save()
47 | } catch {
48 | // Replace this implementation with code to handle the error appropriately.
49 | // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
50 | let nserror = error as NSError
51 | fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
52 | }
53 | }
54 | }
55 |
56 | }
57 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Models/PlaygroundModel.xcdatamodeld/PlaygroundModel.xcdatamodel/contents:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Models/Todo+CoreDataClass.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import CoreData
5 | import Foundation
6 |
7 | @objc(Todo)
8 | public class Todo: NSManagedObject {
9 |
10 | static var allTodos: NSFetchRequest {
11 | let request: NSFetchRequest = Todo.fetchRequest()
12 | request.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)]
13 | return request
14 | }
15 |
16 | static func create(in ctx: NSManagedObjectContext) -> Todo {
17 | NSEntityDescription.insertNewObject(forEntityName: "Todo", into: ctx) as! Todo
18 | }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/SwiftUIPlayground.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import SwiftUI
5 |
6 | @main
7 | struct SwiftUIPlayground: App {
8 | var body: some Scene {
9 | WindowGroup {
10 | MainView()
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/AccountView.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import ImagePickerView
5 | import SwiftUI
6 |
7 | /// https://stackoverflow.com/questions/61844491/how-can-i-create-an-unfilled-frame-in-swiftui
8 | struct AccountView: View {
9 | var body: some View {
10 | VStack(alignment: .leading, spacing: 8) {
11 | Text("Account Balance")
12 | .font(.largeTitle)
13 | Text("Account Name")
14 | .font(.headline)
15 | HStack(spacing: 0) {
16 | Text("There are ")
17 | .font(.caption)
18 | .foregroundColor(.gray)
19 | Text("6 ")
20 | .font(.caption)
21 | .fontWeight(.bold)
22 | .foregroundColor(.blue)
23 | Text("unreconciled transactions.")
24 | .font(.caption)
25 | .foregroundColor(.gray)
26 | }
27 | }
28 | .padding(20)
29 | .background(Color.white)
30 | .overlay(
31 | RoundedRectangle(cornerRadius: 20)
32 | .stroke(Color.blue, lineWidth: 2))
33 | }
34 |
35 | }
36 |
37 | struct AccountView_Previews: PreviewProvider {
38 | static var previews: some View {
39 | AccountView()
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/ActivityIndicatorView.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import SwiftUI
5 |
6 | struct ActivityIndicatorView: View {
7 |
8 | @State private var progress = 0.5
9 |
10 | var body: some View {
11 |
12 | VStack(spacing: 20) {
13 | ProgressView()
14 | ProgressView(value: progress)
15 |
16 | Button("More") {
17 | withAnimation {
18 | progress += 0.05
19 | }
20 | }
21 | }
22 |
23 | }
24 |
25 | }
26 |
27 | struct ActivityIndicator_Previews: PreviewProvider {
28 | static var previews: some View {
29 | ActivityIndicatorView()
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/AlertExampleView.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import SwiftUI
5 |
6 | struct AlertExampleView: View {
7 |
8 | @State var showingAlert: Bool = false
9 |
10 | var body: some View {
11 | VStack {
12 | Button("Show alert") {
13 | self.showingAlert = true
14 | }
15 | }
16 | .alert(isPresented: $showingAlert) {
17 | Alert(
18 | title: Text("Hello"),
19 | message: Text("This is an alert"),
20 | primaryButton: .default(Text("Ok")) {
21 | print("OK button tapped")
22 | },
23 | secondaryButton: .cancel()
24 | )
25 | }
26 | }
27 | }
28 |
29 | struct AlertExampleView_Previews: PreviewProvider {
30 | static var previews: some View {
31 | AlertExampleView()
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/AllWidgetsView.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import SwiftUI
5 |
6 | struct AllWidgetsView: View {
7 |
8 | var body: some View {
9 | NavigationView {
10 | Form {
11 | Group {
12 | Section(header: Text("Button")) {
13 | NavigationLink(destination: ButtonExampleView()) {
14 | Text("Button Example")
15 | }
16 | }
17 | Section(header: Text("DatePicker")) {
18 | DatePickerExampleView()
19 | }
20 | Section(header: Text("EditButton")) {
21 | NavigationLink(destination: EditButtonExampleView()) {
22 | Text("Edit Button Example")
23 | }
24 | }
25 | Section(header: Text("Form")) {
26 | NavigationLink(destination: ContactFormView()) {
27 | Text("Form Example")
28 | }
29 | }
30 | Section(header: Text("Image")) {
31 | ImageExampleView()
32 | .padding()
33 | }
34 | Section(header: Text("List")) {
35 | ListExampleView()
36 | }
37 | Section(header: Text("NavigationView/NavigationLink")) {
38 | NavigationLink(destination: NavigationContentView()) {
39 | Text("Navigation Example")
40 | }
41 | }
42 | Section(header: Text("Picker")) {
43 | NavigationLink(destination: PickerExampleView()) {
44 | Text("Picker Example")
45 | }
46 | }
47 | Section(header: Text("ScrollView")) {
48 | ScrollViewExample()
49 | }
50 | Section(header: Text("Section")) {
51 | NavigationLink(destination: ContactFormView()) {
52 | Text("See Form Example")
53 | }
54 | }
55 | }
56 | // it's true, a view function builder cannot take more than 10 views as of Xcode 12.5
57 | Group {
58 | Section(header: Text("Slider")) {
59 | SliderExampleView()
60 | }
61 | Section(header: Text("Stepper")) {
62 | StepperExampleView()
63 | }
64 | Section(header: Text("TabView")) {
65 | Text("This is embedded in a TabView, see MainView.swift")
66 | }
67 | Section(header: Text("Text")) {
68 | TextExampleView()
69 | }
70 | Section(header: Text("TextField, SecureField")) {
71 | TextFieldExampleView()
72 | }
73 | Section(header: Text("Toggle")) {
74 | ToggleExampleView()
75 | }
76 | Section(header: Text("SceneView")) {
77 | SceneViewExample()
78 | }
79 | }
80 | }
81 | .navigationBarTitle("All Widgets")
82 | }
83 | .navigationViewStyle(StackNavigationViewStyle())
84 | }
85 |
86 | }
87 |
88 | struct AllWidgetsView_Previews: PreviewProvider {
89 | static var previews: some View {
90 | AllWidgetsView()
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/ButtonExampleView.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import SwiftUI
5 |
6 | struct ButtonExampleView: View {
7 |
8 | @State var count = 0
9 |
10 | var body: some View {
11 | VStack(spacing: 10.0) {
12 | Button("Button was tapped \(count) times") {
13 | self.count += 1
14 | }
15 |
16 | Button(
17 | action: {
18 | self.count += 1
19 | }, label: {
20 | Text("Another Button")
21 | }
22 | )
23 |
24 | Button(
25 | action: {
26 | self.count += 1
27 | }, label: {
28 | Image(systemName: "sun.max")
29 | }
30 | )
31 | }
32 | .padding([.top, .bottom], 10)
33 |
34 | }
35 | }
36 |
37 | struct ButtonExampleView_Previews: PreviewProvider {
38 | static var previews: some View {
39 | ButtonExampleView()
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/CameraButtonView.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import SwiftUI
5 |
6 | struct CameraButtonView: View {
7 |
8 | @State var recording = false
9 | var action: ((_ recording: Bool) -> Void)?
10 |
11 | var body: some View {
12 |
13 | ZStack {
14 | Circle()
15 | .stroke(lineWidth: 6)
16 | .foregroundColor(.white)
17 | .frame(width: 65, height: 65)
18 |
19 | RoundedRectangle(cornerRadius: recording ? 8 : self.innerCircleWidth / 2)
20 | .foregroundColor(.white)
21 | .frame(width: self.innerCircleWidth, height: self.innerCircleWidth)
22 |
23 | }
24 | .animation(.linear(duration: 0.2))
25 | .padding(20)
26 | .onTapGesture {
27 | withAnimation {
28 | self.recording.toggle()
29 | self.action?(self.recording)
30 | }
31 | }
32 |
33 | }
34 |
35 | var innerCircleWidth: CGFloat {
36 | self.recording ? 32 : 55
37 | }
38 | }
39 |
40 | struct CameraButtonView_Previews: PreviewProvider {
41 | static var previews: some View {
42 | Group {
43 |
44 | CameraButtonView(recording: false)
45 | .previewLayout(PreviewLayout.sizeThatFits)
46 | .previewDisplayName("not recording")
47 | .background(Color.gray)
48 |
49 | CameraButtonView(recording: true)
50 | .previewLayout(PreviewLayout.sizeThatFits)
51 | .previewDisplayName("recording")
52 | .background(Color.gray)
53 |
54 | ZStack {
55 | Image("turtlerock")
56 | .resizable()
57 | .aspectRatio(contentMode: .fit)
58 | HStack {
59 | Spacer()
60 | CameraButtonView()
61 | }
62 | }
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/ChartExample.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import KeyboardAware
5 | import SwiftUI
6 |
7 | struct MonthValue: Identifiable {
8 | var id = UUID()
9 | var month: Int
10 | var value: Int
11 | }
12 |
13 | /// Inspired by https://stackoverflow.com/questions/61846963/how-to-ensure-view-appears-above-other-views-when-iterating-with-foreach-in-swif
14 | struct ChartExampleView: View {
15 |
16 | let dataPoints = (1 ... 12).map { MonthValue(month: $0, value: Int.random(in: 0 ... 100)) }
17 |
18 | var body: some View {
19 | ZStack {
20 | Color.yellow
21 | .edgesIgnoringSafeArea(.all)
22 | HStack(alignment: .bottom, spacing: 15.0) {
23 | ForEach(dataPoints) { dataPoint in
24 | VStack(spacing: 5) {
25 | LinearGradient(gradient: Gradient(colors: [.green, .blue]), startPoint: .top, endPoint: .bottom)
26 | .frame(width: 12, height: CGFloat(dataPoint.value))
27 | .cornerRadius(5)
28 | Text("\(dataPoint.month)")
29 | .font(.caption)
30 | }
31 | }
32 | }
33 | }
34 | }
35 | }
36 |
37 | struct ChartExample_Previews: PreviewProvider {
38 | static var previews: some View {
39 | ChartExampleView()
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/ChartExampleView.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import SwiftUI
5 |
6 | struct MonthValue: Identifiable {
7 | var id = UUID()
8 | var month: Int
9 | var value: Int
10 | }
11 |
12 | /// Inspired by https://stackoverflow.com/questions/61846963/how-to-ensure-view-appears-above-other-views-when-iterating-with-foreach-in-swif
13 | struct ChartExampleView: View {
14 |
15 | let dataPoints = (1 ... 12).map { MonthValue(month: $0, value: Int.random(in: 0 ... 100)) }
16 |
17 | var body: some View {
18 | ZStack {
19 | Color.yellow
20 | .edgesIgnoringSafeArea(.all)
21 | HStack(alignment: .bottom, spacing: 15.0) {
22 | ForEach(dataPoints) { dataPoint in
23 | VStack(spacing: 5) {
24 | LinearGradient(gradient: Gradient(colors: [.green, .blue]), startPoint: .top, endPoint: .bottom)
25 | .frame(width: 12, height: CGFloat(dataPoint.value))
26 | .cornerRadius(5)
27 | Text("\(dataPoint.month)")
28 | .font(.caption)
29 | }
30 | }
31 | }
32 | }
33 | }
34 | }
35 |
36 | struct ChartExampleView_Previews: PreviewProvider {
37 | static var previews: some View {
38 | ChartExampleView()
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/ClipImageSquareView.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import SwiftUI
5 |
6 | /// Clip image to square in SwiftUI
7 | /// see https://stackoverflow.com/questions/58290963/clip-image-to-square-in-swiftui
8 | struct ClipImageSquareView: View {
9 | var body: some View {
10 | HStack {
11 | ForEach(0 ..< 3, id: \.self) { _ in
12 | ZStack {
13 | Image("cookies")
14 | .resizable()
15 | .aspectRatio(contentMode: .fill)
16 | .layoutPriority(-1)
17 | VStack {
18 | Spacer()
19 | Text("Cookie")
20 | .frame(minWidth: 0, maxWidth: .infinity)
21 | .background(Color.white)
22 | }
23 | }
24 | .clipped()
25 | .aspectRatio(1, contentMode: .fit)
26 | .border(Color.red)
27 | }
28 | }
29 | }
30 | }
31 |
32 | struct ClipImageSquareView_Previews: PreviewProvider {
33 | static var previews: some View {
34 | ClipImageSquareView()
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/CloseButtonExampleView.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import Foundation
5 | import SwiftUI
6 |
7 | struct CloseButtonExampleView: View {
8 |
9 | var body: some View {
10 | Color.yellow
11 | .ignoresSafeArea()
12 | .overlay(self.closeButton, alignment: .topTrailing)
13 | }
14 |
15 | @ViewBuilder var closeButton: some View {
16 | Button(
17 | action: {},
18 | label: {
19 | ZStack {
20 | Circle()
21 | .foregroundColor(.green)
22 | .frame(width: 30, height: 30)
23 | Image(systemName: "xmark")
24 | .foregroundColor(.white)
25 | }
26 | }
27 | )
28 | .padding(20)
29 | .ignoresSafeArea()
30 | }
31 |
32 | }
33 |
34 | struct CloseButtonExampleView_Previews: PreviewProvider {
35 |
36 | static var previewView: some View {
37 | CloseButtonExampleView()
38 | }
39 |
40 | static var previews: some View {
41 | previewView
42 | .previewDevice(PreviewDevice(rawValue: "iPhone SE (2nd generation)"))
43 | previewView
44 | .previewDevice(PreviewDevice(rawValue: "iPhone 12"))
45 | previewView
46 | .previewDevice(PreviewDevice(rawValue: "iPhone 12 Pro Max"))
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/CollectionViewExample.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import SwiftUI
5 | import UIKit
6 |
7 | /// inspired by https://stackoverflow.com/questions/61952997/uicollectionview-with-swiftui-drag-and-drop-reordering-possible
8 | /// see also https://stackoverflow.com/questions/61033980/swiftui-wrapping-uicollectionview-and-use-compositional-layout
9 | struct CollectionViewExample: View {
10 | var body: some View {
11 | CollectionComponent()
12 | }
13 | }
14 |
15 | struct CollectionComponent: UIViewRepresentable {
16 | func makeCoordinator() -> CollectionComponent.Coordinator {
17 | Coordinator(data: [])
18 | }
19 |
20 | class Coordinator: NSObject, UICollectionViewDataSource, UICollectionViewDelegate {
21 | var data: [String] = []
22 |
23 | init(data: [String]) {
24 |
25 | for index in 0 ... 20 {
26 | self.data.append("\(index)")
27 | }
28 |
29 | }
30 |
31 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
32 | self.data.count
33 | }
34 |
35 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
36 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! Cell
37 | cell.cellView.rootView = AnyView(CellView(text: self.data[indexPath.item]))
38 | return cell
39 | }
40 |
41 | }
42 |
43 | func makeUIView(context: Context) -> UICollectionView {
44 |
45 | let layout = UICollectionViewFlowLayout()
46 | layout.scrollDirection = .vertical
47 | layout.itemSize = CGSize(width: 150, height: 150)
48 | layout.sectionInset = UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20)
49 |
50 | let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
51 | collectionView.backgroundColor = .systemBackground
52 | collectionView.dataSource = context.coordinator
53 | collectionView.delegate = context.coordinator
54 | collectionView.register(Cell.self, forCellWithReuseIdentifier: "cell")
55 |
56 | return collectionView
57 | }
58 |
59 | func updateUIView(_ uiView: UICollectionView, context: Context) {}
60 | }
61 |
62 | class Cell: UICollectionViewCell {
63 | public var cellView = UIHostingController(rootView: AnyView(EmptyView()))
64 |
65 | override public init(frame: CGRect) {
66 | super.init(frame: frame)
67 | self.configure()
68 | }
69 |
70 | public required init?(coder aDecoder: NSCoder) {
71 | super.init(coder: aDecoder)
72 | self.configure()
73 | }
74 |
75 | private func configure() {
76 | contentView.addSubview(self.cellView.view)
77 |
78 | self.cellView.view.preservesSuperviewLayoutMargins = false
79 | self.cellView.view.translatesAutoresizingMaskIntoConstraints = false
80 |
81 | NSLayoutConstraint.activate([
82 | self.cellView.view.leftAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leftAnchor),
83 | self.cellView.view.rightAnchor.constraint(equalTo: contentView.layoutMarginsGuide.rightAnchor),
84 | self.cellView.view.topAnchor.constraint(equalTo: contentView.layoutMarginsGuide.topAnchor),
85 | self.cellView.view.bottomAnchor.constraint(equalTo: contentView.layoutMarginsGuide.bottomAnchor),
86 | ])
87 | }
88 | }
89 |
90 | struct CellView: View {
91 | let text: String
92 |
93 | var body: some View {
94 | ZStack {
95 | Text(text)
96 | }
97 | .frame(width: 150, height: 150)
98 | .background(Color.blue)
99 | }
100 | }
101 |
102 | struct CollectionViewExample_Previews: PreviewProvider {
103 | static var previews: some View {
104 | CollectionViewExample()
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/ColorCycleView.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import SwiftUI
5 |
6 | /// from https://stackoverflow.com/questions/61831799/changes-are-not-reflected-in-foreach-in-swiftui
7 | struct ColorCycleView: View {
8 | @State var index: Int = 0
9 |
10 | var colors = [Color.red, Color.blue, Color.orange, Color.green]
11 |
12 | var body: some View {
13 | NavigationView {
14 | VStack {
15 | Rectangle()
16 | .fill(self.colors[index])
17 | .frame(width: 100, height: 100)
18 | }
19 | .navigationBarTitle("Color cycle")
20 | .navigationBarItems(trailing:
21 | Button("Next color") {
22 | self.index = (self.index + 1) % self.colors.count
23 | }
24 | )
25 | }
26 | }
27 | }
28 |
29 | struct ColorCycleView_Previews: PreviewProvider {
30 | static var previews: some View {
31 | ColorCycleView()
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/ConditionalDatePickerView.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import SwiftUI
5 |
6 | /// Conditional DatePicker in SwiftUI
7 | /// see https://stackoverflow.com/questions/61831882/swiftui-list-conditional-items-break-animations
8 | struct ConditionalDatePickerView: View {
9 |
10 | @State var showDatePicker = false
11 |
12 | var body: some View {
13 | Form {
14 | Section(header: Text("Section 1")) {
15 | Toggle(isOn: $showDatePicker) {
16 | Text("Show Date Picker")
17 | }
18 | if self.showDatePicker {
19 | DatePicker(selection: .constant(Date()), label: { Text("Date") })
20 | }
21 | }
22 | Section(header: Text("Section 2")) {
23 | Text("Hello")
24 | }
25 | }
26 | .listStyle(GroupedListStyle())
27 | }
28 | }
29 |
30 | struct ConditionalDatePickerView_Previews: PreviewProvider {
31 | static var previews: some View {
32 | ConditionalDatePickerView()
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/ContactFormView.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import SwiftUI
5 |
6 | struct Contact: Identifiable {
7 | var id = UUID()
8 | var name: String
9 | var subscribed: Bool
10 | var birthday: Date
11 |
12 | static let alice = Contact(name: "Alice", subscribed: false, birthday: Date(year: 1970, month: 7, day: 8))
13 |
14 | static let bob = Contact(name: "Bob", subscribed: true, birthday: Date(year: 1980, month: 3, day: 4))
15 | }
16 |
17 | struct ContactFormView: View {
18 |
19 | @State var contact = Contact.bob
20 |
21 | var body: some View {
22 | Form {
23 | Section(header: Text("Personal information")) {
24 | HStack {
25 | Text("Name")
26 | TextField("Username", text: $contact.name)
27 | .multilineTextAlignment(.trailing)
28 | }
29 | Toggle(isOn: $contact.subscribed) {
30 | Text("Subscribed")
31 | }
32 | DatePicker(selection: $contact.birthday, label: { Text("Birthday") })
33 | }
34 |
35 | PickerExampleView()
36 |
37 | }
38 | .navigationBarTitle("Contact")
39 | }
40 |
41 | }
42 |
43 | struct ContactFormView_Previews: PreviewProvider {
44 | static var previews: some View {
45 | NavigationView {
46 | ContactFormView()
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/CustomAlertView.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import SwiftUI
5 |
6 | struct CustomAlertView: View {
7 |
8 | @State var alertVisible = false
9 |
10 | var body: some View {
11 | Button(
12 | action: {
13 | self.alertVisible = true
14 | },
15 | label: {
16 | Text("Show Alert")
17 | }
18 | )
19 | .overlay(self.alertView)
20 |
21 | }
22 |
23 | @ViewBuilder var alertView: some View {
24 | if alertVisible {
25 | VStack {
26 | Text("Hello Alert!")
27 | Button("Ok") {
28 | self.alertVisible = false
29 | }
30 | }
31 | .frame(width: 200)
32 | .padding()
33 | .background(Color.white)
34 | .cornerRadius(10)
35 | .shadow(radius: 2)
36 | }
37 | }
38 |
39 | }
40 |
41 | struct CustomAlertView_Previews: PreviewProvider {
42 | static var previews: some View {
43 | Group {
44 | CustomAlertView()
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/DarkModeExampleView.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import SwiftUI
5 |
6 | /// inspired by https://stackoverflow.com/questions/61313187/how-can-i-fit-a-shape-in-swift-ui-to-accommodate-the-length-and-width-of-a-text
7 |
8 | struct DarkModeExampleView: View {
9 | @Environment(\.colorScheme) var colorScheme
10 |
11 | var body: some View {
12 | VStack {
13 | Button(
14 | action: {
15 | print("Button Tapped")
16 | },
17 | label: {
18 | HStack {
19 | Image(systemName: "tag.fill")
20 | Text("Tickets")
21 | }
22 | }
23 | )
24 | .buttonStyle(RoundedButtonStyle())
25 | }
26 | .navigationBarTitle("\(String(describing: colorScheme))")
27 | }
28 |
29 | }
30 |
31 | /// Reusable Button style
32 | struct RoundedButtonStyle: ButtonStyle {
33 | func makeBody(configuration: Self.Configuration) -> some View {
34 | ZStack {
35 | configuration.label
36 | .font(.headline)
37 | .padding(EdgeInsets(top: 8, leading: 12, bottom: 8, trailing: 12))
38 | .background(
39 | LinearGradient(gradient: Gradient(colors: [Color("ButtonColor"), Color("ButtonColorGradient")]), startPoint: .top, endPoint: .bottom)
40 | .clipShape(RoundedRectangle(cornerRadius: 8))
41 | )
42 | }
43 | }
44 | }
45 |
46 | struct DarkModeExampleView_Previews: PreviewProvider {
47 | static var previews: some View {
48 | Group {
49 | ForEach([ColorScheme.light, .dark], id: \.self) { colorScheme in
50 | NavigationView {
51 | DarkModeExampleView()
52 | }
53 | .environment(\.colorScheme, colorScheme)
54 | }
55 |
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/DashboardView.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import SwiftUI
5 |
6 | struct CircularProgressView: View {
7 | private let lineWidth: CGFloat = 6
8 |
9 | var body: some View {
10 | Circle()
11 | .stroke(lineWidth: 6)
12 | .scaledToFit()
13 | .opacity(0.3)
14 | .foregroundColor(Color.gray.opacity(0.5))
15 | .overlay(Circle()
16 | .trim(from: 0.0, to: 0.4)
17 | .stroke(style: StrokeStyle(lineWidth: lineWidth, lineCap: .round, lineJoin: .round))
18 | .foregroundColor(Color.blue)
19 | .rotationEffect(Angle(degrees: 270.0))
20 | .animation(.linear))
21 | .overlay(
22 | Text("Text")
23 | )
24 |
25 | }
26 | }
27 |
28 | struct DashboardView: View {
29 | var body: some View {
30 | HStack(spacing: 10) {
31 | CircularProgressView()
32 | CircularProgressView()
33 | CircularProgressView()
34 | }
35 | }
36 | }
37 |
38 | struct DashboardView_Previews: PreviewProvider {
39 | static var previews: some View {
40 | VStack {
41 | DashboardView()
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/DatePickerExampleView.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import SwiftUI
5 |
6 | struct DatePickerExampleView: View {
7 |
8 | @State var date = Date()
9 |
10 | var body: some View {
11 | DatePicker(
12 | selection: $date,
13 | label: { Text("Date") }
14 | )
15 | }
16 | }
17 |
18 | struct DatePickerExampleView_Previews: PreviewProvider {
19 | static var previews: some View {
20 | DatePickerExampleView()
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/DragGestureView.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import SwiftUI
5 |
6 | /// Using a DragGesture to move a view horizontally
7 | /// see https://stackoverflow.com/questions/57020557/why-doesnt-this-swiftui-view-animate
8 | struct DragGestureView: View {
9 |
10 | @GestureState var dragOffset = CGSize.zero
11 | @State var offset: CGFloat = 0
12 |
13 | var dragGesture: some Gesture {
14 | DragGesture()
15 | .updating($dragOffset) { value, state, _ in
16 | state = value.translation
17 | }
18 | .onEnded { gesture in
19 | // keep the offset already moved without animation
20 | self.offset += gesture.translation.height
21 | withAnimation(Animation.easeOut(duration: 3)) {
22 | self.offset += gesture.predictedEndTranslation.height - gesture.translation.height
23 | }
24 | }
25 | }
26 |
27 | var body: some View {
28 | VStack {
29 | ForEach(1 ..< 5) { _ in
30 | Color.red
31 | .frame(minHeight: 20, maxHeight: 100)
32 | }
33 | }
34 | .offset(x: 0, y: offset + dragOffset.height)
35 | .gesture(dragGesture)
36 | }
37 |
38 | }
39 |
40 | struct DragGestureView_Previews: PreviewProvider {
41 | static var previews: some View {
42 | DragGestureView()
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/EditButtonExampleView.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import SwiftUI
5 |
6 | struct EditButtonExampleView: View {
7 |
8 | @State var names = ["Alice", "Bob", "Carol"]
9 |
10 | var body: some View {
11 | List {
12 | ForEach(names, id: \.self) { name in
13 | Text(name)
14 | }
15 | .onDelete(perform: delete)
16 | }
17 | .navigationBarTitle("Names")
18 | .navigationBarItems(trailing: EditButton())
19 | }
20 |
21 | func delete(at offsets: IndexSet) {
22 | self.names.remove(atOffsets: offsets)
23 | }
24 |
25 | }
26 |
27 | struct EditButtonExampleView_Previews: PreviewProvider {
28 | static var previews: some View {
29 | EditButtonExampleView()
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/EditableListExample.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import SwiftUI
5 |
6 | // see https://stackoverflow.com/a/62005070/128083
7 |
8 | struct Position {
9 | var id = UUID()
10 | var count: Int
11 | var name: String
12 | }
13 |
14 | class BookingModel: ObservableObject {
15 |
16 | @Published var positions: [Position]
17 |
18 | init(positions: [Position] = []) {
19 | self.positions = positions
20 | }
21 |
22 | }
23 |
24 | struct EditableListExample: View {
25 |
26 | @ObservedObject var bookingModel = BookingModel(
27 | positions: [
28 | Position(count: 1, name: "Candy"),
29 | Position(count: 0, name: "Bread"),
30 | ]
31 | )
32 |
33 | var body: some View {
34 | // >>> Passing a binding into an Array via index:
35 | List(bookingModel.positions.indexed(), id: \.element.id) { i, _ in
36 | PositionRowView(position: self.$bookingModel.positions[i])
37 | }
38 | }
39 | }
40 |
41 | struct PositionRowView: View {
42 |
43 | @Binding var position: Position
44 |
45 | var body: some View {
46 | Stepper(
47 | value: $position.count,
48 | label: {
49 | Text("\(position.count)x \(position.name)")
50 | }
51 | )
52 | }
53 |
54 | }
55 |
56 | struct EditableListExample_Previews: PreviewProvider {
57 | static var previews: some View {
58 | EditableListExample()
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/EnumeratedListView.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import Foundation
5 | import SwiftUI
6 |
7 | // see https://stackoverflow.com/a/67132409/128083
8 |
9 | struct Light: Identifiable {
10 | var id: Int
11 | var active: Bool = false
12 | }
13 |
14 | class HomeModel: ObservableObject {
15 | @Published var lights = (1 ... 5).map { Light(id: $0) }
16 | }
17 |
18 | struct EnumeratedListView: View {
19 |
20 | @StateObject var homeModel = HomeModel()
21 |
22 | var body: some View {
23 | Form {
24 | ForEach(self.homeModel.lights.indexed(), id: \.element.id) { idx, light in
25 | Toggle("Light \(light.id)", isOn: $homeModel.lights[idx].active)
26 | }
27 | }
28 | }
29 |
30 | }
31 |
32 | struct EnumeratedList_Previews: PreviewProvider {
33 | static var previews: some View {
34 | EnumeratedListView()
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/EnvironmentView.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import SwiftUI
5 |
6 | /// List of all the environment values: https://developer.apple.com/documentation/swiftui/environmentvalues
7 | /// Custom environment keys: https://swiftwithmajid.com/2019/08/21/the-power-of-environment-in-swiftui/#custom-environment-keys
8 | /// Custom editable environment keys like editMode: https://stackoverflow.com/a/61847419/128083
9 | struct EnvironmentView: View {
10 | @Environment(\.locale) var locale
11 | @Environment(\.sizeCategory) var sizeCategory
12 | @Environment(\.font) var font
13 | @Environment(\.isEnabled) var isEnabled
14 | @Environment(\.colorScheme) var colorScheme
15 | @Environment(\.verticalSizeClass) var verticalSizeClass: UserInterfaceSizeClass?
16 | @Environment(\.horizontalSizeClass) var horizontalSizeClass: UserInterfaceSizeClass?
17 |
18 | var body: some View {
19 | VStack(alignment: .leading) {
20 | Text("Locale: \(String(describing: locale))")
21 | Text("Size category: \(String(describing: sizeCategory))")
22 | Text("horizontalSizeClass: \(String(describing: horizontalSizeClass))")
23 | Text("verticalSizeClass: \(String(describing: verticalSizeClass))")
24 | Text("font: \(String(describing: font))")
25 | Text("isEnabled: \(String(describing: isEnabled))")
26 | Text("colorScheme: \(String(describing: colorScheme))")
27 | Button("Example Button") {}
28 | }
29 | .environment(\.isEnabled, false)
30 |
31 | }
32 | }
33 |
34 | struct EnvironmentView_Previews: PreviewProvider {
35 | static var previews: some View {
36 | EnvironmentView()
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/Frameworks/Core Data/Binding+OptionalFallback.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import SwiftUI
5 |
6 | // https://stackoverflow.com/questions/57021722/swiftui-optional-textfield
7 | func ?? (lhs: Binding, rhs: T) -> Binding {
8 | Binding(
9 | get: { lhs.wrappedValue ?? rhs },
10 | set: { lhs.wrappedValue = $0 }
11 | )
12 | }
13 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/Frameworks/Core Data/TodoView.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import CoreData
5 | import SwiftUI
6 |
7 | struct TodoView: View {
8 | @ObservedObject var todo: Todo
9 | @Environment(\.managedObjectContext) var managedObjectContext
10 |
11 | var body: some View {
12 | Form {
13 | TextField("Todo", text: $todo.name ?? "")
14 | }
15 | .onDisappear {
16 | try? self.managedObjectContext.save()
17 | }
18 | }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/Frameworks/Core Data/TodosView.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import CoreData
5 | import SwiftUI
6 |
7 | struct TodosView: View {
8 | @FetchRequest(fetchRequest: Todo.allTodos) var todos: FetchedResults
9 | @Environment(\.managedObjectContext) var managedObjectContext
10 |
11 | var body: some View {
12 | NavigationView {
13 | List {
14 | ForEach(self.todos) { todo in
15 | NavigationLink(destination: TodoView(todo: todo)) {
16 | Text(todo.name ?? "")
17 | }
18 | }
19 | .onDelete(perform: deleteRows)
20 | }
21 | .navigationBarTitle("Todos", displayMode: .inline)
22 | .navigationBarItems(trailing: addButton)
23 | }
24 | }
25 |
26 | func deleteRows(at offsets: IndexSet) {
27 | for index in offsets {
28 | self.managedObjectContext.delete(self.todos[index])
29 | }
30 | }
31 |
32 | var addButton: some View {
33 | Button(
34 | action: {
35 | let todo = Todo.create(in: self.managedObjectContext)
36 | todo.name = "Example \(Date())"
37 | try! self.managedObjectContext.save()
38 | },
39 | label: {
40 | Image(systemName: "plus")
41 | }
42 | )
43 | }
44 |
45 | }
46 |
47 | struct TodosView_Previews: PreviewProvider {
48 | static var previews: some View {
49 | TodosView()
50 | .environment(\.managedObjectContext, CoreDataStack.shared.persistentContainer.viewContext)
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/Frameworks/Core Image/ImageEffectView.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import Combine
5 | import CoreImage
6 | import CoreImage.CIFilterBuiltins
7 | import ImagePickerView
8 | import SwiftUI
9 |
10 | /// see https://stackoverflow.com/questions/61929273/core-image-photo-effect-react-change-slowly-in-real-iphone-when-changing-intens/61930650#61930650
11 | class ImageEffect: ObservableObject {
12 |
13 | @Published var filterIntensity = 0.5
14 | @Published var inputImage: UIImage?
15 | @Published var outputImage: UIImage?
16 |
17 | @Published var currentFilter = CIFilter.sepiaTone()
18 | let context = CIContext()
19 | var subscriptions = Set()
20 | let queue = DispatchQueue(label: "Image processing")
21 |
22 | init() {
23 | self.$inputImage
24 | .map { inputImage -> CIImage? in
25 | guard let inputImage = inputImage else { return nil }
26 | return CIImage(image: inputImage)
27 | }
28 | .combineLatest(self.$filterIntensity)
29 | .debounce(for: .milliseconds(50), scheduler: self.queue)
30 | .map { inputImage, filterIntensity -> UIImage? in
31 | guard let inputImage = inputImage else { return nil }
32 |
33 | self.currentFilter.inputImage = inputImage
34 | self.currentFilter.intensity = Float(filterIntensity)
35 |
36 | guard let outputImage = self.currentFilter.outputImage else { return nil }
37 |
38 | guard let cgImage = self.context.createCGImage(outputImage, from: outputImage.extent) else { return nil }
39 |
40 | return UIImage(cgImage: cgImage)
41 |
42 | }
43 | .receive(on: RunLoop.main)
44 | .sink { image in
45 | self.outputImage = image
46 | }
47 | .store(in: &self.subscriptions)
48 | }
49 |
50 | }
51 |
52 | struct ImageEffectView: View {
53 |
54 | @ObservedObject var imageEffect = ImageEffect()
55 | @State private var showingImagePicker = false
56 |
57 | var body: some View {
58 | NavigationView {
59 | VStack {
60 | ZStack {
61 | Rectangle()
62 | .fill(Color.secondary)
63 |
64 | if imageEffect.outputImage != nil {
65 | Image(uiImage: imageEffect.outputImage!)
66 | .resizable()
67 | .scaledToFit()
68 | } else {
69 | Text("Tap to select a picture")
70 | .foregroundColor(.white)
71 | .font(.headline)
72 | }
73 | }
74 | .onTapGesture {
75 | self.showingImagePicker = true
76 | }
77 |
78 | HStack {
79 | Text("Intensity")
80 | Slider(value: $imageEffect.filterIntensity)
81 | }
82 | .padding(.vertical)
83 |
84 | }
85 | .padding([.horizontal, .bottom])
86 | .navigationBarTitle("Instafilter")
87 | .sheet(isPresented: $showingImagePicker) {
88 | ImagePickerView(sourceType: .photoLibrary) { image in
89 | self.imageEffect.inputImage = image
90 | }
91 | }
92 | }
93 | }
94 |
95 | }
96 |
97 | struct ImageEffectView_Previews: PreviewProvider {
98 | static var previews: some View {
99 | ImageEffectView()
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/Frameworks/Foundation/TodosURLSessionExampleView.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import Combine
5 | import SwiftUI
6 |
7 | struct TypiTodo: Codable, Identifiable {
8 | var id: Int
9 | var title: String
10 | }
11 |
12 | class TodosModel: ObservableObject {
13 |
14 | @Published var todos = [TypiTodo]()
15 | @Published var state = State.ready
16 |
17 | enum State {
18 | case ready
19 | case loading(Cancellable)
20 | case loaded
21 | case error(Error)
22 | }
23 |
24 | let url = URL(string: "https://jsonplaceholder.typicode.com/todos/")!
25 | let urlSession = URLSession.shared
26 |
27 | var dataTask: AnyPublisher<[TypiTodo], Error> {
28 | self.urlSession
29 | .dataTaskPublisher(for: self.url)
30 | .map(\.data)
31 | .decode(type: [TypiTodo].self, decoder: JSONDecoder())
32 | .receive(on: RunLoop.main)
33 | .eraseToAnyPublisher()
34 | }
35 |
36 | func load() {
37 | assert(Thread.isMainThread)
38 | self.state = .loading(self.dataTask.sink(
39 | receiveCompletion: { completion in
40 | switch completion {
41 | case .finished:
42 | break
43 | case let .failure(error):
44 | self.state = .error(error)
45 | }
46 | },
47 | receiveValue: { value in
48 | self.state = .loaded
49 | self.todos = value
50 | }
51 | ))
52 | }
53 |
54 | func loadIfNeeded() {
55 | assert(Thread.isMainThread)
56 | guard case .ready = self.state else { return }
57 | self.load()
58 | }
59 | }
60 |
61 | /// Best Practices to use URLSession to load JSON data for SwiftUI Views
62 | /// see also: https://stackoverflow.com/questions/61855811/best-practices-to-use-urlsession-to-load-json-data-for-swiftui-views
63 | /// Base components for abstracting this same approach: https://github.com/ralfebert/EndpointModel
64 | struct TodosURLSessionExampleView: View {
65 |
66 | @ObservedObject var model = TodosModel()
67 |
68 | var body: some View {
69 | List(model.todos) { todo in
70 | Text(todo.title)
71 | }
72 | .overlay(StatusOverlay(model: model))
73 | .onAppear { self.model.loadIfNeeded() }
74 | }
75 | }
76 |
77 | struct StatusOverlay: View {
78 |
79 | @ObservedObject var model: TodosModel
80 |
81 | @ViewBuilder
82 | var body: some View {
83 | switch model.state {
84 | case .ready:
85 | EmptyView()
86 | case .loading:
87 | ProgressView()
88 | case .loaded:
89 | EmptyView()
90 | case let .error(error):
91 | VStack(spacing: 10) {
92 | Text(error.localizedDescription)
93 | .frame(maxWidth: 300)
94 | Button("Retry") {
95 | self.model.load()
96 | }
97 | }
98 | .padding()
99 | .background(Color.yellow)
100 | }
101 | }
102 |
103 | }
104 |
105 | struct TodosURLSessionExampleView_Previews: PreviewProvider {
106 | static var previews: some View {
107 | Group {
108 | TodosURLSessionExampleView(model: TodosModel())
109 | TodosURLSessionExampleView(model: self.exampleLoadedModel)
110 | TodosURLSessionExampleView(model: self.exampleLoadingModel)
111 | TodosURLSessionExampleView(model: self.exampleErrorModel)
112 | }
113 | }
114 |
115 | static var exampleLoadedModel: TodosModel {
116 | let todosModel = TodosModel()
117 | todosModel.todos = [TypiTodo(id: 1, title: "Drink water"), TypiTodo(id: 2, title: "Enjoy the sun")]
118 | todosModel.state = .loaded
119 | return todosModel
120 | }
121 |
122 | static var exampleLoadingModel: TodosModel {
123 | let todosModel = TodosModel()
124 | todosModel.state = .loading(ExampleCancellable())
125 | return todosModel
126 | }
127 |
128 | static var exampleErrorModel: TodosModel {
129 | let todosModel = TodosModel()
130 | todosModel.state = .error(ExampleError.exampleError)
131 | return todosModel
132 | }
133 |
134 | enum ExampleError: Error {
135 | case exampleError
136 | }
137 |
138 | struct ExampleCancellable: Cancellable {
139 | func cancel() {}
140 | }
141 |
142 | }
143 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/Frameworks/MessageUI/MailView.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import MessageUI
5 | import SwiftUI
6 |
7 | /// https://stackoverflow.com/questions/56784722/swiftui-send-email
8 | struct MailView: UIViewControllerRepresentable {
9 |
10 | @Binding var isShowing: Bool
11 | @Binding var result: Result?
12 |
13 | class Coordinator: NSObject, MFMailComposeViewControllerDelegate {
14 |
15 | @Binding var isShowing: Bool
16 | @Binding var result: Result?
17 |
18 | init(isShowing: Binding,
19 | result: Binding?>)
20 | {
21 | _isShowing = isShowing
22 | _result = result
23 | }
24 |
25 | func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
26 | defer {
27 | isShowing = false
28 | }
29 | guard error == nil else {
30 | self.result = .failure(error!)
31 | return
32 | }
33 | self.result = .success(result)
34 | }
35 | }
36 |
37 | func makeCoordinator() -> Coordinator {
38 | Coordinator(isShowing: $isShowing, result: $result)
39 | }
40 |
41 | func makeUIViewController(context: UIViewControllerRepresentableContext) -> MFMailComposeViewController {
42 | let vc = MFMailComposeViewController()
43 | vc.mailComposeDelegate = context.coordinator
44 | return vc
45 | }
46 |
47 | func updateUIViewController(_ uiViewController: MFMailComposeViewController,
48 | context: UIViewControllerRepresentableContext) {}
49 | }
50 |
51 | struct MailExampleView: View {
52 |
53 | @State var result: Result? = nil
54 | @State var isShowingMailView = false
55 |
56 | var body: some View {
57 |
58 | VStack {
59 | if MFMailComposeViewController.canSendMail() {
60 | Button("Show mail view") {
61 | self.isShowingMailView.toggle()
62 | }
63 | } else {
64 | Text("Can't send emails from this device")
65 | }
66 | if result != nil {
67 | Text("Result: \(String(describing: result))")
68 | .lineLimit(nil)
69 | }
70 | }
71 | .sheet(isPresented: $isShowingMailView) {
72 | MailView(isShowing: self.$isShowingMailView, result: self.$result)
73 | }
74 |
75 | }
76 |
77 | }
78 |
79 | struct MailView_Previews: PreviewProvider {
80 | static var previews: some View {
81 | MailExampleView()
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/Frameworks/PencilKit/PencilKitView.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import PencilKit
5 | import SwiftUI
6 |
7 | struct CanvasRepresentable: UIViewRepresentable {
8 |
9 | func makeUIView(context: Context) -> PKCanvasView {
10 | let canvasView = PKCanvasView()
11 | canvasView.isOpaque = false
12 | canvasView.drawingPolicy = .anyInput
13 | canvasView.delegate = context.coordinator
14 | canvasView.tool = PKInkingTool(.pen, color: .black, width: 1)
15 | return canvasView
16 | }
17 |
18 | func updateUIView(_ uiView: PKCanvasView, context: Context) {}
19 |
20 | func makeCoordinator() -> Coordinator {
21 | Coordinator()
22 | }
23 |
24 | class Coordinator: NSObject, PKCanvasViewDelegate {}
25 |
26 | }
27 |
28 | struct PencilKitView_Previews: PreviewProvider {
29 | static var previews: some View {
30 | CanvasRepresentable()
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/ImageExampleView.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import SwiftUI
5 |
6 | struct ImageExampleView: View {
7 | var body: some View {
8 | HStack(spacing: 30.0) {
9 | Image("cookies")
10 | .resizable()
11 | .aspectRatio(contentMode: .fit)
12 | .frame(maxWidth: 100, maxHeight: 100)
13 |
14 | Image(systemName: "sun.max")
15 | .font(.system(size: 60))
16 |
17 | }
18 |
19 | }
20 | }
21 |
22 | struct ImageExampleView_Previews: PreviewProvider {
23 | static var previews: some View {
24 | ImageExampleView()
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/ImagePickerView.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import ImagePickerView
5 | import SwiftUI
6 |
7 | struct ImagePickerExampleView: View {
8 |
9 | @State var showImagePicker: Bool = false
10 | @State var image: UIImage?
11 |
12 | var body: some View {
13 | VStack {
14 | if image != nil {
15 | Image(uiImage: image!)
16 | .resizable()
17 | .aspectRatio(contentMode: .fit)
18 | }
19 | Button("Pick image") {
20 | self.showImagePicker.toggle()
21 | }
22 | }
23 | .sheet(isPresented: $showImagePicker) {
24 | ImagePickerView(sourceType: .photoLibrary) { image in
25 | self.image = image
26 | }
27 | }
28 | }
29 | }
30 |
31 | struct ImagePickerExampleView_Previews: PreviewProvider {
32 | static var previews: some View {
33 | ImagePickerExampleView()
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/KeyboardAwareView.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import KeyboardAware
5 | import SwiftUI
6 |
7 | struct KeyboardAwareView: View {
8 | @State var text = "example"
9 |
10 | var body: some View {
11 | NavigationView {
12 | ScrollView {
13 | VStack(alignment: .leading) {
14 | ForEach(0 ..< 20) { i in
15 | Text("Text \(i):")
16 | TextField("Text", text: self.$text)
17 | .textFieldStyle(RoundedBorderTextFieldStyle())
18 | .padding(.bottom, 10)
19 | }
20 | }
21 | .padding()
22 | }
23 | .keyboardAware()
24 | .navigationBarTitle("Keyboard Example")
25 | }
26 |
27 | }
28 | }
29 |
30 | struct KeyboardAwareView_Previews: PreviewProvider {
31 | static var previews: some View {
32 | KeyboardAwareView()
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/Layout/LayoutSameHeightView.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import SwiftUI
5 |
6 | // https://stackoverflow.com/questions/61905699/how-to-make-subview-fit-all-available-vertical-space-in-hstack
7 | struct LayoutSameHeightView: View {
8 | var body: some View {
9 | HStack(alignment: .top) {
10 | VStack {
11 | Image("cookies")
12 | .resizable()
13 | .aspectRatio(contentMode: .fit)
14 | Text("Lorem Ipsum Lorem Ipsum Lorem Ipsum")
15 |
16 | }
17 | .frame(maxHeight: .infinity, alignment: .center) // << here !!
18 | .background(Color.blue)
19 | VStack {
20 | Image("turtlerock")
21 | .resizable()
22 | .aspectRatio(contentMode: .fit)
23 | Text("Hello")
24 | }
25 | .frame(maxHeight: .infinity, alignment: .center) // << here !!
26 | .background(Color.red)
27 | }
28 | .fixedSize(horizontal: false, vertical: true) // << here !!
29 | }
30 | }
31 |
32 | struct LayoutSameHeightView_Previews: PreviewProvider {
33 | static var previews: some View {
34 | LayoutSameHeightView()
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/Layout/RelativePositionExampleView.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import SwiftUI
5 |
6 | /// see https://stackoverflow.com/questions/61952141/swiftui-how-to-overlay-an-image-with-multiple-shapes-and-dependent-positioning
7 | /// see https://stackoverflow.com/questions/61953736/changing-the-anchor-point-for-a-view-when-placing-using-position
8 | struct RelativePositionExampleView: View {
9 | var body: some View {
10 | Image("cookies")
11 | .resizable()
12 | .aspectRatio(contentMode: .fit)
13 | .overlay(
14 | GeometryReader { geometry in
15 | Text("Hello")
16 | .background(Color.yellow)
17 | .position(x: geometry.size.width * 0.7, y: geometry.size.height * 0.7)
18 | }
19 | )
20 | }
21 | }
22 |
23 | struct RelativePositionExampleView_Previews: PreviewProvider {
24 | static var previews: some View {
25 | RelativePositionExampleView()
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/LinkedStateChangeView.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import SwiftUI
5 |
6 | /// from https://stackoverflow.com/questions/61831034/swiftui-display-doesnt-update-on-child-link-changing-the-state
7 | struct LinkedStateChangeView: View {
8 | @State var enabled: Bool = false
9 | @State var items: [String] = ["item"]
10 |
11 | var body: some View {
12 | NavigationView {
13 | Section {
14 | ForEach(self.items, id: \.self) { _ in
15 | NavigationLink(destination: Toggle(isOn: self.$enabled) { Text("enable") }) {
16 | if self.enabled {
17 | Text("enabled")
18 | } else {
19 | Text("disabled")
20 | }
21 | }
22 | }
23 | }
24 | }
25 | }
26 | }
27 |
28 | struct LinkedStateChangeView_Previews: PreviewProvider {
29 | static var previews: some View {
30 | LinkedStateChangeView()
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/ListExampleView.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import SwiftUI
5 |
6 | struct ListExampleView: View {
7 |
8 | var names = ["Alice", "Bob"]
9 |
10 | struct Person {
11 | var name: String
12 | }
13 |
14 | var personArray = [Person(name: "Alice"), Person(name: "Bob")]
15 |
16 | var contactsArray = [Contact.alice, Contact.bob]
17 |
18 | var body: some View {
19 | Group {
20 | NavigationLink("List of Views", destination:
21 | List {
22 | Text("Alice")
23 | Text("Bob")
24 | })
25 | NavigationLink("List of Array of simple values", destination:
26 | List(names, id: \.self) { name in
27 | Text(name)
28 | })
29 | NavigationLink("List of Array", destination:
30 | List(personArray, id: \.name) { person in
31 | Text(person.name)
32 | })
33 | NavigationLink("List of Array with Identifiable objects", destination:
34 | List(contactsArray) { contact in
35 | Text(contact.name)
36 | })
37 | NavigationLink("Editable List with Binding", destination:
38 | EditableListExample())
39 | }
40 | }
41 | }
42 |
43 | struct ListExampleView_Previews: PreviewProvider {
44 | static var previews: some View {
45 | NavigationView {
46 | Form {
47 | ListExampleView()
48 | }
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/MainView.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import SwiftUI
5 |
6 | struct MainView: View {
7 |
8 | @State var currentTab = Tab.widgets
9 |
10 | enum Tab {
11 | case widgets
12 | case landmarks
13 | }
14 |
15 | var body: some View {
16 | TabView(selection: $currentTab) {
17 | AllWidgetsView().tabItem {
18 | VStack {
19 | Image(systemName: "rectangle.grid.3x2")
20 | Text("Widgets")
21 | }
22 | }.tag(Tab.widgets)
23 | CategoryHome()
24 | .environmentObject(UserData())
25 | .tabItem {
26 | VStack {
27 | Image(systemName: "location.north")
28 | Text("Landmarks")
29 | }
30 | }.tag(Tab.landmarks)
31 | }
32 | }
33 | }
34 |
35 | struct MainView_Previews: PreviewProvider {
36 | static var previews: some View {
37 | MainView()
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/MenuButtonExampleView.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import SwiftUI
5 |
6 | struct MenuButtonExampleView: View {
7 |
8 | var body: some View {
9 | Color.yellow
10 | .ignoresSafeArea()
11 | .overlay(self.menuButton, alignment: .topTrailing)
12 | }
13 |
14 | @ViewBuilder var menuButton: some View {
15 | Menu(
16 | content: {
17 | Button(
18 | action: {
19 | debugPrint("Action")
20 | },
21 | label: {
22 | Label("Action", systemImage: "xmark")
23 | }
24 | )
25 |
26 | },
27 | label: {
28 | Button(
29 | action: {},
30 | label: {
31 | ZStack {
32 | Circle()
33 | .foregroundColor(.green)
34 | .frame(width: 30, height: 30)
35 | Image(systemName: "ellipsis")
36 | .foregroundColor(.white)
37 | }
38 | }
39 | )
40 | .padding(20)
41 | }
42 | )
43 | }
44 |
45 | }
46 |
47 | struct MenuButtonExampleView_Previews: PreviewProvider {
48 |
49 | static var previewView: some View {
50 | MenuButtonExampleView()
51 | }
52 |
53 | static var previews: some View {
54 | previewView
55 | .previewDevice(PreviewDevice(rawValue: "iPhone SE (2nd generation)"))
56 | previewView
57 | .previewDevice(PreviewDevice(rawValue: "iPhone 12"))
58 | previewView
59 | .previewDevice(PreviewDevice(rawValue: "iPhone 12 Pro Max"))
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/ModalPickerView.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import SwiftUI
5 |
6 | /// see https://stackoverflow.com/questions/61923913/modal-picker-not-scrolling-right-swiftui
7 | struct ModalPickerView: View {
8 |
9 | @State var showingModal = false
10 | @State var hours: Int = 0
11 | @State var minutes: Int = 0
12 |
13 | var body: some View {
14 |
15 | ZStack {
16 | VStack {
17 | Button("Show me") {
18 | self.showingModal = true
19 | }
20 |
21 | if showingModal {
22 | VStack(alignment: .center) {
23 | ZStack {
24 | Color.black.opacity(0.4)
25 | .edgesIgnoringSafeArea(.vertical)
26 |
27 | VStack(spacing: 20) {
28 | Text("Time between meals")
29 | .bold().padding()
30 | .frame(maxWidth: .infinity)
31 | .background(Color.yellow)
32 | .foregroundColor(Color.white)
33 |
34 | HStack {
35 | Picker("", selection: $hours) {
36 | ForEach(0 ..< 4, id: \.self) { i in
37 | Text("\(i) hours").tag(i)
38 | }
39 | }
40 | .frame(width: 150, height: 120)
41 | .clipped()
42 |
43 | Picker("", selection: $minutes) {
44 | ForEach(0 ..< 60, id: \.self) { i in
45 | Text("\(i) min").tag(i)
46 | }
47 | }
48 | .frame(width: 150, height: 120)
49 | .clipped()
50 | }
51 |
52 | Spacer()
53 |
54 | Button(action: {
55 | self.showingModal = false
56 | }) {
57 | Text("Close")
58 | }.padding()
59 | }
60 | .frame(width: 300, height: 300)
61 | .background(Color.white)
62 | .mask(RoundedRectangle(cornerRadius: 20))
63 | .shadow(radius: 20)
64 | }
65 | }
66 | }
67 | }
68 | }
69 | }
70 | }
71 |
72 | struct ModalPickerView_Previews: PreviewProvider {
73 | static var previews: some View {
74 | ModalPickerView()
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/NavigationViewExample.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import SwiftUI
5 |
6 | struct NavigationContentView: View {
7 |
8 | @Environment(\.presentationMode) var presentationMode
9 |
10 | var body: some View {
11 | VStack(spacing: 15.0) {
12 | Text("Example Content")
13 | NavigationLink(destination: NavigationContentView()) {
14 | Text("Navigate to other page")
15 | }
16 | Button("Go Back") {
17 | self.presentationMode.wrappedValue.dismiss()
18 | }
19 | NavigationLink(destination: ProgrammaticNavigationExampleView()) {
20 | Text("Programmatic Navigation Example")
21 | }
22 | }
23 | .navigationBarTitle("Content")
24 | }
25 |
26 | }
27 |
28 | struct NavigationViewExample_Previews: PreviewProvider {
29 | static var previews: some View {
30 | NavigationView {
31 | NavigationContentView()
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/OptionalsExampleView.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import SwiftUI
5 |
6 | struct Person {
7 | var name: String
8 | }
9 |
10 | struct OptionalsExampleView: View {
11 |
12 | var person: Person? = Person(name: "Bob")
13 |
14 | var body: some View {
15 | VStack {
16 | if let person = person {
17 | VStack {
18 | Text("Name:")
19 | Text(person.name)
20 | }
21 | } else {
22 | Text("No person available")
23 | }
24 | }
25 |
26 | }
27 |
28 | }
29 |
30 | struct OptionalsExampleView_Previews: PreviewProvider {
31 | static var previews: some View {
32 | Group {
33 | OptionalsExampleView()
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/PickerExampleView.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import SwiftUI
5 |
6 | enum Weather: CaseIterable {
7 | case sunny
8 | case cloudy
9 | case rainy
10 | }
11 |
12 | extension Weather {
13 |
14 | var image: Image {
15 | switch self {
16 |
17 | case .sunny:
18 | return Image(systemName: "sun.max")
19 | case .cloudy:
20 | return Image(systemName: "cloud")
21 | case .rainy:
22 | return Image(systemName: "cloud.rain")
23 | }
24 | }
25 |
26 | }
27 |
28 | struct PickerExampleView: View {
29 |
30 | @State var selection = Weather.sunny
31 |
32 | var body: some View {
33 | Section {
34 | self.examplePicker
35 | }
36 |
37 | Section {
38 | self.examplePicker
39 | .pickerStyle(SegmentedPickerStyle())
40 | }
41 | .padding()
42 | }
43 |
44 | var examplePicker: some View {
45 | Picker(
46 | selection: $selection,
47 | label: Text("Weather"),
48 | content: {
49 | ForEach(Weather.allCases, id: \.self) { weather in
50 | weather.image.tag(weather)
51 | }
52 | }
53 | )
54 | }
55 |
56 | }
57 |
58 | struct PickerExampleView_Previews: PreviewProvider {
59 | static var previews: some View {
60 | PickerExampleView()
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/PlayersProgressView.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import SwiftUI
5 |
6 | struct PlayerLoopView: View {
7 | @ObservedObject var player: Player
8 |
9 | var body: some View {
10 | ZStack {
11 | Circle()
12 | .stroke(style: StrokeStyle(lineWidth: 10.0))
13 | .foregroundColor(Color.purple)
14 | .opacity(0.3)
15 |
16 | Circle()
17 | .trim(
18 | from: 0,
19 | to: player.isPlaying ? 1.0 : 0.0
20 | )
21 | .stroke(
22 | style: StrokeStyle(lineWidth: 10.0, lineCap: .round, lineJoin: .round)
23 | )
24 | .animation(
25 | player.isPlaying ?
26 | Animation.linear(duration: player.duration)
27 | .repeatForever(autoreverses: false) : nil
28 | )
29 | .rotationEffect(Angle(degrees: -90))
30 | .foregroundColor(Color.purple)
31 | }
32 | .frame(width: 100, height: 100)
33 | .padding()
34 | }
35 | }
36 |
37 | struct PlayersProgressView: View {
38 | @ObservedObject var engine = Engine()
39 |
40 | var body: some View {
41 | NavigationView {
42 | VStack {
43 | ForEach(self.engine.players) { player in
44 | HStack {
45 | Text("Player")
46 | PlayerLoopView(player: player)
47 | }
48 | }
49 | }
50 | .navigationBarItems(trailing:
51 | HStack {
52 | Button("Add Player") {
53 | self.engine.addPlayer()
54 | }
55 | Button("Play All") {
56 | self.engine.playAll()
57 | }
58 | Button("Stop All") {
59 | self.engine.stopAll()
60 | }
61 | }.padding()
62 | )
63 | }
64 | }
65 | }
66 |
67 | class Player: ObservableObject, Identifiable {
68 | var id = UUID()
69 | @Published var isPlaying: Bool = false
70 | var duration: Double = 10
71 |
72 | func play() {
73 | self.isPlaying = true
74 | }
75 |
76 | func stop() {
77 | self.isPlaying = false
78 | }
79 | }
80 |
81 | class Engine: ObservableObject {
82 | @Published var players = [Player]()
83 |
84 | func addPlayer() {
85 | let player = Player()
86 | players.append(player)
87 | DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
88 | player.isPlaying = true
89 | }
90 | }
91 |
92 | func stopAll() {
93 | self.players.forEach { $0.stop() }
94 | }
95 |
96 | func playAll() {
97 | self.players.forEach { $0.play() }
98 | }
99 | }
100 |
101 | struct PlayersProgressView_Previews: PreviewProvider {
102 | static var previews: some View {
103 | PlayersProgressView()
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/PlaygroundView.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import SwiftUI
5 |
6 | struct PlaygroundView: View {
7 | var body: some View {
8 | Text("Hello, World!")
9 | }
10 | }
11 |
12 | struct PlaygroundView_Previews: PreviewProvider {
13 | static var previews: some View {
14 | PlaygroundView()
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/ProgrammaticNavigationExampleView.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import SwiftUI
5 |
6 | /// Example for navigation using NavigationLink tag/selection
7 | /// inspired by https://stackoverflow.com/questions/59040566/navigationlink-ontapgesture-and-navigation-not-firing-consistently
8 | /// you can't build programatic navigation using that, see: https://stackoverflow.com/questions/61920405/programatic-navigation-for-navigationview-using-tag-selection-or-isactive#comment109519536_61920405
9 | struct ProgrammaticNavigationExampleView: View {
10 |
11 | enum NavDestination {
12 | case red
13 | case green
14 | }
15 |
16 | @State var destination: NavDestination?
17 |
18 | var body: some View {
19 | VStack(spacing: 20) {
20 | NavigationLink(destination: Color.red, tag: NavDestination.red, selection: $destination) {
21 | EmptyView()
22 | }
23 | NavigationLink(destination: Color.green, tag: NavDestination.green, selection: $destination) {
24 | EmptyView()
25 | }
26 | Text("Page 1")
27 | Button("Show random page") {
28 | self.destination = [.red, .green].randomElement()!
29 | }
30 | }
31 | .navigationBarTitle("Page 1")
32 | }
33 |
34 | }
35 |
36 | struct ProgrammaticNavigationExample_Previews: PreviewProvider {
37 | static var previews: some View {
38 | NavigationView {
39 | ProgrammaticNavigationExampleView()
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/QRCodeScannerExampleView.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import CodeScanner
5 | import SwiftUI
6 |
7 | struct NextView: View {
8 |
9 | var scannedCode: String
10 |
11 | var body: some View {
12 | Text(scannedCode)
13 | }
14 | }
15 |
16 | /// see https://stackoverflow.com/a/61968288/128083
17 | struct QRCodeScannerExampleView: View {
18 | @State var isPresentingScanner = false
19 | @State var scannedCode: String?
20 |
21 | var body: some View {
22 | NavigationView {
23 | VStack(spacing: 10) {
24 | if self.scannedCode != nil {
25 | NavigationLink("Next page", destination: NextView(scannedCode: scannedCode!), isActive: .constant(true)).hidden()
26 | }
27 | Button("Scan Code") {
28 | self.isPresentingScanner = true
29 | }
30 | .sheet(isPresented: $isPresentingScanner) {
31 | self.scannerSheet
32 | }
33 | Text("Scan a QR code to begin")
34 | }
35 |
36 | }
37 | }
38 |
39 | var scannerSheet: some View {
40 | CodeScannerView(
41 | codeTypes: [.qr],
42 | completion: { result in
43 | if case let .success(code) = result {
44 | self.scannedCode = code
45 | self.isPresentingScanner = false
46 | }
47 | }
48 | )
49 | }
50 | }
51 |
52 | struct QRCodeScannerExampleView_Previews: PreviewProvider {
53 | static var previews: some View {
54 | QRCodeScannerExampleView()
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/SceneViewExample.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import SceneKit
5 | import SwiftUI
6 |
7 | class RendererDelegate: NSObject, ObservableObject, SCNSceneRendererDelegate {
8 |
9 | func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {
10 | print(renderer.currentViewport.size)
11 | }
12 |
13 | }
14 |
15 | struct SceneViewExample: View {
16 |
17 | @StateObject var delegate = RendererDelegate()
18 |
19 | var body: some View {
20 | SceneView(scene: self.scene, delegate: self.delegate)
21 | .ignoresSafeArea()
22 | }
23 |
24 | var scene: SCNScene {
25 | let scene = SCNScene(named: "art.scnassets/ship.scn")!
26 |
27 | let ship = scene.rootNode.childNode(withName: "ship", recursively: true)!
28 |
29 | // animate the 3d object
30 | ship.runAction(SCNAction.repeatForever(SCNAction.rotateBy(x: 0, y: 2, z: 0, duration: 1)))
31 |
32 | return scene
33 | }
34 | }
35 |
36 | struct SceneViewExample_Previews: PreviewProvider {
37 | static var previews: some View {
38 | SceneViewExample()
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/ScrollViewExample.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import SwiftUI
5 |
6 | struct ScrollViewExample: View {
7 | var body: some View {
8 | ScrollView(.horizontal, showsIndicators: true, content: {
9 | HStack {
10 | ForEach(1 ..< 14, id: \.self) { day in
11 | VStack {
12 | Image(systemName: "sun.max")
13 | .font(.system(size: 30))
14 | .padding()
15 | Text("Day \(day)")
16 | }
17 | }
18 | }
19 | .padding()
20 | })
21 | }
22 | }
23 |
24 | struct ScrollViewExample_Previews: PreviewProvider {
25 | static var previews: some View {
26 | ScrollViewExample()
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/SelectionEffectView.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import SwiftUI
5 |
6 | // see https://stackoverflow.com/questions/61925943/is-there-a-way-to-blur-the-background-of-a-tapped-element-in-swiftui
7 | struct SelectionEffectView: View {
8 | let images = ["sun.max", "moon", "cloud"]
9 |
10 | @State private var selected: String? = nil
11 |
12 | var body: some View {
13 | VStack {
14 | ForEach(images, id: \.self) { name in
15 | Image(systemName: name)
16 | .resizable()
17 | .aspectRatio(contentMode: .fit)
18 | .frame(width: 100, height: 100)
19 | .onTapGesture {
20 | if self.selected == name {
21 | self.selected = nil
22 | } else {
23 | self.selected = name
24 | }
25 | }
26 | .blur(radius: self.selected != nil && self.selected != name ? 10 : 0)
27 | .scaleEffect(self.selected == name ? 1.2 : 1)
28 | }
29 | }
30 | .animation(.spring())
31 | }
32 | }
33 |
34 | struct SelectionEffectView_Previews: PreviewProvider {
35 | static var previews: some View {
36 | SelectionEffectView()
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/ShadowsView.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import SwiftUI
5 |
6 | struct ShadowsView: View {
7 | var body: some View {
8 | Color.yellow
9 | .ignoresSafeArea()
10 | .overlay(self.scrollView, alignment: .bottom)
11 | }
12 |
13 | var scrollView: some View {
14 | VStack {
15 | ForEach(1 ... 3, id: \.self) { _ in
16 | Color.white
17 | .frame(height: 100)
18 | .cornerRadius(10)
19 | .shadow(color: Color.red, radius: 30, x: 0, y: 0)
20 | }
21 | }
22 | .compositingGroup()
23 | .padding()
24 | .frame(height: 200, alignment: .top)
25 | }
26 | }
27 |
28 | struct ShadowsView_Previews: PreviewProvider {
29 | static var previews: some View {
30 | ShadowsView()
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/ShareSheetExampleView.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import SwiftUI
5 |
6 | import SwiftUI
7 | import UIKit
8 |
9 | struct ActivityViewController: UIViewControllerRepresentable {
10 |
11 | var activityItems: [Any]
12 | var applicationActivities: [UIActivity]?
13 |
14 | func makeUIViewController(context: UIViewControllerRepresentableContext) -> UIActivityViewController {
15 | let controller = UIActivityViewController(activityItems: activityItems, applicationActivities: applicationActivities)
16 | return controller
17 | }
18 |
19 | func updateUIViewController(_ uiViewController: UIActivityViewController, context: UIViewControllerRepresentableContext) {}
20 |
21 | }
22 |
23 | /// see also https://stackoverflow.com/questions/56533564/showing-uiactivityviewcontroller-in-switui
24 | struct ShareSheetExampleView: View {
25 |
26 | @State private var isSharePresented: Bool = false
27 |
28 | var body: some View {
29 | Button("Share") {
30 | self.isSharePresented = true
31 | }
32 | .sheet(
33 | isPresented: $isSharePresented,
34 | onDismiss: {
35 | print("Dismissed")
36 | },
37 | content: {
38 | ActivityViewController(activityItems: [URL(string: "https://www.apple.com")!])
39 | }
40 | )
41 | }
42 | }
43 |
44 | struct ShareSheetExampleView_Previews: PreviewProvider {
45 | static var previews: some View {
46 | ShareSheetExampleView()
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/SimpleGridViewExample.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import SwiftUI
5 |
6 | extension Array {
7 | func chunks(_ size: Int) -> [[Element]] {
8 | stride(from: 0, to: self.count, by: size).map { ($0 ..< Swift.min($0 + size, self.count)).map { self[$0] } }
9 | }
10 | }
11 |
12 | struct Product: Identifiable, Hashable {
13 | var id = UUID()
14 | var name: String
15 | }
16 |
17 | struct SimpleGridViewExample: View {
18 |
19 | var products = [Product(name: "p1"), Product(name: "p2"), Product(name: "p3"), Product(name: "p4"), Product(name: "p5")]
20 |
21 | var body: some View {
22 | ScrollView {
23 | VStack(alignment: .leading) {
24 | ForEach(products.chunks(2), id: \.self) { chunk in
25 | HStack {
26 | ForEach(chunk, id: \.self) { product in
27 | VStack {
28 |
29 | Image(systemName: "sun.max")
30 | .resizable()
31 | .frame(maxWidth: 100, maxHeight: 100)
32 | .aspectRatio(contentMode: .fit)
33 |
34 | Text(product.name)
35 | }
36 | }
37 | }
38 | }
39 | }
40 | }
41 | }
42 |
43 | }
44 |
45 | struct SimpleGridViewExample_Previews: PreviewProvider {
46 | static var previews: some View {
47 | SimpleGridViewExample()
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/SizePreferenceExampleView.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import SwiftUI
5 |
6 | struct SizePreferenceKey: PreferenceKey {
7 | static let defaultValue: CGSize = .zero
8 |
9 | static func reduce(value: inout CGSize, nextValue: () -> CGSize) {
10 | value = nextValue()
11 | }
12 | }
13 |
14 | struct SizeReaderView: View {
15 |
16 | var body: some View {
17 | GeometryReader { geometry in
18 | Color.clear.preference(
19 | key: SizePreferenceKey.self,
20 | value: geometry.size
21 | )
22 | }
23 | }
24 | }
25 |
26 | struct SizePreferenceExampleView: View {
27 |
28 | @State var text = "Lorem ipsum"
29 | @State var size: CGSize = .zero
30 |
31 | var body: some View {
32 | VStack {
33 | Button(
34 | action: {
35 | self.text = Array(repeating: "Lorem ipsum", count: Int.random(in: 1 ... 10)).joined(separator: " ")
36 | },
37 | label: {
38 | Text(self.text)
39 | .padding()
40 | }
41 | )
42 | .background(SizeReaderView())
43 | .background(Color.yellow)
44 | .frame(maxWidth: 200)
45 |
46 | Text(String(describing: self.size))
47 | .frame(width: self.size.width, height: self.size.height)
48 | .background(Color.green)
49 |
50 | }
51 | .onPreferenceChange(SizePreferenceKey.self) { size in
52 | self.size = size
53 | }
54 | }
55 |
56 | }
57 |
58 | struct SizePreferenceExampleView_Previews: PreviewProvider {
59 |
60 | static var previews: some View {
61 | SizePreferenceExampleView()
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/SizedUIViewRepresentableView.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import SwiftUI
5 |
6 | // How do you pass the size from UIKit up to SwiftUI in a UIViewRepresentable?
7 | // https://stackoverflow.com/questions/61832221/passing-uikit-uiview-size-to-swiftui-in-uiviewrepresentable
8 | struct YellowBoxView: UIViewRepresentable {
9 |
10 | func makeUIView(context: Context) -> YellowBoxUIKitView {
11 | let view = YellowBoxUIKitView()
12 | view.setContentHuggingPriority(.required, for: .horizontal)
13 | view.setContentHuggingPriority(.required, for: .vertical)
14 | return view
15 | }
16 |
17 | func updateUIView(_ uiView: YellowBoxUIKitView, context: Context) {}
18 |
19 | }
20 |
21 | /// Some UIKit view that wants to have a specific size
22 | class YellowBoxUIKitView: UIView {
23 |
24 | init() {
25 | super.init(frame: .zero)
26 | self.backgroundColor = .yellow
27 | }
28 |
29 | @available(*, unavailable)
30 | required init?(coder: NSCoder) {
31 | fatalError("init(coder:) is not supported")
32 | }
33 |
34 | override var intrinsicContentSize: CGSize {
35 | CGSize(width: 100, height: 100)
36 | }
37 |
38 | }
39 |
40 | struct SizedUIViewRepresentableView: View {
41 | var body: some View {
42 | YellowBoxView()
43 | }
44 | }
45 |
46 | struct SizedUIViewRepresentableView_Previews: PreviewProvider {
47 | static var previews: some View {
48 | SizedUIViewRepresentableView()
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/SliderExampleView.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import SwiftUI
5 |
6 | struct SliderExampleView: View {
7 |
8 | @State var value = 50.0
9 |
10 | var body: some View {
11 | Slider(value: $value, in: 1.0 ... 100.0) {
12 | Text("Value \(value)")
13 | }
14 | }
15 | }
16 |
17 | struct SliderExample_Previews: PreviewProvider {
18 | static var previews: some View {
19 | SliderExampleView()
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/StateExampleView.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import SwiftUI
5 |
6 | struct CounterView: View {
7 | let name: String
8 | @State var count = 1
9 |
10 | var body: some View {
11 | Button("Counter \(name) \(count)") { self.count += 1 }
12 | .padding()
13 | .border(Color.blue)
14 | }
15 | }
16 |
17 | /// Demonstrates that SwiftUI state is coupled to view appearance / disappearance
18 | struct StateExampleView: View {
19 |
20 | @State var outerCount = 1
21 | @State var showB = false
22 |
23 | var body: some View {
24 | VStack {
25 |
26 | Button("Outer Counter \(outerCount)") { self.outerCount += 1 }
27 | .padding()
28 |
29 | Toggle(isOn: $showB) {
30 | Text("Toggle A/B")
31 | }
32 |
33 | if showB {
34 | CounterView(name: "B")
35 | } else {
36 | CounterView(name: "A")
37 | }
38 |
39 | if showB {
40 | CounterView(name: "B")
41 | } else {
42 | CounterView(name: "A")
43 | }
44 |
45 | CounterView(name: "C")
46 |
47 | }
48 | .border(Color.red)
49 | .padding()
50 | }
51 | }
52 |
53 | struct StateExampleView_Previews: PreviewProvider {
54 | static var previews: some View {
55 | StateExampleView()
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/StepperExampleView.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import SwiftUI
5 |
6 | struct StepperExampleView: View {
7 | @State var value = 50
8 |
9 | var body: some View {
10 | Stepper("Value \(value)", value: $value, in: 1 ... 100)
11 | }
12 | }
13 |
14 | struct StepperExampleView_Previews: PreviewProvider {
15 | static var previews: some View {
16 | StepperExampleView()
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/StrangeStateGlitchExampleView copy.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import SwiftUI
5 |
6 | class FlipModel: ObservableObject {
7 | @Published var flipped = true {
8 | didSet {
9 | print("new value for flipped: \(self.flipped)")
10 | self.counterModel?.counter += 1
11 | }
12 | }
13 |
14 | var counterModel: CounterModel?
15 | }
16 |
17 | class CounterModel: ObservableObject {
18 | @Published var counter = 0
19 | }
20 |
21 | struct StrangeStateGlitchExampleView: View {
22 |
23 | @StateObject var flipModel = FlipModel()
24 | @StateObject var counterModel = CounterModel()
25 |
26 | // MARK: - View
27 |
28 | var body: some View {
29 | VStack(spacing: 10) {
30 | Button("Toggle value from SwiftUI") {
31 | self.flipModel.flipped.toggle()
32 | }
33 | Button("Toggle value from external thread") {
34 | DispatchQueue.main.async {
35 | self.flipModel.flipped.toggle()
36 | }
37 | }
38 | OtherView(flipModel: self.flipModel)
39 | Text("Flipped (ExampleView): \(String(describing: flipModel.flipped))")
40 | Text("Flipped \(self.counterModel.counter) times")
41 | }
42 | .onAppear {
43 | self.flipModel.counterModel = counterModel
44 | }
45 |
46 | }
47 |
48 | }
49 |
50 | struct OtherView: View {
51 |
52 | @ObservedObject var flipModel: FlipModel
53 |
54 | var body: some View {
55 | Text("Flipped (OtherView): \(String(describing: flipModel.flipped))")
56 | }
57 | }
58 |
59 | struct StrangeStateGlitchExampleView_Previews: PreviewProvider {
60 |
61 | static var previews: some View {
62 | StrangeStateGlitchExampleView()
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/SwiftUISwitchView.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import SwiftUI
5 |
6 | enum Status {
7 | case loggedIn, loggedOut, expired
8 | }
9 |
10 | struct SwiftUISwitchView: View {
11 |
12 | @State var userStatus: Status = .loggedIn
13 |
14 | var body: some View {
15 | VStack {
16 | Text("switch for function builders was added in Xcode 12")
17 |
18 | switch self.userStatus {
19 | case .loggedIn:
20 | Text("Welcome!")
21 | case .loggedOut:
22 | Image(systemName: "person.fill")
23 | case .expired:
24 | Text("Session expired")
25 | }
26 |
27 | }
28 | }
29 | }
30 |
31 | struct SwitchUsageInSwiftUI_Previews: PreviewProvider {
32 | static var previews: some View {
33 | SwiftUISwitchView()
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/TextExampleView.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import SwiftUI
5 |
6 | struct TextExampleView: View {
7 | var body: some View {
8 | VStack(spacing: 20.0) {
9 | Text("Hamlet")
10 | .font(.title)
11 |
12 | Text("by William Shakespeare")
13 | .font(.system(size: 12, weight: .light, design: .serif))
14 | .italic()
15 |
16 | Text("To be, or not to be, that is the question:")
17 | .frame(width: 100)
18 |
19 | Text("Brevity is the soul of wit.")
20 | .frame(width: 100)
21 | .lineLimit(1)
22 | }
23 | }
24 | }
25 |
26 | struct TextExampleView_Previews: PreviewProvider {
27 | static var previews: some View {
28 | TextExampleView()
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/TextFieldExampleView.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import SwiftUI
5 |
6 | struct TextFieldExampleView: View {
7 |
8 | @State var value = "ABC"
9 | @State var numberValue: Int = 123
10 |
11 | var body: some View {
12 | VStack(spacing: 20) {
13 | VStack(alignment: .leading) {
14 | Text("TextField")
15 | TextField("Value", text: $value)
16 | .textFieldStyle(RoundedBorderTextFieldStyle())
17 | }
18 |
19 | VStack(alignment: .leading) {
20 | Text("TextField with formatter")
21 | TextField("Number value", value: $numberValue, formatter: NumberFormatter())
22 | .textFieldStyle(RoundedBorderTextFieldStyle())
23 | }
24 |
25 | VStack(alignment: .leading) {
26 | Text("SecureField")
27 | SecureField("Value", text: $value)
28 | .textFieldStyle(RoundedBorderTextFieldStyle())
29 | }
30 | }
31 | }
32 | }
33 |
34 | struct TextFieldExampleView_Previews: PreviewProvider {
35 | static var previews: some View {
36 | TextFieldExampleView()
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/ToggleExampleView.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import SwiftUI
5 |
6 | struct ToggleExampleView: View {
7 | @State var value = true
8 |
9 | var body: some View {
10 | Toggle(isOn: $value) {
11 | Text("Value \(String(describing: value))")
12 | }
13 | }
14 | }
15 |
16 | struct ToggleExampleView_Previews: PreviewProvider {
17 | static var previews: some View {
18 | ToggleExampleView()
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/UserDefaultsExampleView.swift:
--------------------------------------------------------------------------------
1 | // SwiftUIPlayground
2 | // https://github.com/ralfebert/SwiftUIPlayground/
3 |
4 | import Combine
5 | import SwiftUI
6 |
7 | /// see https://stackoverflow.com/questions/56822195/how-do-i-use-userdefaults-with-swiftui
8 | /// this assumes the UserDefaults are only changed using the ObservableObject wrapper object
9 | @propertyWrapper
10 | struct UserDefault {
11 | let key: String
12 | let defaultValue: T
13 |
14 | init(_ key: String, defaultValue: T) {
15 | self.key = key
16 | self.defaultValue = defaultValue
17 | }
18 |
19 | var wrappedValue: T {
20 | get {
21 | UserDefaults.standard.object(forKey: self.key) as? T ?? self.defaultValue
22 | }
23 | set {
24 | UserDefaults.standard.set(newValue, forKey: self.key)
25 | }
26 | }
27 | }
28 |
29 | class TextSettings: ObservableObject {
30 |
31 | @UserDefault("TextCount", defaultValue: 1)
32 | var count: Int {
33 | willSet {
34 | self.objectWillChange.send()
35 | }
36 | }
37 |
38 | @UserDefault("TextUppercase", defaultValue: true)
39 | var upcase: Bool {
40 | willSet {
41 | self.objectWillChange.send()
42 | }
43 | }
44 |
45 | }
46 |
47 | struct TextSettingsView: View {
48 | @EnvironmentObject var settings: TextSettings
49 |
50 | var body: some View {
51 | Form {
52 | Picker(selection: $settings.count, label:
53 | Text("Text Repeat Count")) {
54 | ForEach(Array(1 ... 5), id: \.self) { value in
55 | Text(String(value)).tag(value)
56 | }
57 | }
58 |
59 | Toggle(isOn: $settings.upcase) {
60 | Text("Uppercase")
61 | }
62 |
63 | }
64 | }
65 | }
66 |
67 | /// Example for a shared @EnvironmentObject/ObservableObject
68 | /// see https://stackoverflow.com/questions/61941322/swiftui-updating-text-after-changes-in-settings/61943534#61943534
69 | struct UserDefaultsExampleView: View {
70 | @EnvironmentObject var settings: TextSettings
71 |
72 | var body: some View {
73 | Text(self.text)
74 | .navigationBarItems(trailing: NavigationLink("Settings", destination: TextSettingsView()))
75 | }
76 |
77 | var text: String {
78 | var str = String(repeating: "Hello ", count: Int(settings.count))
79 | if self.settings.upcase {
80 | str = str.uppercased()
81 | }
82 | return str
83 | }
84 |
85 | }
86 |
87 | struct TextWithSettingExampleView_Previews: PreviewProvider {
88 | static var previews: some View {
89 | NavigationView {
90 | UserDefaultsExampleView()
91 | }
92 | .environmentObject(TextSettings())
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/art.scnassets/ship.scn:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralfebert/SwiftUIPlayground/b9c3533aba05fa5412dcddcf37358e8960ccab72/SwiftUIPlayground/Views/art.scnassets/ship.scn
--------------------------------------------------------------------------------
/SwiftUIPlayground/Views/art.scnassets/texture.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralfebert/SwiftUIPlayground/b9c3533aba05fa5412dcddcf37358e8960ccab72/SwiftUIPlayground/Views/art.scnassets/texture.png
--------------------------------------------------------------------------------
/docs/Button.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralfebert/SwiftUIPlayground/b9c3533aba05fa5412dcddcf37358e8960ccab72/docs/Button.png
--------------------------------------------------------------------------------
/docs/DatePicker.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralfebert/SwiftUIPlayground/b9c3533aba05fa5412dcddcf37358e8960ccab72/docs/DatePicker.png
--------------------------------------------------------------------------------
/docs/EditButton.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralfebert/SwiftUIPlayground/b9c3533aba05fa5412dcddcf37358e8960ccab72/docs/EditButton.png
--------------------------------------------------------------------------------
/docs/Form.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralfebert/SwiftUIPlayground/b9c3533aba05fa5412dcddcf37358e8960ccab72/docs/Form.png
--------------------------------------------------------------------------------
/docs/Image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralfebert/SwiftUIPlayground/b9c3533aba05fa5412dcddcf37358e8960ccab72/docs/Image.png
--------------------------------------------------------------------------------
/docs/List.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralfebert/SwiftUIPlayground/b9c3533aba05fa5412dcddcf37358e8960ccab72/docs/List.png
--------------------------------------------------------------------------------
/docs/NavigationView.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralfebert/SwiftUIPlayground/b9c3533aba05fa5412dcddcf37358e8960ccab72/docs/NavigationView.png
--------------------------------------------------------------------------------
/docs/Picker.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralfebert/SwiftUIPlayground/b9c3533aba05fa5412dcddcf37358e8960ccab72/docs/Picker.png
--------------------------------------------------------------------------------
/docs/ScrollView.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralfebert/SwiftUIPlayground/b9c3533aba05fa5412dcddcf37358e8960ccab72/docs/ScrollView.png
--------------------------------------------------------------------------------
/docs/Section.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralfebert/SwiftUIPlayground/b9c3533aba05fa5412dcddcf37358e8960ccab72/docs/Section.png
--------------------------------------------------------------------------------
/docs/SecureField.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralfebert/SwiftUIPlayground/b9c3533aba05fa5412dcddcf37358e8960ccab72/docs/SecureField.png
--------------------------------------------------------------------------------
/docs/Slider.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralfebert/SwiftUIPlayground/b9c3533aba05fa5412dcddcf37358e8960ccab72/docs/Slider.png
--------------------------------------------------------------------------------
/docs/Stepper.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralfebert/SwiftUIPlayground/b9c3533aba05fa5412dcddcf37358e8960ccab72/docs/Stepper.png
--------------------------------------------------------------------------------
/docs/TabView.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralfebert/SwiftUIPlayground/b9c3533aba05fa5412dcddcf37358e8960ccab72/docs/TabView.png
--------------------------------------------------------------------------------
/docs/Text.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralfebert/SwiftUIPlayground/b9c3533aba05fa5412dcddcf37358e8960ccab72/docs/Text.png
--------------------------------------------------------------------------------
/docs/TextField.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralfebert/SwiftUIPlayground/b9c3533aba05fa5412dcddcf37358e8960ccab72/docs/TextField.png
--------------------------------------------------------------------------------
/docs/Toggle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralfebert/SwiftUIPlayground/b9c3533aba05fa5412dcddcf37358e8960ccab72/docs/Toggle.png
--------------------------------------------------------------------------------
/docs/overview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ralfebert/SwiftUIPlayground/b9c3533aba05fa5412dcddcf37358e8960ccab72/docs/overview.png
--------------------------------------------------------------------------------
/docs/views.csv:
--------------------------------------------------------------------------------
1 | swiftui_view,uikit_view,example
2 | Button,UIButton,ButtonExampleView
3 | DatePicker,UIDatePicker,DatePickerExampleView
4 | EditButton,,EditButtonExampleView
5 | Form,,ContactFormView
6 | Image,UIImageView,ImageExampleView
7 | List,UITableView,ListExampleView
8 | NavigationView,UINavigationController,NavigationViewExample
9 | Picker,UIPickerView,PickerExampleView
10 | ScrollView,UIScrollView,ScrollViewExample
11 | Section,,ContactFormView
12 | SecureField,UITextField,TextFieldExampleView
13 | Slider,UISlider,SliderExampleView
14 | Stepper,UIStepper,StepperExampleView
15 | TabView,UITabBarController,MainView
16 | Text,UILabel,TextExampleView
17 | TextField,UITextField,TextFieldExampleView
18 | Toggle,UISwitch,ToggleExampleView
19 |
--------------------------------------------------------------------------------
/docs/views.rb:
--------------------------------------------------------------------------------
1 | #!/bin/env ruby
2 |
3 | require 'csv'
4 | require 'mdtable'
5 | require 'pry'
6 | require 'active_support'
7 | require 'active_support/core_ext/object/blank'
8 |
9 | views = CSV.parse(File.read("views.csv"), headers: true).map { |row| OpenStruct.new(row) }
10 |
11 | links = []
12 |
13 | text = ""
14 |
15 | data = [['SwiftUI view', 'UIKit View', 'Example']] + views.map { |view|
16 | swiftui_link = "https://developer.apple.com/documentation/swiftui/#{view.swiftui_view.downcase}"
17 |
18 | swiftui_handle = "swiftui-#{view.swiftui_view.downcase}"
19 | example_handle = "example-#{view.swiftui_view.downcase}"
20 | example_link = "https://github.com/ralfebert/SwiftUIPlayground/blob/master/SwiftUIPlayground/Views/#{view.example}.swift"
21 |
22 | links << "[#{swiftui_handle}]: #{swiftui_link}"
23 | links << "[#{example_handle}]: #{example_link}"
24 |
25 | uikit_link = ""
26 | uikit_equivalent = unless view.uikit_view.blank?
27 | uikit_handle = "uikit-#{view.uikit_view.downcase}"
28 | uikit_link = "https://developer.apple.com/documentation/uikit/#{view.uikit_view.downcase}"
29 | uikit_link = " ([#{view.uikit_view}](#{uikit_link}) in UIKit)"
30 | links << "[#{uikit_handle}]: #{uikit_link}"
31 | "[#{view.uikit_view}][#{uikit_handle}]"
32 | end
33 |
34 | text += "* [#{view.swiftui_view}](#{swiftui_link})#{uikit_link}: [example](#{example_link})\n"
35 |
36 | [
37 | "[#{view.swiftui_view}][#{swiftui_handle}]",
38 | uikit_equivalent || '',
39 | %Q{[
][#{example_handle}]}
40 | ]
41 |
42 | }
43 |
44 | puts MDTable.convert(data)
45 | puts "\n"
46 | puts links.join("\n")
47 | puts "\n"
48 | puts "\n"
49 | puts text
50 |
--------------------------------------------------------------------------------