Skip to content

Commit

Permalink
Add views to presenting row-based layouts (#27)
Browse files Browse the repository at this point in the history
# Add views to presenting row-based layouts

## ♻️ Current situation & Problem
We identified a use case providing a default component to present
`ListRow`s that automatically adapt the the current dynamic type size,
device orientation and size classes.
This PR adds a new `DynamicHStack` that automatically adapts row-based,
horizontal content to the current dynamic type size.


## ⚙️ Release Notes 
* Added `DynamicHStack` for presenting row-based content that adapts to
the current dynamic type size.
* Added the `ListRow` view to present key-value-based list rows, based
on the `DynamicHStack`.


## 📚 Documentation
Documentation and code examples were provided. I restructured the
landing page a bit to accommodate for those new elements.


## ✅ Testing
This PR adds basic support for snapshot testing using baseline images.
These allow to unit test view layouts across builds. We rely on the
[SnapshotTesting](https://github.com/pointfreeco/swift-snapshot-testing)
framework for that.


## 📝 Code of Conduct & Contributing Guidelines 

By submitting creating this pull request, you agree to follow our [Code
of
Conduct](https://github.com/StanfordSpezi/.github/blob/main/CODE_OF_CONDUCT.md)
and [Contributing
Guidelines](https://github.com/StanfordSpezi/.github/blob/main/CONTRIBUTING.md):
- [x] I agree to follow the [Code of
Conduct](https://github.com/StanfordSpezi/.github/blob/main/CODE_OF_CONDUCT.md)
and [Contributing
Guidelines](https://github.com/StanfordSpezi/.github/blob/main/CONTRIBUTING.md).
  • Loading branch information
Supereg authored Jan 31, 2024
1 parent 0137e69 commit 2772604
Show file tree
Hide file tree
Showing 14 changed files with 419 additions and 88 deletions.
5 changes: 5 additions & 0 deletions .reuse/dep5
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/

Files: Tests/SpeziViewsTests/__Snapshots__/*
Copyright: 2024 Stanford University and the project authors (see CONTRIBUTORS.md)
License: MIT
6 changes: 4 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ let package = Package(
.library(name: "SpeziValidation", targets: ["SpeziValidation"])
],
dependencies: [
.package(url: "https://github.com/apple/swift-collections.git", .upToNextMajor(from: "1.0.4"))
.package(url: "https://github.com/apple/swift-collections.git", from: "1.0.4"),
.package(url: "https://github.com/pointfreeco/swift-snapshot-testing.git", from: "1.15.1")
],
targets: [
.target(
Expand All @@ -46,7 +47,8 @@ let package = Package(
name: "SpeziViewsTests",
dependencies: [
.target(name: "SpeziViews"),
.target(name: "SpeziValidation")
.target(name: "SpeziValidation"),
.product(name: "SnapshotTesting", package: "swift-snapshot-testing")
]
)
]
Expand Down
72 changes: 0 additions & 72 deletions Sources/SpeziViews/Resources/Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -17,42 +17,6 @@
}
}
},
"Enter your first name ..." : {
"comment" : "Given name placeholder",
"extractionState" : "stale",
"localizations" : {
"de" : {
"stringUnit" : {
"state" : "translated",
"value" : "Vornamen eingeben ..."
}
},
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Enter your first name ..."
}
}
}
},
"Enter your last name ..." : {
"comment" : "Family name placeholder",
"extractionState" : "stale",
"localizations" : {
"de" : {
"stringUnit" : {
"state" : "translated",
"value" : "Familienname eingeben ..."
}
},
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Enter your last name ..."
}
}
}
},
"Error" : {
"comment" : "View State default error title",
"localizations" : {
Expand All @@ -70,42 +34,6 @@
}
}
},
"First Name" : {
"comment" : "Given name title",
"extractionState" : "stale",
"localizations" : {
"de" : {
"stringUnit" : {
"state" : "translated",
"value" : "Vorname"
}
},
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "First Name"
}
}
}
},
"Last Name" : {
"comment" : "Family name title",
"extractionState" : "stale",
"localizations" : {
"de" : {
"stringUnit" : {
"state" : "translated",
"value" : "Familienname"
}
},
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Last Name"
}
}
}
},
"MARKDOWN_LOADING_ERROR" : {
"localizations" : {
"de" : {
Expand Down
35 changes: 23 additions & 12 deletions Sources/SpeziViews/SpeziViews.docc/SpeziViews.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,42 +38,53 @@ SpeziViews provides easy-to-use and easily-reusable UI components that makes the

## Topics

### Managing State
### Manage and communicate View State

- ``ViewState``
- ``OperationState``
- ``SwiftUI/View/viewStateAlert(state:)-4wzs4``
- ``SwiftUI/View/viewStateAlert(state:)-27a86``
- ``OperationState``
- ``SwiftUI/View/map(state:to:)``
- ``SwiftUI/View/processingOverlay(isProcessing:overlay:)-5xplv``
- ``SwiftUI/View/processingOverlay(isProcessing:overlay:)-3df8d``
- ``SwiftUI/EnvironmentValues/defaultErrorDescription``
- ``AnyLocalizedError``

### Manage Layout
Automatically adapt your view layouts to dynamic type sizes, device orientation, and device size classes.

- ``DynamicHStack``
- ``ListRow``
- ``DescriptionGridRow``

### User Input

- ``AsyncButton``
- ``SwiftUI/EnvironmentValues/processingDebounceDuration``
- ``CanvasView``

### Displaying Content
### Displaying Text

- ``Label``
- ``LazyText``
- ``MarkdownView``
- ``DescriptionGridRow``

### Readers
### Interact with the View Environment

- ``HorizontalGeometryReader``
- ``WidthPreferenceKey``
- ``SwiftUI/View/focusOnTap()``
- ``SwiftUI/View/observeOrientationChanges(_:)``

### Localization

- ``Foundation/LocalizedStringResource/BundleDescription/atURL(from:)``
- ``Foundation/LocalizedStringResource/localizedString(for:)``
- ``Swift/StringProtocol/localized(_:)``
- ``Foundation/LocalizedStringResource/BundleDescription/atURL(from:)``

### Managing Focus

- ``SwiftUI/View/focusOnTap()``
### Readers

- ``HorizontalGeometryReader``
- ``WidthPreferenceKey``

### Error Handling

- ``AnyLocalizedError``
- ``SwiftUI/EnvironmentValues/defaultErrorDescription``
57 changes: 57 additions & 0 deletions Sources/SpeziViews/ViewModifier/DeviceOrientationModifier.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
//
// This source file is part of the Stanford Spezi open-source project
//
// SPDX-FileCopyrightText: 2024 Stanford University and the project authors (see CONTRIBUTORS.md)
//
// SPDX-License-Identifier: MIT
//

import SwiftUI


struct DeviceOrientationModifier: ViewModifier {
@Binding private var orientation: UIDeviceOrientation


init(orientation: Binding<UIDeviceOrientation>) {
self._orientation = orientation
}


func body(content: Content) -> some View {
content
.onAppear {
orientation = UIDevice.current.orientation
}
.onReceive(NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)) { _ in
orientation = UIDevice.current.orientation
}
}
}


extension View {
/// Observe changes to the device orientation.
///
/// Use this modifier to observe changes to the current device orientation.
///
/// ```swift
/// struct MyView: View {
/// @State private var orientation = UIDevice.current.orientation
///
/// var body: some View {
/// List {
/// // ...
/// }
/// .observeOrientationChanges($orientation)
/// }
/// }
/// ```
///
///
/// - Parameter orientation: The Binding to your `UIDeviceOrientation` state.
/// - Returns: The modified view that observes device orientation.
public func observeOrientationChanges(_ orientation: Binding<UIDeviceOrientation>) -> some View {
modifier(DeviceOrientationModifier(orientation: orientation))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@
import SwiftUI


/// A ``DescriptionGridRow`` provides a layout to align a description next to content element in a `Grid`-based layout.
/// Attach a leading description label to your content.
///
/// The `DescriptionGridRow` provides a layout to attach a leading description label to a content element
/// in a [`Grid`](https://developer.apple.com/documentation/swiftui/grid)-based layout.
public struct DescriptionGridRow<Description: View, Content: View>: View {
private let description: Description
private let content: Content
Expand All @@ -27,7 +30,7 @@ public struct DescriptionGridRow<Description: View, Content: View>: View {
}


/// Creates a new ``DescriptionGridRow`` instance providing a layout to align a desription next to content element in a `Grid`-based layout.
/// Creates a new ``DescriptionGridRow`` instance providing a layout to align a description next to content element in a `Grid`-based layout.
/// - Parameters:
/// - description: The description `View` of the `DescriptionGridRow``
/// - content: The content `View` of the `DescriptionGridRow``
Expand Down
Loading

0 comments on commit 2772604

Please sign in to comment.