Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>

This commit is contained in:
Matt Bruce 2026-01-09 15:50:39 -06:00
parent 4fe7fde9b9
commit 8596a699c1
4 changed files with 84 additions and 23 deletions

View File

@ -8,10 +8,53 @@
import SwiftUI
import Bedrock
// MARK: - Device Size Tier
/// Categorizes devices by screen size for adaptive layouts.
/// Primarily used to adjust card dimensions on smaller iPhones and constrain width on iPad.
enum DeviceSizeTier {
case compact // iPhone SE, Mini, older 4.7" devices (height < 700pt)
case standard // Regular iPhone (height 700-850pt)
case large // Pro Max, Plus models (height > 850pt)
case tablet // iPad
/// The current device's size tier based on screen dimensions.
static var current: DeviceSizeTier {
let bounds = UIScreen.main.bounds
let height = max(bounds.width, bounds.height)
let width = min(bounds.width, bounds.height)
// iPad detection: width >= 768pt in portrait
if width >= 768 {
return .tablet
}
switch height {
case ..<700:
return .compact
case ..<850:
return .standard
default:
return .large
}
}
/// Whether the current device is an iPad.
static var isTablet: Bool {
current == .tablet
}
/// Whether the current device is a compact iPhone (SE, Mini).
static var isCompact: Bool {
current == .compact
}
}
// MARK: - App-Specific Sizes
extension Design {
/// BusinessCard-specific size constants.
/// Some values adapt based on device size tier.
enum CardSize {
static let cardWidth: CGFloat = 320
static let cardHeight: CGFloat = 340
@ -20,7 +63,17 @@ extension Design {
static let avatarLarge: CGFloat = 80
static let avatarOverlap: CGFloat = 40
static let logoSize: CGFloat = 64
static let bannerHeight: CGFloat = 240
/// Banner height adapts to device size to prevent dominating small screens.
static var bannerHeight: CGFloat {
switch DeviceSizeTier.current {
case .compact: return 180
case .standard: return 210
case .large: return 240
case .tablet: return 240
}
}
static let qrSize: CGFloat = 200
static let qrSizeLarge: CGFloat = 260
static let colorSwatchSize: CGFloat = 40
@ -29,10 +82,14 @@ extension Design {
static let widgetPhoneHeight: CGFloat = 120
static let widgetWatchSize: CGFloat = 100
static let floatingButtonSize: CGFloat = 56
/// Bottom offset for floating button above tab bar.
static let floatingButtonBottomOffset: CGFloat = 72
/// Aspect ratio for logo container (3:2 landscape).
static let logoContainerAspectRatio: CGFloat = 3.0 / 2.0
/// Maximum card width to prevent stretching on iPad.
/// On iPhone this returns nil (no constraint needed).
static var maxCardWidth: CGFloat? {
DeviceSizeTier.isTablet ? 500 : nil
}
}
}

View File

@ -1082,6 +1082,8 @@ private struct CardPreviewSheet: View {
NavigationStack {
ScrollView {
BusinessCardView(card: card)
.frame(maxWidth: Design.CardSize.maxCardWidth)
.frame(maxWidth: .infinity)
.padding(Design.Spacing.large)
}
.background(Color.AppBackground.base)

View File

@ -98,7 +98,9 @@ private struct CardPageView: View {
ScrollView {
VStack(spacing: Design.Spacing.large) {
BusinessCardView(card: card)
.frame(maxWidth: Design.CardSize.maxCardWidth)
}
.frame(maxWidth: .infinity)
.padding(.horizontal, Design.Spacing.large)
.padding(.vertical, Design.Spacing.xLarge)
}

View File

@ -9,10 +9,19 @@ struct RootTabView: View {
var body: some View {
@Bindable var appState = appState
ZStack(alignment: .bottom) {
TabView(selection: $appState.selectedTab) {
Tab(String.localized("My Cards"), systemImage: "rectangle.stack", value: AppTab.cards) {
CardsHomeView()
.safeAreaInset(edge: .bottom, spacing: 0) {
// Floating share button positioned above tab bar
if !appState.cardStore.cards.isEmpty {
FloatingShareButton {
showingShareSheet = true
}
.frame(maxWidth: .infinity)
.padding(.bottom, Design.Spacing.small)
}
}
}
Tab(String.localized("Contacts"), systemImage: "person.2", value: AppTab.contacts) {
@ -23,14 +32,6 @@ struct RootTabView: View {
WidgetsView()
}
}
// Floating share button - only shown on cards tab when cards exist
if appState.selectedTab == .cards && !appState.cardStore.cards.isEmpty {
FloatingShareButton {
showingShareSheet = true
}
}
}
.sheet(isPresented: $showingShareSheet) {
ShareCardView()
}
@ -62,7 +63,6 @@ private struct FloatingShareButton: View {
}
.accessibilityLabel(String.localized("Share"))
.accessibilityHint(String.localized("Opens the share sheet to send your card"))
.padding(.bottom, Design.CardSize.floatingButtonBottomOffset)
}
}