diff --git a/Attendi.xcodeproj/project.pbxproj b/Attendi.xcodeproj/project.pbxproj index e2a2846..166788d 100644 --- a/Attendi.xcodeproj/project.pbxproj +++ b/Attendi.xcodeproj/project.pbxproj @@ -11,7 +11,7 @@ 02870A292FF7FF530079EA3A /* Features in Frameworks */ = {isa = PBXBuildFile; productRef = 02870A282FF7FF530079EA3A /* Features */; }; 0296F7F02FFAB1B600D2C5FC /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0296F7EF2FFAB1B600D2C5FC /* WidgetKit.framework */; }; 0296F7F22FFAB1B600D2C5FC /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0296F7F12FFAB1B600D2C5FC /* SwiftUI.framework */; }; - 0296F7FF2FFAB1B600D2C5FC /* AttendiWidgetExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 0296F7EE2FFAB1B600D2C5FC /* AttendiWidgetExtension.appex */; platformFilters = (ios, xros, ); settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 0296F7FF2FFAB1B600D2C5FC /* AttendiWidgetExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 0296F7EE2FFAB1B600D2C5FC /* AttendiWidgetExtension.appex */; platformFilter = ios; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 02D46AFE2FFAE77500266643 /* Features in Frameworks */ = {isa = PBXBuildFile; productRef = 02D46AFD2FFAE77500266643 /* Features */; }; /* End PBXBuildFile section */ @@ -342,10 +342,7 @@ }; 0296F7FE2FFAB1B600D2C5FC /* PBXTargetDependency */ = { isa = PBXTargetDependency; - platformFilters = ( - ios, - xros, - ); + platformFilter = ios; target = 0296F7ED2FFAB1B600D2C5FC /* AttendiWidgetExtension */; targetProxy = 0296F7FD2FFAB1B600D2C5FC /* PBXContainerItemProxy */; }; @@ -833,7 +830,7 @@ SDKROOT = auto; SKIP_INSTALL = YES; STRING_CATALOG_GENERATE_SYMBOLS = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator watchos watchsimulator xros xrsimulator"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; SWIFT_APPROACHABLE_CONCURRENCY = YES; @@ -841,7 +838,7 @@ SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; SWIFT_VERSION = 6.0; - TARGETED_DEVICE_FAMILY = "1,2,4,7"; + TARGETED_DEVICE_FAMILY = "1,2"; WATCHOS_DEPLOYMENT_TARGET = 26.0; XROS_DEPLOYMENT_TARGET = 26.0; }; @@ -925,14 +922,14 @@ SDKROOT = auto; SKIP_INSTALL = YES; STRING_CATALOG_GENERATE_SYMBOLS = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator watchos watchsimulator xros xrsimulator"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SWIFT_APPROACHABLE_CONCURRENCY = YES; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; SWIFT_VERSION = 6.0; - TARGETED_DEVICE_FAMILY = "1,2,4,7"; + TARGETED_DEVICE_FAMILY = "1,2"; WATCHOS_DEPLOYMENT_TARGET = 26.0; XROS_DEPLOYMENT_TARGET = 26.0; }; diff --git a/Packages/Features/Sources/Notifying/Styles/Labels/NotificationLabelStyle.swift b/Packages/Features/Sources/Notifying/Styles/Labels/NotificationLabelStyle.swift index b92e445..0bb1d1c 100644 --- a/Packages/Features/Sources/Notifying/Styles/Labels/NotificationLabelStyle.swift +++ b/Packages/Features/Sources/Notifying/Styles/Labels/NotificationLabelStyle.swift @@ -45,10 +45,14 @@ public struct NotificationLabelStyle: LabelStyle { } .padding(.vertical, Constant.Padding.vertical) .padding(.horizontal, Constant.Padding.horizontal) + #if os(visionOS) + .glassBackgroundEffect(in: .capsule) + #else .glassEffect( .regular, in: .capsule ) + #endif } } diff --git a/Packages/Features/Sources/Recording/Styles/Buttons/RecordingButtonStyle.swift b/Packages/Features/Sources/Recording/Styles/Buttons/RecordingButtonStyle.swift index b117c19..1161068 100644 --- a/Packages/Features/Sources/Recording/Styles/Buttons/RecordingButtonStyle.swift +++ b/Packages/Features/Sources/Recording/Styles/Buttons/RecordingButtonStyle.swift @@ -3,7 +3,8 @@ import SwiftUI /// The button style for the controls of a ``RecordingView``. /// /// The style renders the button's label in the primary foreground color over a padded, circular Liquid Glass background — tinted by -/// the color given at initialization — that reacts fluidly to presses. The whole button dims while disabled, and both the label and its +/// the color given at initialization — that reacts fluidly to presses. On visionOS, where Liquid Glass is unavailable, the background is +/// the platform's own glass material overlaid with the tint color. The whole button dims while disabled, and both the label and its /// padding scale relative to the current dynamic type size. struct RecordingButtonStyle: ButtonStyle { @@ -52,6 +53,14 @@ struct RecordingButtonStyle: ButtonStyle { .padding(padding) .scaledToFit() .foregroundStyle(.primary.opacity(opacity)) + #if os(visionOS) + .background( + tintColor.opacity(Constant.Opacity.tint), + in: .circle + ) + .glassBackgroundEffect(in: .circle) + .opacity(opacity) + #else .glassEffect( .regular .tint(tintColor) @@ -59,6 +68,7 @@ struct RecordingButtonStyle: ButtonStyle { in: .circle ) .opacity(opacity) + #endif } } @@ -108,6 +118,8 @@ private enum Constant { static let disabled: Double = 0.5 /// The opacity of an enabled button. static let enabled: Double = 1 + /// The opacity of the tint color laid over the glass background on visionOS. + static let tint: Double = 0.5 } /// The padding constants. diff --git a/Packages/Features/Sources/Recording/Views/RecordingView.swift b/Packages/Features/Sources/Recording/Views/RecordingView.swift index d6d58ee..af33ff1 100644 --- a/Packages/Features/Sources/Recording/Views/RecordingView.swift +++ b/Packages/Features/Sources/Recording/Views/RecordingView.swift @@ -81,54 +81,13 @@ public struct RecordingView: View { .accessibilityValue(model.textTimerAccessible) } + #if os(visionOS) + controls + #else GlassEffectContainer { - HStack( - spacing: Constant.Spacing.stack - ) { - if model.shouldShowActions { - Button { - model.pressedDiscard() - } label: { - Image.Icon.discard - .resizable() - } - .buttonStyle(.recording( - tintColor: .red - )) - .accessibilityLabel(.viewRecordingButtonDiscardLabel) - } - - Button { - model.pressedMain() - } label: { - Image(model.iconMain) - .resizable() - } - .buttonStyle(.recording( - tintColor: .yellow - )) - .disabled(model.shouldDisableMain) - .accessibilityLabel(model.labelMain) - - if model.shouldShowActions { - Button { - model.pressedSend() - } label: { - if let icon = model.iconSend { - Image(icon) - .resizable() - } else { - ProgressView() - } - } - .buttonStyle(.recording( - tintColor: .green - )) - .disabled(model.isProcessing) - .accessibilityLabel(model.labelSend) - } - } + controls } + #endif } .animation( .easeInOut, @@ -174,6 +133,64 @@ public struct RecordingView: View { } +// MARK: - Subviews + +private extension RecordingView { + + // MARK: Computed + + /// The main, discard, and send buttons controlling the recording flow, laid out in a horizontal stack. + var controls: some View { + HStack( + spacing: Constant.Spacing.stack + ) { + if model.shouldShowActions { + Button { + model.pressedDiscard() + } label: { + Image.Icon.discard + .resizable() + } + .buttonStyle(.recording( + tintColor: .red + )) + .accessibilityLabel(.viewRecordingButtonDiscardLabel) + } + + Button { + model.pressedMain() + } label: { + Image(model.iconMain) + .resizable() + } + .buttonStyle(.recording( + tintColor: .yellow + )) + .disabled(model.shouldDisableMain) + .accessibilityLabel(model.labelMain) + + if model.shouldShowActions { + Button { + model.pressedSend() + } label: { + if let icon = model.iconSend { + Image(icon) + .resizable() + } else { + ProgressView() + } + } + .buttonStyle(.recording( + tintColor: .green + )) + .disabled(model.isProcessing) + .accessibilityLabel(model.labelSend) + } + } + } + +} + // MARK: - Constants /// The constant values used across the view.