import SwiftUI @available(macOS 10.15, *) extension Settings { /** Represents a section with right-aligned title and optional bottom divider. */ @available(macOS 10.15, *) public struct Section: View { /** Preference key holding max width of section labels. */ private struct LabelWidthPreferenceKey: PreferenceKey { typealias Value = Double static var defaultValue = 0.0 static func reduce(value: inout Double, nextValue: () -> Double) { let next = nextValue() value = next > value ? next : value } } /** Convenience overlay for finding a label's dimensions using `GeometryReader`. */ private struct LabelOverlay: View { var body: some View { GeometryReader { geometry in Color.clear .preference( key: LabelWidthPreferenceKey.self, value: geometry.size.width ) } } } /** Convenience modifier for applying `LabelWidthPreferenceKey`. */ struct LabelWidthModifier: ViewModifier { @Binding var maximumWidth: Double func body(content: Content) -> some View { content .onPreferenceChange(LabelWidthPreferenceKey.self) { newMaximumWidth in maximumWidth = newMaximumWidth } } } public let label: AnyView public let content: AnyView public let bottomDivider: Bool public let verticalAlignment: VerticalAlignment /** A section is responsible for controlling a single setting. - Parameters: - bottomDivider: Whether to place a `Divider` after the section content. Default is `false`. - verticalAlignement: The vertical alignment of the section content. - label: A view describing the setting handled by this section. - content: A content view. */ public init( bottomDivider: Bool = false, verticalAlignment: VerticalAlignment = .firstTextBaseline, label: @escaping () -> some View, @ViewBuilder content: @escaping () -> some View ) { self.label = label() .overlay(LabelOverlay()) .eraseToAnyView() // TODO: Remove use of `AnyView`. self.bottomDivider = bottomDivider self.verticalAlignment = verticalAlignment let stack = VStack(alignment: .leading) { content() } self.content = stack.eraseToAnyView() } /** Creates instance of section, responsible for controling a single setting with `Text` as a `Label`. - Parameters: - title: A string describing the setting handled by this section. - bottomDivider: Whether to place a `Divider` after the section content. Default is `false`. - verticalAlignement: The vertical alignment of the section content. - content: A content view. */ public init( title: String, bottomDivider: Bool = false, verticalAlignment: VerticalAlignment = .firstTextBaseline, @ViewBuilder content: @escaping () -> some View ) { let textLabel = { Text(title) .font(.system(size: 13.0)) .overlay(LabelOverlay()) .eraseToAnyView() } self.init( bottomDivider: bottomDivider, verticalAlignment: verticalAlignment, label: textLabel, content: content ) } public var body: some View { HStack(alignment: verticalAlignment) { label .alignmentGuide(.settingsSectionLabel) { $0[.trailing] } content Spacer() } } } }