diff --git a/Sources/OpenSwiftUI/View/LabelContent/LabeledContent.swift b/Sources/OpenSwiftUI/View/LabelContent/LabeledContent.swift index d96abae94..47a46a72c 100644 --- a/Sources/OpenSwiftUI/View/LabelContent/LabeledContent.swift +++ b/Sources/OpenSwiftUI/View/LabelContent/LabeledContent.swift @@ -3,7 +3,10 @@ // OpenSwiftUI // // Audited for 6.5.4 -// Status: Blocked by Text +// Status: Complete + +public import Foundation +public import OpenSwiftUICore /// A container for attaching a label to a value-bearing view. /// @@ -182,61 +185,187 @@ extension LabeledContent: View where Label: View, Content: View { } } -//@available(OpenSwiftUI_v4_0, *) -//extension LabeledContent where Label == Text, Content: View { -// public init( -// _ titleKey: LocalizedStringKey, -// @ViewBuilder content: () -> Content -// ) { -// _openSwiftUIUnimplementedFailure() -// } -// -// @_disfavoredOverload -// public init( -// _ title: S, -// @ViewBuilder content: () -> Content -// ) where S: StringProtocol { -// _openSwiftUIUnimplementedFailure() -// } -//} -// -//@available(OpenSwiftUI_v4_0, *) -//extension LabeledContent where Label == Text, Content == Text { -// public init( -// _ titleKey: LocalizedStringKey, -// value: S -// ) where S: StringProtocol { -// _openSwiftUIUnimplementedFailure() -// } -// -// @_disfavoredOverload -// public init( -// _ title: S1, -// value: S2 -// ) where S1: StringProtocol, S2: StringProtocol { -// _openSwiftUIUnimplementedFailure() -// } -// -// public init( -// _ titleKey: LocalizedStringKey, -// value: F.FormatInput, -// format: F -// ) where F: FormatStyle, F.FormatInput: Equatable, F.FormatOutput == String { -// _openSwiftUIUnimplementedFailure() -// } -// -// public init( -// _ title: S, -// value: F.FormatInput, -// format: F -// ) where S: StringProtocol, F: FormatStyle, F.FormatInput: Equatable, F.FormatOutput == String { -// _openSwiftUIUnimplementedFailure() -// } -//} -// -//@available(OpenSwiftUI_v4_0, *) -//extension LabeledContent where Label == LabeledContentStyleConfiguration.Label, Content == LabeledContentStyleConfiguration.Content { -// public init(_ configuration: LabeledContentStyleConfiguration) { -// _openSwiftUIUnimplementedFailure() -// } -//} +@available(OpenSwiftUI_v4_0, *) +extension LabeledContent where Label == Text, 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( + _ titleKey: LocalizedStringKey, + @ViewBuilder content: () -> Content + ) { + self.init(content: content) { + Text(titleKey) + } + accessibilityPresentation = .merged + } + + /// Creates a labeled view that generates its label from a string. + /// + /// This initializer creates a ``Text`` label on your behalf, and treats the + /// title similar to ``Text/init(_:)``. See `Text` for more + /// information about localizing strings. + /// + /// - Parameters: + /// - title: A string that describes the purpose of the view. + /// - content: The value content being labeled. + @_disfavoredOverload + public init( + _ title: S, + @ViewBuilder content: () -> Content + ) where S: StringProtocol { + self.init(content: content) { + Text(title) + } + accessibilityPresentation = .merged + } +} + +@available(OpenSwiftUI_v4_0, *) +extension LabeledContent where Label == Text, Content == Text { + /// Creates a labeled informational view. + /// + /// 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. + /// + /// Form { + /// LabeledContent("Name", value: person.name) + /// } + /// + /// In some contexts, this text will be selectable by default. + /// + /// - Parameters: + /// - titleKey: The key for the view's localized title, that describes + /// the purpose of the view. + /// - value: The value being labeled. + public init( + _ titleKey: LocalizedStringKey, + value: S + ) where S: StringProtocol { + self.init(titleKey) { + Text(value) + } + } + + /// Creates a labeled informational view. + /// + /// This initializer creates a ``Text`` label on your behalf, and treats the + /// title similar to ``Text/init(_:)``. See `Text` for more + /// information about localizing strings. + /// + /// Form { + /// ForEach(person.pet) { pet in + /// LabeledContent(pet.species, value: pet.name) + /// } + /// } + /// + /// - Parameters: + /// - title: A string that describes the purpose of the view. + /// - value: The value being labeled. + @_disfavoredOverload + public init( + _ title: S1, + value: S2 + ) where S1: StringProtocol, S2: StringProtocol { + self.init(title) { + Text(value) + } + } + + /// Creates a labeled informational view from a formatted value. + /// + /// 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. + /// + /// Form { + /// LabeledContent("Age", value: person.age, format: .number) + /// LabeledContent("Height", value: person.height, + /// format: .measurement(width: .abbreviated)) + /// } + /// + /// - Parameters: + /// - titleKey: The key for the view's localized title, that describes + /// the purpose of the view. + /// - value: The value being labeled. + /// - format: A format style of type `F` to convert the underlying value + /// of type `F.FormatInput` to a string representation. + public init( + _ titleKey: LocalizedStringKey, + value: F.FormatInput, + format: F + ) where F: FormatStyle, F.FormatInput: Equatable, F.FormatOutput == String { + self.init(titleKey) { + Text(value, format: format) + } + } + + /// Creates a labeled informational view from a formatted value. + /// + /// This initializer creates a ``Text`` label on your behalf, and treats the + /// title similar to ``Text/init(_:)``. See `Text` for more + /// information about localizing strings. + /// + /// Form { + /// Section("Downloads") { + /// ForEach(download) { file in + /// LabeledContent(file.name, value: file.downloadSize, + /// format: .byteCount(style: .file)) + /// } + /// } + /// } + /// + /// - Parameters: + /// - title: A string that describes the purpose of the view. + /// - value: The value being labeled. + /// - format: A format style of type `F` to convert the underlying value + /// of type `F.FormatInput` to a string representation. + public init( + _ title: S, + value: F.FormatInput, + format: F + ) where S: StringProtocol, F: FormatStyle, F.FormatInput: Equatable, F.FormatOutput == String { + self.init(title) { + Text(value, format: format) + } + } +} + +@available(OpenSwiftUI_v4_0, *) +extension LabeledContent where Label == LabeledContentStyleConfiguration.Label, Content == LabeledContentStyleConfiguration.Content { + /// 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(_ configuration: LabeledContentStyleConfiguration) { + self.init { + configuration.content + } label: { + configuration.label + } + } +} diff --git a/Sources/OpenSwiftUI/View/LabelContent/LabeledView.swift b/Sources/OpenSwiftUI/View/LabelContent/LabeledView.swift index d59c54c9f..9dc4e1e1d 100644 --- a/Sources/OpenSwiftUI/View/LabelContent/LabeledView.swift +++ b/Sources/OpenSwiftUI/View/LabelContent/LabeledView.swift @@ -3,7 +3,10 @@ // OpenSwiftUI // // Audited for 6.5.4 -// Status: Blocked by Text +// Status: Complete + +public import Foundation +public import OpenSwiftUICore @_spi(Private) @available(OpenSwiftUI_v4_0, *) @@ -13,7 +16,7 @@ public struct LabeledView: View where Label: View, Content: View var content: Content - public init( + nonisolated public init( @ViewBuilder content: () -> Content, @ViewBuilder label: () -> Label ) { @@ -34,58 +37,70 @@ public struct LabeledView: View where Label: View, Content: View @available(*, unavailable) extension LabeledView: Sendable {} -//@_spi(Private) -//@available(OpenSwiftUI_v4_0, *) -//@available(*, deprecated, renamed: "LabeledContent") -//extension LabeledView where Label == Text { -// public init( -// _ titleKey: LocalizedStringKey, -// @ViewBuilder content: () -> Content -// ) { -// _openSwiftUIUnimplementedFailure() -// } -// -// @_disfavoredOverload -// public init( -// _ title: S, -// @ViewBuilder content: () -> Content -// ) where S: StringProtocol { -// _openSwiftUIUnimplementedFailure() -// } -//} -// -//@_spi(Private) -//@available(OpenSwiftUI_v4_0, *) -//@available(*, deprecated, renamed: "LabeledContent") -//extension LabeledView where Label == Text, Content == Text { -// public init( -// _ titleKey: LocalizedStringKey, -// value: S -// ) where S: StringProtocol { -// _openSwiftUIUnimplementedFailure() -// } -// -// @_disfavoredOverload -// public init( -// _ title: S1, -// value: S2 -// ) where S1: StringProtocol, S2: StringProtocol { -// _openSwiftUIUnimplementedFailure() -// } -// -// public init( -// _ titleKey: LocalizedStringKey, -// value: F.FormatInput, -// format: F -// ) where F: FormatStyle, F.FormatInput: Equatable, F.FormatOutput == String { -// _openSwiftUIUnimplementedFailure() -// } -// -// public init( -// _ title: S, -// value: F.FormatInput, -// format: F -// ) where S: StringProtocol, F: FormatStyle, F.FormatInput: Equatable, F.FormatOutput == String { -// _openSwiftUIUnimplementedFailure() -// } -//} +@_spi(Private) +@available(OpenSwiftUI_v4_0, *) +@available(*, deprecated, renamed: "LabeledContent") +extension LabeledView where Label == Text { + nonisolated public init( + _ titleKey: LocalizedStringKey, + @ViewBuilder content: () -> Content + ) { + self.init(content: content) { + Text(titleKey) + } + } + + @_disfavoredOverload + nonisolated public init( + _ title: S, + @ViewBuilder content: () -> Content + ) where S: StringProtocol { + self.init(content: content) { + Text(title) + } + } +} + +@_spi(Private) +@available(OpenSwiftUI_v4_0, *) +@available(*, deprecated, renamed: "LabeledContent") +extension LabeledView where Label == Text, Content == Text { + nonisolated public init( + _ titleKey: LocalizedStringKey, + value: S + ) where S: StringProtocol { + self.init(titleKey) { + Text(value) + } + } + + @_disfavoredOverload + nonisolated public init( + _ title: S1, + value: S2 + ) where S1: StringProtocol, S2: StringProtocol { + self.init(title) { + Text(value) + } + } + + nonisolated public init( + _ titleKey: LocalizedStringKey, + value: F.FormatInput, + format: F + ) where F: FormatStyle, F.FormatInput: Equatable, F.FormatOutput == String { + self.init(titleKey) { + Text(value, format: format) + } + } + + nonisolated public init( + _ title: S, + value: F.FormatInput, + format: F + ) where S: StringProtocol, F: FormatStyle, F.FormatInput: Equatable, F.FormatOutput == String { + self.init(title) { + Text(value, format: format) + } + } +}