Skip to content

Commit

Permalink
LabeledContentStyle's now respect labelsHidden correctly
Browse files Browse the repository at this point in the history
  • Loading branch information
shaps80 committed Oct 4, 2022
1 parent b7800ca commit d9842f2
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 96 deletions.
123 changes: 64 additions & 59 deletions Sources/SwiftUIBackports/Shared/LabeledContent/LabeledContent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -123,40 +123,62 @@ extension Backport where Wrapped == Any {
///
/// You can set label styles using the ``View/labeledContentStyle(_:)``
/// modifier. You can also build custom styles using ``LabeledContentStyle``.
public struct LabeledContent<Label, Content> {
public struct LabeledContent<Label, Content>: View {
@EnvironmentContains(key: "LabelsHiddenKey") private var isHidden
@Environment(\.backportLabeledContentStyle) private var style

let config: LabeledContentStyleConfiguration

public var body: some View {
style.makeBody(configuration: config)
style.makeBody(configuration: config.labelHidden(isHidden))
}

/// Creates labeled content based on a labeled content style configuration.
///
/// You can use this initializer within the
/// ``LabeledContentStyle/makeBody(configuration:)`` method of a
/// ``LabeledContentStyle`` to create a labeled content instance.
/// This is useful for custom styles that only modify the current style,
/// as opposed to implementing a brand new style.
///
/// For example, the following style adds a red border around the labeled
/// content, but otherwise preserves the current style:
///
/// struct RedBorderLabeledContentStyle: LabeledContentStyle {
/// func makeBody(configuration: Configuration) -> some View {
/// LabeledContent(configuration)
/// .border(.red)
/// }
/// }
///
/// - Parameter configuration: The properties of the labeled content
public init(_ config: Backport.LabeledContentStyleConfiguration) {
self.config = config
}
}

}

extension Backport.LabeledContent where Wrapped == Any, Label == Backport<Any>.LabeledContentStyleConfiguration.Label, Content == Backport<Any>.LabeledContentStyleConfiguration.Content {
extension Backport.LabeledContent where Wrapped == Any, Label: View, Content: View {

/// Creates labeled content based on a labeled content style configuration.
///
/// You can use this initializer within the
/// ``LabeledContentStyle/makeBody(configuration:)`` method of a
/// ``LabeledContentStyle`` to create a labeled content instance.
/// This is useful for custom styles that only modify the current style,
/// as opposed to implementing a brand new style.
///
/// For example, the following style adds a red border around the labeled
/// content, but otherwise preserves the current style:
/// Creates a labeled view that generates its label from a localized string
/// key.
///
/// struct RedBorderLabeledContentStyle: LabeledContentStyle {
/// func makeBody(configuration: Configuration) -> some View {
/// LabeledContent(configuration)
/// .border(.red)
/// }
/// }
/// This initializer creates a ``Text`` label on your behalf, and treats the
/// localized key similar to ``Text/init(_:tableName:bundle:comment:)``. See
/// `Text` for more information about localizing strings.
///
/// - Parameter configuration: The properties of the labeled content
public init(_ config: Backport.LabeledContentStyleConfiguration) {
self.config = config
/// - Parameters:
/// - titleKey: The key for the view's localized title, that describes
/// the purpose of the view.
/// - content: The value content being labeled.
public init(@ViewBuilder content: () -> Content, @ViewBuilder label: () -> Label) {
config = .init(
label: label(),
content: content()
)
}

}

extension Backport.LabeledContent where Wrapped == Any, Label == Text, Content : View {
Expand All @@ -173,10 +195,11 @@ extension Backport.LabeledContent where Wrapped == Any, Label == Text, Content :
/// the purpose of the view.
/// - content: The value content being labeled.
public init(_ titleKey: LocalizedStringKey, @ViewBuilder content: () -> Content) {
config = .init(
label: Text(titleKey),
content: content()
)
self.init {
content()
} label: {
Text(titleKey)
}
}

/// Creates a labeled view that generates its label from a string.
Expand All @@ -189,32 +212,11 @@ extension Backport.LabeledContent where Wrapped == Any, Label == Text, Content :
/// - title: A string that describes the purpose of the view.
/// - content: The value content being labeled.
public init<S>(_ title: S, @ViewBuilder content: () -> Content) where S: StringProtocol {
config = .init(
label: Text(title),
content: content()
)
}

}

extension Backport.LabeledContent: View where Wrapped == Any, Label: View, Content: View {

/// Creates a labeled view that generates its label from a localized string
/// key.
///
/// This initializer creates a ``Text`` label on your behalf, and treats the
/// localized key similar to ``Text/init(_:tableName:bundle:comment:)``. See
/// `Text` for more information about localizing strings.
///
/// - Parameters:
/// - titleKey: The key for the view's localized title, that describes
/// the purpose of the view.
/// - content: The value content being labeled.
public init(@ViewBuilder content: () -> Content, @ViewBuilder label: () -> Label) {
config = .init(
label: label(),
content: content()
)
self.init {
content()
} label: {
Text(title)
}
}

}
Expand All @@ -238,9 +240,11 @@ extension Backport.LabeledContent where Wrapped == Any, Label == Text, Content =
/// the purpose of the view.
/// - value: The value being labeled.
public init<S: StringProtocol>(_ titleKey: LocalizedStringKey, value: S) {
config = .init(
label: Text(titleKey),
content: Text(value))
self.init {
Text(value)
} label: {
Text(titleKey)
}
}

/// Creates a labeled informational view.
Expand All @@ -259,10 +263,11 @@ extension Backport.LabeledContent where Wrapped == Any, Label == Text, Content =
/// - title: A string that describes the purpose of the view.
/// - value: The value being labeled.
public init<S1: StringProtocol, S2: StringProtocol>(_ title: S1, value: S2) {
config = .init(
label: Text(title),
content: Text(value)
)
self.init {
Text(value)
} label: {
Text(title)
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,53 +8,39 @@ extension Backport where Wrapped == Any {

/// The properties of a labeled content instance.
public struct LabeledContentStyleConfiguration {
private struct Label<Content: View>: View {
let isHidden: Bool
let content: Content

/// A type-erased label of a labeled content instance.
public struct Label: View {
@EnvironmentContains(key: "LabelsHiddenKey") private var isHidden
let view: AnyView
public var body: some View {
if isHidden {
EmptyView()
} else {
view
if !isHidden {
content
}
}
init<V: View>(_ view: V) {
self.view = .init(view)
}
}

/// A type-erased content of a labeled content instance.
public struct Content: View {
@EnvironmentContains(key: "LabelsHiddenKey") private var isHidden
let view: AnyView
public var body: some View {
view
.foregroundColor(isHidden ? .primary : .secondary)
.frame(maxWidth: .infinity, alignment: isHidden ? .leading : .trailing)
}
init<V: View>(_ view: V) {
self.view = .init(view)
}
}
var labelHidden: Bool = false

private let _label: AnyView

/// The label of the labeled content instance.
public let label: Label
public var label: some View {
Label(isHidden: labelHidden, content: _label)
}

/// The content of the labeled content instance.
public let content: Content
public let content: AnyView

internal init<L: View, C: View>(label: L, content: C) {
self.label = .init(label)
_label = .init(label)
self.content = .init(content)
}

internal init<L: View, C: View>(@ViewBuilder content: () -> C, @ViewBuilder label: () -> L) {
self.content = .init(content())
self.label = .init(label())
func labelHidden(_ hidden: Bool) -> Self {
var copy = self
copy.labelHidden = hidden
return copy
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,26 @@ import SwiftUI
extension Backport where Wrapped == Any {

public struct AutomaticLabeledContentStyle: BackportLabeledContentStyle {
public func makeBody(configuration: Configuration) -> some View {
HStack(alignment: .firstTextBaseline) {
configuration.label
Spacer()
configuration.content
.multilineTextAlignment(.trailing)
private struct Content: View {
let configuration: Configuration

var body: some View {
if configuration.labelHidden {
configuration.content
} else {
HStack(alignment: .firstTextBaseline) {
configuration.label
Spacer()
configuration.content
.foregroundColor(.secondary)
}
}
}
}

public func makeBody(configuration: Configuration) -> some View {
Content(configuration: configuration)
}
}

}
Expand Down

0 comments on commit d9842f2

Please sign in to comment.