diff --git a/Yattee/Views/Settings/MacOSSettings.swift b/Yattee/Views/Settings/MacOSSettings.swift index d3203093..6b2455d5 100644 --- a/Yattee/Views/Settings/MacOSSettings.swift +++ b/Yattee/Views/Settings/MacOSSettings.swift @@ -134,6 +134,21 @@ struct SettingsFormSection: View { #endif } +/// A label style that forces the icon to a fixed width so adjacent +/// labels align regardless of icon glyph width. Use when a section has +/// a vertical stack of `Label`s with mixed-width SF Symbols. +struct FixedIconWidthLabelStyle: LabelStyle { + var iconWidth: CGFloat = 22 + + func makeBody(configuration: Configuration) -> some View { + HStack(spacing: 8) { + configuration.icon + .frame(width: iconWidth, alignment: .center) + configuration.title + } + } +} + /// A settings row that pushes a destination view onto the navigation stack. /// /// On macOS it renders as a plain full-width list row with a trailing diff --git a/Yattee/Views/Settings/iCloudSettingsView.swift b/Yattee/Views/Settings/iCloudSettingsView.swift index 980f37bb..0b6e0bfd 100644 --- a/Yattee/Views/Settings/iCloudSettingsView.swift +++ b/Yattee/Views/Settings/iCloudSettingsView.swift @@ -49,18 +49,16 @@ struct iCloudSettingsView: View { } var body: some View { - List { + SettingsFormContainer { #if DEBUG - Section { + SettingsFormSection(footer: "settings.icloud.dev.footer") { Label(String(localized: "settings.icloud.dev.title"), systemImage: "hammer.fill") .foregroundStyle(.orange) .font(.subheadline) - } footer: { - Text(String(localized: "settings.icloud.dev.footer")) } #endif - Section { + SettingsFormSection(footer: "settings.icloud.footer") { Toggle(isOn: Binding( get: { settingsManager?.iCloudSyncEnabled ?? false }, set: { newValue in @@ -73,8 +71,6 @@ struct iCloudSettingsView: View { )) { Label(String(localized: "settings.icloud.enable"), systemImage: "icloud") } - } footer: { - Text(String(localized: "settings.icloud.footer")) } if settingsManager?.iCloudSyncEnabled == true { @@ -167,7 +163,7 @@ struct iCloudSettingsView: View { @ViewBuilder private var syncCategoriesSection: some View { - Section { + SettingsFormSection(footer: "settings.icloud.categories.footer") { Toggle(isOn: Binding( get: { settingsManager?.syncInstances ?? true }, set: { newValue in @@ -271,16 +267,18 @@ struct iCloudSettingsView: View { )) { Label(String(localized: "settings.icloud.category.mediaSources"), systemImage: "externaldrive.connected.to.line.below") } - } footer: { - Text(String(localized: "settings.icloud.categories.footer")) } + #if os(macOS) + .labelStyle(FixedIconWidthLabelStyle()) + #endif } // MARK: - Sync Status Section @ViewBuilder private var syncStatusSection: some View { - Section { + let footer: LocalizedStringKey? = lastManualSyncRelative.map { LocalizedStringKey("settings.icloud.lastManualSync \($0)") } + SettingsFormSection(footer: footer) { // iCloud Account Status HStack { Label(String(localized: "settings.icloud.account"), systemImage: "person.crop.circle") @@ -357,11 +355,6 @@ struct iCloudSettingsView: View { Label(String(localized: "settings.icloud.syncNow"), systemImage: syncNowIcon) } .disabled(cloudKitSync?.isSyncing == true) - - } footer: { - if let lastSync = lastManualSyncRelative { - Text(String(localized: "settings.icloud.lastManualSync \(lastSync)")) - } } .animation(.easeInOut(duration: 0.3), value: cloudKitSync?.syncStatus) .animation(.easeInOut(duration: 0.3), value: cloudKitSync?.uploadProgress)