|
| 1 | +// |
| 2 | +// LabeledContent.swift |
| 3 | +// OpenSwiftUI |
| 4 | +// |
| 5 | +// Audited for 6.5.4 |
| 6 | +// Status: Blocked by Text |
| 7 | + |
| 8 | +/// A container for attaching a label to a value-bearing view. |
| 9 | +/// |
| 10 | +/// The instance's content represents a read-only or read-write value, and its |
| 11 | +/// label identifies or describes the purpose of that value. |
| 12 | +/// The resulting element has a layout that's consistent with other framework |
| 13 | +/// controls and automatically adapts to its container, like a form or toolbar. |
| 14 | +/// Some styles of labeled content also apply styling or behaviors to the value |
| 15 | +/// content, like making ``Text`` views selectable. |
| 16 | +/// |
| 17 | +/// The following example associates a label with a custom view and has |
| 18 | +/// a layout that matches the label of the ``Picker``: |
| 19 | +/// |
| 20 | +/// Form { |
| 21 | +/// LabeledContent("Custom Value") { |
| 22 | +/// MyCustomView(value: $value) |
| 23 | +/// } |
| 24 | +/// Picker("Selected Value", selection: $selection) { |
| 25 | +/// Text("Option 1").tag(1) |
| 26 | +/// Text("Option 2").tag(2) |
| 27 | +/// } |
| 28 | +/// } |
| 29 | +/// |
| 30 | +/// ### Custom view labels |
| 31 | +/// |
| 32 | +/// You can assemble labeled content with an explicit view for its label |
| 33 | +/// using the ``init(content:label:)`` initializer. For example, you can |
| 34 | +/// rewrite the previous labeled content example using a ``Text`` view: |
| 35 | +/// |
| 36 | +/// LabeledContent { |
| 37 | +/// MyCustomView(value: $value) |
| 38 | +/// } label: { |
| 39 | +/// Text("Custom Value") |
| 40 | +/// } |
| 41 | +/// |
| 42 | +/// The `label` view builder accepts any kind of view, like a ``Label``: |
| 43 | +/// |
| 44 | +/// LabeledContent { |
| 45 | +/// MyCustomView(value: $value) |
| 46 | +/// } label: { |
| 47 | +/// Label("Custom Value", systemImage: "hammer") |
| 48 | +/// } |
| 49 | +/// |
| 50 | +/// For cases where adding a subtitle to the label is desired, use a view |
| 51 | +/// builder that creates multiple `Text` views where the first text represents |
| 52 | +/// the title and the second text represents the subtitle: |
| 53 | +/// |
| 54 | +/// LabeledContent { |
| 55 | +/// MyCustomView(value: $value) |
| 56 | +/// } label: { |
| 57 | +/// Text("Custom Value") |
| 58 | +/// Text("Custom Subtitle Value") |
| 59 | +/// } |
| 60 | +/// |
| 61 | +/// ### Textual labeled content |
| 62 | +/// |
| 63 | +/// You can construct labeled content with string values or formatted values |
| 64 | +/// to create read-only displays of textual values: |
| 65 | +/// |
| 66 | +/// Form { |
| 67 | +/// Section("Information") { |
| 68 | +/// LabeledContent("Name", value: person.name) |
| 69 | +/// LabeledContent("Age", value: person.age, format: .number) |
| 70 | +/// LabeledContent("Height", value: person.height, |
| 71 | +/// format: .measurement(width: .abbreviated)) |
| 72 | +/// } |
| 73 | +/// if !person.pets.isEmpty { |
| 74 | +/// Section("Pets") { |
| 75 | +/// ForEach(pet) { pet in |
| 76 | +/// LabeledContent(pet.species, value: pet.name) |
| 77 | +/// } |
| 78 | +/// } |
| 79 | +/// } |
| 80 | +/// } |
| 81 | +/// |
| 82 | +/// Wherever possible, OpenSwiftUI makes this text selectable. |
| 83 | +/// |
| 84 | +/// ### Compositional elements |
| 85 | +/// |
| 86 | +/// You can use labeled content as the label for other elements. For example, |
| 87 | +/// a ``NavigationLink`` can present a summary value for the destination it |
| 88 | +/// links to: |
| 89 | +/// |
| 90 | +/// Form { |
| 91 | +/// NavigationLink(value: Settings.wifiDetail) { |
| 92 | +/// LabeledContent("Wi-Fi", value: ssidName) |
| 93 | +/// } |
| 94 | +/// } |
| 95 | +/// |
| 96 | +/// In some cases, the styling of views used as the value content is |
| 97 | +/// specialized as well. For example, while a ``Toggle`` in an inset group |
| 98 | +/// form on macOS is styled as a switch by default, it's styled as a checkbox |
| 99 | +/// when used as a value element within a surrounding `LabeledContent` |
| 100 | +/// instance: |
| 101 | +/// |
| 102 | +/// Form { |
| 103 | +/// LabeledContent("Source Control") { |
| 104 | +/// Toggle("Refresh local status automatically", |
| 105 | +/// isOn: $refreshLocalStatus) |
| 106 | +/// Toggle("Fetch and refresh server status automatically", |
| 107 | +/// isOn: $refreshServerStatus) |
| 108 | +/// Toggle("Add and remove files automatically", |
| 109 | +/// isOn: $addAndRemoveFiles) |
| 110 | +/// Toggle("Select files to commit automatically", |
| 111 | +/// isOn: $selectFiles) |
| 112 | +/// } |
| 113 | +/// } |
| 114 | +/// |
| 115 | +/// ### Controlling label visibility |
| 116 | +/// |
| 117 | +/// A label communicates the identity or purpose of the value, which is |
| 118 | +/// important for accessibility. However, you might want to hide the label |
| 119 | +/// in the display, and some controls or contexts may visually hide their label |
| 120 | +/// by default. The ``View/labelsHidden()`` modifier allows controlling that |
| 121 | +/// visibility. The following example hides both labels, producing only a |
| 122 | +/// group of the two value views: |
| 123 | +/// |
| 124 | +/// Group { |
| 125 | +/// LabeledContent("Custom Value") { |
| 126 | +/// MyCustomView(value: $value) |
| 127 | +/// } |
| 128 | +/// Picker("Selected Value", selection: $selection) { |
| 129 | +/// Text("Option 1").tag(1) |
| 130 | +/// Text("Option 2").tag(2) |
| 131 | +/// } |
| 132 | +/// } |
| 133 | +/// .labelsHidden() |
| 134 | +/// |
| 135 | +/// ### Styling labeled content |
| 136 | +/// |
| 137 | +/// You can set label styles using the ``View/labeledContentStyle(_:)`` |
| 138 | +/// modifier. You can also build custom styles using ``LabeledContentStyle``. |
| 139 | +@available(OpenSwiftUI_v4_0, *) |
| 140 | +public struct LabeledContent<Label, Content> { |
| 141 | + var label: Label |
| 142 | + |
| 143 | + var content: Content |
| 144 | + |
| 145 | + var accessibilityPresentation: AccessibilityLabeledContentPresentation? |
| 146 | +} |
| 147 | + |
| 148 | +@available(*, unavailable) |
| 149 | +extension LabeledContent: Sendable {} |
| 150 | + |
| 151 | +@available(OpenSwiftUI_v4_0, *) |
| 152 | +extension LabeledContent: View where Label: View, Content: View { |
| 153 | + |
| 154 | + /// Creates a standard labeled element, with a view that conveys |
| 155 | + /// the value of the element and a label. |
| 156 | + /// |
| 157 | + /// - Parameters: |
| 158 | + /// - content: The view that conveys the value of the resulting labeled |
| 159 | + /// element. |
| 160 | + /// - label: The label that describes the purpose of the result. |
| 161 | + nonisolated public init( |
| 162 | + @ViewBuilder content: () -> Content, |
| 163 | + @ViewBuilder label: () -> Label |
| 164 | + ) { |
| 165 | + self.content = content() |
| 166 | + self.label = label() |
| 167 | + self.accessibilityPresentation = nil |
| 168 | + } |
| 169 | + |
| 170 | + public var body: some View { |
| 171 | + ResolvedLabeledContent( |
| 172 | + configuration: LabeledContentStyleConfiguration( |
| 173 | + label: .init(), |
| 174 | + content: .init(), |
| 175 | + accessibilityPresentation: accessibilityPresentation |
| 176 | + ) |
| 177 | + ).viewAlias(LabeledContentStyleConfiguration.Label.self) { |
| 178 | + label |
| 179 | + }.viewAlias(LabeledContentStyleConfiguration.Content.self) { |
| 180 | + content |
| 181 | + } |
| 182 | + } |
| 183 | +} |
| 184 | + |
| 185 | +//@available(OpenSwiftUI_v4_0, *) |
| 186 | +//extension LabeledContent where Label == Text, Content: View { |
| 187 | +// public init( |
| 188 | +// _ titleKey: LocalizedStringKey, |
| 189 | +// @ViewBuilder content: () -> Content |
| 190 | +// ) { |
| 191 | +// _openSwiftUIUnimplementedFailure() |
| 192 | +// } |
| 193 | +// |
| 194 | +// @_disfavoredOverload |
| 195 | +// public init<S>( |
| 196 | +// _ title: S, |
| 197 | +// @ViewBuilder content: () -> Content |
| 198 | +// ) where S: StringProtocol { |
| 199 | +// _openSwiftUIUnimplementedFailure() |
| 200 | +// } |
| 201 | +//} |
| 202 | +// |
| 203 | +//@available(OpenSwiftUI_v4_0, *) |
| 204 | +//extension LabeledContent where Label == Text, Content == Text { |
| 205 | +// public init<S>( |
| 206 | +// _ titleKey: LocalizedStringKey, |
| 207 | +// value: S |
| 208 | +// ) where S: StringProtocol { |
| 209 | +// _openSwiftUIUnimplementedFailure() |
| 210 | +// } |
| 211 | +// |
| 212 | +// @_disfavoredOverload |
| 213 | +// public init<S1, S2>( |
| 214 | +// _ title: S1, |
| 215 | +// value: S2 |
| 216 | +// ) where S1: StringProtocol, S2: StringProtocol { |
| 217 | +// _openSwiftUIUnimplementedFailure() |
| 218 | +// } |
| 219 | +// |
| 220 | +// public init<F>( |
| 221 | +// _ titleKey: LocalizedStringKey, |
| 222 | +// value: F.FormatInput, |
| 223 | +// format: F |
| 224 | +// ) where F: FormatStyle, F.FormatInput: Equatable, F.FormatOutput == String { |
| 225 | +// _openSwiftUIUnimplementedFailure() |
| 226 | +// } |
| 227 | +// |
| 228 | +// public init<S, F>( |
| 229 | +// _ title: S, |
| 230 | +// value: F.FormatInput, |
| 231 | +// format: F |
| 232 | +// ) where S: StringProtocol, F: FormatStyle, F.FormatInput: Equatable, F.FormatOutput == String { |
| 233 | +// _openSwiftUIUnimplementedFailure() |
| 234 | +// } |
| 235 | +//} |
| 236 | +// |
| 237 | +//@available(OpenSwiftUI_v4_0, *) |
| 238 | +//extension LabeledContent where Label == LabeledContentStyleConfiguration.Label, Content == LabeledContentStyleConfiguration.Content { |
| 239 | +// public init(_ configuration: LabeledContentStyleConfiguration) { |
| 240 | +// _openSwiftUIUnimplementedFailure() |
| 241 | +// } |
| 242 | +//} |
0 commit comments