From 67162381a840560cdc2023fa43970929c6db19fa Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Thu, 12 Mar 2026 14:18:19 +0000 Subject: [PATCH] Fix the AuthenticationStartScreen with large Dynamic Type sizes. (#5191) * Allow AuthenticationStartLogo to be scaled and fix dark mode when not on a gradient. * Fix the layout of AuthenticationStartScreen with large Dynamic Type sizes. --- .../View/AuthenticationStartLogo.swift | 154 ++++++++++++++---- .../View/AuthenticationStartScreen.swift | 65 ++++---- .../Other/PlaceholderScreenCoordinator.swift | 2 +- ...ticationStartScreen.Default-iPad-en-GB.png | 4 +- ...icationStartScreen.Default-iPad-pseudo.png | 4 +- ...cationStartScreen.Default-iPhone-en-GB.png | 4 +- ...ationStartScreen.Default-iPhone-pseudo.png | 4 +- ...tionStartScreen.Provisioned-iPad-en-GB.png | 4 +- ...ionStartScreen.Provisioned-iPad-pseudo.png | 4 +- ...onStartScreen.Provisioned-iPhone-en-GB.png | 4 +- ...nStartScreen.Provisioned-iPhone-pseudo.png | 4 +- 11 files changed, 175 insertions(+), 78 deletions(-) diff --git a/ElementX/Sources/Screens/Authentication/StartScreen/View/AuthenticationStartLogo.swift b/ElementX/Sources/Screens/Authentication/StartScreen/View/AuthenticationStartLogo.swift index 8d5eea263..a05100cee 100644 --- a/ElementX/Sources/Screens/Authentication/StartScreen/View/AuthenticationStartLogo.swift +++ b/ElementX/Sources/Screens/Authentication/StartScreen/View/AuthenticationStartLogo.swift @@ -10,80 +10,132 @@ import SwiftUI /// The app's logo styled to fit on various launch pages. struct AuthenticationStartLogo: View { + /// Set to specify a custom size for the Logo, otherwise the default size of 158pt will be used. + var size: CGFloat? + /// Set to `true` to skip the brand chrome. + let hideBrandChrome: Bool + /// Set to `true` when using on top of `Asset.Images.launchBackground`. + let isOnGradient: Bool + + private let appLogoImage = Image(asset: Asset.Images.appLogo) + + struct SizeMetrics { + let scale: CGFloat + let imageSize: CGFloat + } + + private var sizeMetrics: SizeMetrics? { + size.map { customSize in + let scale = customSize / 158 + return SizeMetrics(scale: scale, + imageSize: hideBrandChrome ? customSize : 110 * scale) + } + } + + var body: some View { + if let sizeMetrics { + appLogoImage + .resizable() + .frame(width: sizeMetrics.imageSize, height: sizeMetrics.imageSize) + .modifier(AuthenticationBrandLogoModifier(scale: sizeMetrics.scale, + hideBrandChrome: hideBrandChrome, + isOnGradient: isOnGradient)) + } else { + appLogoImage + .modifier(AuthenticationBrandLogoModifier(scale: 1, + hideBrandChrome: hideBrandChrome, + isOnGradient: isOnGradient)) + } + } +} + +/// Applies the brand chrome styling (rounded card with shadows and border) to any image, +/// as seen on the authentication start screen. +private struct AuthenticationBrandLogoModifier: ViewModifier { @Environment(\.colorScheme) private var colorScheme - /// Set to `true` when using on top of `Asset.Images.launchBackground` + /// Scale factor relative to the original 158pt design. + let scale: CGFloat + /// Set to `true` to skip the brand chrome. let hideBrandChrome: Bool + /// Set to `true` when using on top of `Asset.Images.launchBackground`. + let isOnGradient: Bool - /// Extra padding needed to avoid cropping the shadows. - private let extra: CGFloat = 64 - /// The shape that the logo is composed on top of. - private let outerShape = RoundedRectangle(cornerRadius: 44) private let outerShapeShadowColor = Color(red: 0.11, green: 0.11, blue: 0.13) private var isLight: Bool { colorScheme == .light } + + /// Extra padding needed to avoid cropping the shadows. + private var extra: CGFloat { + 64 * scale + } + + /// The shape that the logo is composed on top of. + private var outerShape: RoundedRectangle { + RoundedRectangle(cornerRadius: 44 * scale) + } - var body: some View { + func body(content: Content) -> some View { if hideBrandChrome { - Image(asset: Asset.Images.appLogo) + content } else { - brandLogo + styledContent(content) } } - private var brandLogo: some View { - Image(asset: Asset.Images.appLogo) + private func styledContent(_ content: Content) -> some View { + content .background { Circle() - .inset(by: 1) - .shadow(color: .black.opacity(!isLight ? 0.3 : 0.4), - radius: 12.57143, - y: 6.28571) + .inset(by: 1 * scale) + .shadow(color: .black.opacity(!isLight && isOnGradient ? 0.3 : 0.4), + radius: 12.57143 * scale, + y: 6.28571 * scale) Circle() - .inset(by: 1) + .inset(by: 1 * scale) .shadow(color: .black.opacity(0.5), - radius: 12.57143, - y: 6.28571) + radius: 12.57143 * scale, + y: 6.28571 * scale) .blendMode(.overlay) } - .padding(24) + .padding(24 * scale) .background { Color.white - .opacity(isLight ? 0.23 : 0.05) + .opacity(isLight ? 0.23 : isOnGradient ? 0.05 : 0.13) } .clipShape(outerShape) .overlay { outerShape - .inset(by: 0.25) - .stroke(.white.opacity(isLight ? 1 : 0.9), lineWidth: 0.5) + .inset(by: 0.25 * scale) + .stroke(.white.opacity(isLight ? 1 : isOnGradient ? 0.9 : 0.25), lineWidth: 0.5 * scale) .blendMode(isLight ? .normal : .overlay) } .padding(extra) .background { ZStack { - if !isLight { + if !isLight, isOnGradient { outerShape - .inset(by: 1) + .inset(by: 1 * scale) .padding(extra) .shadow(color: .black.opacity(0.5), - radius: 32.91666, - y: 1.05333) + radius: 32.91666 * scale, + y: 1.05333 * scale) } else { outerShape - .inset(by: 1) + .inset(by: 1 * scale) .padding(extra) .shadow(color: outerShapeShadowColor.opacity(isLight ? 0.23 : 0.08), - radius: 16, - y: 8) + radius: 16 * scale, + y: 8 * scale) outerShape - .inset(by: 1) + .inset(by: 1 * scale) .padding(extra) .shadow(color: outerShapeShadowColor.opacity(0.5), - radius: 16, - y: 8) + radius: 16 * scale, + y: 8 * scale) .blendMode(.overlay) } } @@ -98,3 +150,43 @@ struct AuthenticationStartLogo: View { .accessibilityHidden(true) } } + +#Preview { + VStack(spacing: 0) { + HStack(spacing: 0) { + AuthenticationStartLogo(hideBrandChrome: false, isOnGradient: false) + .padding() + AuthenticationStartLogo(hideBrandChrome: false, isOnGradient: true) + .padding() + .background { + AuthenticationStartScreenBackgroundImage().offset(y: 70) + } + .clipped() + } + .background(.compound.bgCanvasDefault) + + HStack(spacing: 0) { + AuthenticationStartLogo(hideBrandChrome: false, isOnGradient: false) + .padding() + AuthenticationStartLogo(hideBrandChrome: false, isOnGradient: true) + .padding() + .background { + AuthenticationStartScreenBackgroundImage().offset(y: 70) + } + .clipped() + } + .background(.compound.bgCanvasDefault) + .colorScheme(.dark) + + HStack(spacing: 0) { + AuthenticationStartLogo(size: 54, hideBrandChrome: false, isOnGradient: false) + .padding() + .background(.compound.bgCanvasDefault) + AuthenticationStartLogo(size: 54, hideBrandChrome: false, isOnGradient: false) + .padding() + .background(.compound.bgCanvasDefault) + .colorScheme(.dark) + } + .padding(.top) + } +} diff --git a/ElementX/Sources/Screens/Authentication/StartScreen/View/AuthenticationStartScreen.swift b/ElementX/Sources/Screens/Authentication/StartScreen/View/AuthenticationStartScreen.swift index 23079148d..e9fb1fa28 100644 --- a/ElementX/Sources/Screens/Authentication/StartScreen/View/AuthenticationStartScreen.swift +++ b/ElementX/Sources/Screens/Authentication/StartScreen/View/AuthenticationStartScreen.swift @@ -16,36 +16,30 @@ struct AuthenticationStartScreen: View { @Bindable var context: AuthenticationStartScreenViewModel.Context var body: some View { + // This view uses a GeometryReader instead of FullscreenDialog so its content takes the full + // height available (after taking the buttons out of the equation) in order for the logo + // and title to appear vertically centred and equally spaced within this content area. GeometryReader { geometry in - VStack(alignment: .leading, spacing: 0) { - Spacer() - .frame(height: UIConstants.spacerHeight(in: geometry)) - - content - .frame(width: geometry.size.width) - .accessibilityIdentifier(A11yIdentifiers.authenticationStartScreen.hidden) - - buttons - .frame(width: geometry.size.width) - .padding(.bottom, UIConstants.actionButtonBottomPadding) - .padding(.bottom, geometry.safeAreaInsets.bottom > 0 ? 0 : 16) - .padding(.top, 8) - - Spacer() - .frame(height: UIConstants.spacerHeight(in: geometry)) - } - .frame(maxHeight: .infinity) - .safeAreaInset(edge: .bottom) { - versionText - .font(.compound.bodySM) - .foregroundColor(.compound.textSecondary) - .frame(maxWidth: .infinity) - .padding(.bottom) - .onTapGesture(count: 7) { - context.send(viewAction: .reportProblem) - } - .accessibilityIdentifier(A11yIdentifiers.authenticationStartScreen.appVersion) + ScrollView { + VStack(alignment: .leading, spacing: 0) { + Spacer() + .frame(height: UIConstants.spacerHeight(in: geometry)) + + content + .frame(width: geometry.size.width) + .accessibilityIdentifier(A11yIdentifiers.authenticationStartScreen.hidden) + + buttons + .frame(width: geometry.size.width) + .padding(.bottom, geometry.safeAreaInsets.bottom > 0 ? 0 : 16) + .padding(.top, 8) + + Spacer() + .frame(height: UIConstants.spacerHeight(in: geometry)) + } + .frame(minHeight: geometry.size.height) } + .scrollBounceBehavior(.basedOnSize) } .navigationBarHidden(true) .background { @@ -64,7 +58,8 @@ struct AuthenticationStartScreen: View { if verticalSizeClass == .regular { Spacer() - AuthenticationStartLogo(hideBrandChrome: context.viewState.hideBrandChrome) + AuthenticationStartLogo(hideBrandChrome: context.viewState.hideBrandChrome, + isOnGradient: !context.viewState.hideBrandChrome) } Spacer() @@ -77,7 +72,7 @@ struct AuthenticationStartScreen: View { .multilineTextAlignment(.center) Text(L10n.screenOnboardingWelcomeMessage(InfoPlistReader.main.productionAppName)) .font(.compound.bodyLG) - .foregroundColor(.compound.textSecondary) + .foregroundColor(.compound.textPrimary) .multilineTextAlignment(.center) } .padding() @@ -114,6 +109,16 @@ struct AuthenticationStartScreen: View { } .buttonStyle(.compound(.tertiary)) } + + versionText + .font(.compound.bodySM) + .foregroundColor(.compound.textSecondary) + .frame(maxWidth: .infinity) + .padding(.top, 16) + .onTapGesture(count: 7) { + context.send(viewAction: .reportProblem) + } + .accessibilityIdentifier(A11yIdentifiers.authenticationStartScreen.appVersion) } .padding(.horizontal, verticalSizeClass == .compact ? 128 : 24) .readableFrame() diff --git a/ElementX/Sources/Screens/Other/PlaceholderScreenCoordinator.swift b/ElementX/Sources/Screens/Other/PlaceholderScreenCoordinator.swift index 21d823c1f..f9992031e 100644 --- a/ElementX/Sources/Screens/Other/PlaceholderScreenCoordinator.swift +++ b/ElementX/Sources/Screens/Other/PlaceholderScreenCoordinator.swift @@ -28,7 +28,7 @@ struct PlaceholderScreen: View { let hideGradientBackground: Bool var body: some View { - AuthenticationStartLogo(hideBrandChrome: hideBrandChrome) + AuthenticationStartLogo(hideBrandChrome: hideBrandChrome, isOnGradient: !hideGradientBackground) .frame(maxWidth: .infinity, maxHeight: .infinity) .background { if !hideGradientBackground { diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationStartScreen.Default-iPad-en-GB.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationStartScreen.Default-iPad-en-GB.png index 542bba71d..7ecb22531 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationStartScreen.Default-iPad-en-GB.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationStartScreen.Default-iPad-en-GB.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6158f690ffe1defeef9b583dd8267204ee733a45f2f5cf44d6696cc5af64789b -size 111970 +oid sha256:540afca94168f6daa9ed51c761bd1ab271014898da893a39904d08e88872a263 +size 112308 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationStartScreen.Default-iPad-pseudo.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationStartScreen.Default-iPad-pseudo.png index fa7a6a1fb..9f328d1cf 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationStartScreen.Default-iPad-pseudo.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationStartScreen.Default-iPad-pseudo.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:514e4b542257ee77beb1c82896476935e1d8a1727d8a77384545cd94f8f87ea5 -size 132189 +oid sha256:24320001ef26a722134e4ba829094cb8ee848079880f9f72c5284b1fb550bfe7 +size 132912 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationStartScreen.Default-iPhone-en-GB.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationStartScreen.Default-iPhone-en-GB.png index 78e9d3185..463aed69b 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationStartScreen.Default-iPhone-en-GB.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationStartScreen.Default-iPhone-en-GB.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:43a70f508336cb1d5fe0f3679b700b3d6cd8e64182b549e40f42c8f8aa0374e0 -size 510930 +oid sha256:178b907d46a505230ef1d0530ff763789547c843ea828f7e3b4f18aae2be435f +size 517884 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationStartScreen.Default-iPhone-pseudo.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationStartScreen.Default-iPhone-pseudo.png index 1e256779e..144ce14ce 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationStartScreen.Default-iPhone-pseudo.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationStartScreen.Default-iPhone-pseudo.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7c7c51cd982fa0262784bf27f4a9bd295219a7733bc2ed4359d403bd81d2564f -size 533667 +oid sha256:47a8832cc78842e8c471e603b88ea56b261fd9af821825246d55af8ab121e117 +size 541873 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationStartScreen.Provisioned-iPad-en-GB.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationStartScreen.Provisioned-iPad-en-GB.png index 3efac09a7..1835510b3 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationStartScreen.Provisioned-iPad-en-GB.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationStartScreen.Provisioned-iPad-en-GB.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a1a60cca5b53f6998cb6c2bf2cf1a8dd2fdd1343ea76fd72df32bba1d07fd011 -size 99862 +oid sha256:b64fcfd41c482d6c7341d0b9a515c671175c10261f0fa5f412f943b1b895a655 +size 99485 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationStartScreen.Provisioned-iPad-pseudo.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationStartScreen.Provisioned-iPad-pseudo.png index cf9fd4ccc..4f265a1c9 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationStartScreen.Provisioned-iPad-pseudo.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationStartScreen.Provisioned-iPad-pseudo.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9ab408cd8e4aa4f955a03f68d845a5ac4d57e90ebbbab6b3fa0502aa6d0407c9 -size 118992 +oid sha256:aea8fbf225d77a752f06612e76139a15e690d0be3dc51a56bb2189284a68d862 +size 119996 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationStartScreen.Provisioned-iPhone-en-GB.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationStartScreen.Provisioned-iPhone-en-GB.png index bd10a24ab..1141bb2b4 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationStartScreen.Provisioned-iPhone-en-GB.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationStartScreen.Provisioned-iPhone-en-GB.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:97d86af1a0f6eeb5d6933b13a6a5de3df8db1d2672d2e7cdf1f2fe83a0adbf64 -size 531619 +oid sha256:b7eb4153207abeabf4f3217aae294603c8764bf28df93b901f0ab4c42a803527 +size 532886 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationStartScreen.Provisioned-iPhone-pseudo.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationStartScreen.Provisioned-iPhone-pseudo.png index a9f6f62a8..636577de4 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationStartScreen.Provisioned-iPhone-pseudo.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/authenticationStartScreen.Provisioned-iPhone-pseudo.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bda2bb728b9f9179ea211c5de083c8f4b67a6ffc4cc2f484c81c9ec4bb40d013 -size 558660 +oid sha256:aadf9de9f2a5a4e6c2f0eb4a7becbed65e0ae24cdb9dd39ef1f25353d3309b3f +size 561031