Compare commits
4 Commits
f73da42a3d
...
1999f7c137
| Author | SHA1 | Date | |
|---|---|---|---|
| 1999f7c137 | |||
| f574b91ebb | |||
| 0ea6993621 | |||
| 0e6d7b5355 |
@ -207,12 +207,13 @@
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
"fr-CA",
|
||||
);
|
||||
mainGroup = EA836AB62F0ACE8A00077F87;
|
||||
minimizedProjectReferenceProxies = 1;
|
||||
packageReferences = (
|
||||
EA836AEE2F0AD00000077F87 /* XCRemoteSwiftPackageReference "purchases-ios-spm" */,
|
||||
EA836AF72F0AD00000077F87 /* XCLocalSwiftPackageReference "Bedrock" */,
|
||||
EA836AF72F0AD00000077F87 /* XCLocalSwiftPackageReference "../Bedrock" */,
|
||||
EA836AF82F0AD00000077F87 /* XCRemoteSwiftPackageReference "MijickCamera" */,
|
||||
);
|
||||
preferredProjectObjectVersion = 77;
|
||||
@ -421,12 +422,13 @@
|
||||
DEVELOPMENT_TEAM = 6R7KLBPBLZ;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_KEY_NSCameraUsageDescription = "SelfieCam needs camera access to show your selfie preview and capture photos and videos.";
|
||||
INFOPLIST_KEY_NSMicrophoneUsageDescription = "SelfieCam needs microphone access to record audio with your videos.";
|
||||
INFOPLIST_KEY_NSPhotoLibraryAddUsageDescription = "SelfieCam needs photo library access to save your captured photos and videos.";
|
||||
INFOPLIST_KEY_NSCameraUsageDescription = "SelfieCam needs camera access to display your live selfie preview, apply real-time filters and ring light effects, capture high-quality photos and videos, and enable advanced features like Center Stage auto-framing.";
|
||||
INFOPLIST_KEY_NSMicrophoneUsageDescription = "SelfieCam needs microphone access to record clear audio when capturing videos, ensuring your voiceovers and ambient sounds are captured along with your video content.";
|
||||
INFOPLIST_KEY_NSPhotoLibraryAddUsageDescription = "SelfieCam needs photo library access to automatically save your captured photos and videos to your device, making them available in the Photos app and other compatible applications.";
|
||||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||
"INFOPLIST_KEY_UILaunchScreen_BackgroundColor" = LaunchBackground;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -457,12 +459,13 @@
|
||||
DEVELOPMENT_TEAM = 6R7KLBPBLZ;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_KEY_NSCameraUsageDescription = "SelfieCam needs camera access to show your selfie preview and capture photos and videos.";
|
||||
INFOPLIST_KEY_NSMicrophoneUsageDescription = "SelfieCam needs microphone access to record audio with your videos.";
|
||||
INFOPLIST_KEY_NSPhotoLibraryAddUsageDescription = "SelfieCam needs photo library access to save your captured photos and videos.";
|
||||
INFOPLIST_KEY_NSCameraUsageDescription = "SelfieCam needs camera access to display your live selfie preview, apply real-time filters and ring light effects, capture high-quality photos and videos, and enable advanced features like Center Stage auto-framing.";
|
||||
INFOPLIST_KEY_NSMicrophoneUsageDescription = "SelfieCam needs microphone access to record clear audio when capturing videos, ensuring your voiceovers and ambient sounds are captured along with your video content.";
|
||||
INFOPLIST_KEY_NSPhotoLibraryAddUsageDescription = "SelfieCam needs photo library access to automatically save your captured photos and videos to your device, making them available in the Photos app and other compatible applications.";
|
||||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||
"INFOPLIST_KEY_UILaunchScreen_BackgroundColor" = LaunchBackground;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -607,6 +610,13 @@
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
|
||||
/* Begin XCLocalSwiftPackageReference section */
|
||||
EA836AF72F0AD00000077F87 /* XCLocalSwiftPackageReference "../Bedrock" */ = {
|
||||
isa = XCLocalSwiftPackageReference;
|
||||
relativePath = ../Bedrock;
|
||||
};
|
||||
/* End XCLocalSwiftPackageReference section */
|
||||
|
||||
/* Begin XCRemoteSwiftPackageReference section */
|
||||
EA836AEE2F0AD00000077F87 /* XCRemoteSwiftPackageReference "purchases-ios-spm" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
@ -616,10 +626,6 @@
|
||||
minimumVersion = 5.52.1;
|
||||
};
|
||||
};
|
||||
EA836AF72F0AD00000077F87 /* XCLocalSwiftPackageReference "Bedrock" */ = {
|
||||
isa = XCLocalSwiftPackageReference;
|
||||
relativePath = ../Bedrock;
|
||||
};
|
||||
EA836AF82F0AD00000077F87 /* XCRemoteSwiftPackageReference "MijickCamera" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "http://192.168.1.128:3000/mbrucedogs/MijickCamera";
|
||||
@ -643,7 +649,7 @@
|
||||
};
|
||||
EA836AF32F0AD00000077F87 /* Bedrock */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = EA836AF72F0AD00000077F87 /* XCLocalSwiftPackageReference "Bedrock" */;
|
||||
package = EA836AF72F0AD00000077F87 /* XCLocalSwiftPackageReference "../Bedrock" */;
|
||||
productName = Bedrock;
|
||||
};
|
||||
EA836AF52F0AD00000077F87 /* MijickCamera */ = {
|
||||
|
||||
@ -16,7 +16,9 @@ struct SelfieCamApp: App {
|
||||
|
||||
var body: some Scene {
|
||||
WindowGroup {
|
||||
ContentView()
|
||||
AppLaunchView(config: .selfieCam) {
|
||||
ContentView()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -31,6 +31,10 @@ struct CustomCameraScreen: MCameraScreen {
|
||||
@State private var isShowingScreenFlash: Bool = false
|
||||
@State private var originalBrightness: CGFloat = UIScreen.main.brightness
|
||||
|
||||
// Timer countdown state
|
||||
@State private var countdownSeconds: Int = 0
|
||||
@State private var isCountdownActive: Bool = false
|
||||
|
||||
// Pinch to zoom gesture state
|
||||
@GestureState private var magnification: CGFloat = 1.0
|
||||
@State private var lastMagnification: CGFloat = 1.0
|
||||
@ -95,8 +99,21 @@ struct CustomCameraScreen: MCameraScreen {
|
||||
isCenterStageActive: cameraSettings.isCenterStageEnabled
|
||||
)
|
||||
|
||||
// Capture Button
|
||||
// Capture Button with timer indicator badge
|
||||
CaptureButton(action: { performCapture() })
|
||||
.overlay(alignment: .bottomTrailing) {
|
||||
if cameraSettings.selectedTimer.seconds > 0 {
|
||||
ZStack {
|
||||
Circle()
|
||||
.fill(Color.black.opacity(Design.Opacity.accent))
|
||||
.frame(width: 24, height: 24)
|
||||
Text(cameraSettings.selectedTimer.displayName)
|
||||
.font(.system(size: 10, weight: .bold))
|
||||
.foregroundColor(.white)
|
||||
}
|
||||
.offset(x: 4, y: 4)
|
||||
}
|
||||
}
|
||||
.padding(.bottom, Design.Spacing.large)
|
||||
}
|
||||
.padding(.horizontal, Design.Spacing.large)
|
||||
@ -106,6 +123,26 @@ struct CustomCameraScreen: MCameraScreen {
|
||||
|
||||
|
||||
|
||||
// Countdown overlay
|
||||
if isCountdownActive {
|
||||
ZStack {
|
||||
Color.black.opacity(0.3)
|
||||
.ignoresSafeArea()
|
||||
|
||||
VStack {
|
||||
Text("\(countdownSeconds)")
|
||||
.font(.system(size: 120, weight: .bold, design: .rounded))
|
||||
.foregroundColor(.white)
|
||||
.shadow(radius: 10)
|
||||
|
||||
Text("Get ready!")
|
||||
.font(.title2)
|
||||
.foregroundColor(.white.opacity(0.8))
|
||||
}
|
||||
}
|
||||
.transition(.opacity)
|
||||
}
|
||||
|
||||
// Screen flash overlay for front camera
|
||||
if isShowingScreenFlash {
|
||||
screenFlashColor
|
||||
@ -114,6 +151,7 @@ struct CustomCameraScreen: MCameraScreen {
|
||||
}
|
||||
}
|
||||
.animation(.easeInOut(duration: 0.05), value: isShowingScreenFlash)
|
||||
.animation(.easeInOut(duration: 0.3), value: isCountdownActive)
|
||||
.onAppear {
|
||||
// Set flash mode from saved settings
|
||||
setFlashMode(cameraSettings.flashMode.toMijickFlashMode)
|
||||
@ -162,9 +200,40 @@ struct CustomCameraScreen: MCameraScreen {
|
||||
cameraPosition == .front && cameraSettings.flashMode != .off && cameraSettings.isFlashSyncedWithRingLight
|
||||
}
|
||||
|
||||
/// Performs capture with screen flash if needed
|
||||
/// Performs capture with timer countdown and screen flash if needed
|
||||
private func performCapture() {
|
||||
print("performCapture called - shouldUseCustomScreenFlash: \(shouldUseCustomScreenFlash)")
|
||||
// Check if timer is enabled
|
||||
let timerSeconds = cameraSettings.selectedTimer.seconds
|
||||
|
||||
if timerSeconds > 0 && !isCountdownActive {
|
||||
// Start countdown
|
||||
startCountdown(seconds: timerSeconds)
|
||||
} else if !isCountdownActive {
|
||||
// No timer or countdown already running, proceed with capture
|
||||
performActualCapture()
|
||||
}
|
||||
}
|
||||
|
||||
/// Starts the countdown timer
|
||||
private func startCountdown(seconds: Int) {
|
||||
countdownSeconds = seconds
|
||||
isCountdownActive = true
|
||||
|
||||
Task { @MainActor in
|
||||
while countdownSeconds > 0 {
|
||||
try? await Task.sleep(for: .seconds(1))
|
||||
countdownSeconds -= 1
|
||||
}
|
||||
|
||||
// Countdown finished, perform capture
|
||||
isCountdownActive = false
|
||||
performActualCapture()
|
||||
}
|
||||
}
|
||||
|
||||
/// Performs the actual capture with screen flash if needed
|
||||
private func performActualCapture() {
|
||||
print("performActualCapture called - shouldUseCustomScreenFlash: \(shouldUseCustomScreenFlash)")
|
||||
if shouldUseCustomScreenFlash {
|
||||
// Save original brightness and boost to max
|
||||
originalBrightness = UIScreen.main.brightness
|
||||
|
||||
@ -141,6 +141,14 @@ struct SettingsView: View {
|
||||
|
||||
acknowledgmentsSection
|
||||
|
||||
// MARK: - Debug Section
|
||||
|
||||
#if DEBUG
|
||||
SettingsSectionHeader(title: "Debug", systemImage: "ant.fill")
|
||||
|
||||
brandingDebugSection
|
||||
#endif
|
||||
|
||||
Spacer(minLength: Design.Spacing.xxxLarge)
|
||||
}
|
||||
.padding(.horizontal, Design.Spacing.large)
|
||||
@ -752,6 +760,70 @@ struct SettingsView: View {
|
||||
.padding(.top, Design.Spacing.large)
|
||||
.padding(.bottom, Design.Spacing.xSmall)
|
||||
}
|
||||
|
||||
// MARK: - Branding Debug Section
|
||||
|
||||
#if DEBUG
|
||||
private var brandingDebugSection: some View {
|
||||
VStack(spacing: Design.Spacing.small) {
|
||||
// Icon Generator
|
||||
NavigationLink {
|
||||
IconGeneratorView(config: .selfieCam, appName: "SelfieCam")
|
||||
} label: {
|
||||
HStack {
|
||||
VStack(alignment: .leading, spacing: Design.Spacing.xxSmall) {
|
||||
Text("Icon Generator")
|
||||
.font(.system(size: Design.BaseFontSize.body, weight: .medium))
|
||||
.foregroundStyle(.white)
|
||||
|
||||
Text("Generate and save app icon to Files")
|
||||
.font(.system(size: Design.BaseFontSize.caption))
|
||||
.foregroundStyle(.white.opacity(Design.Opacity.medium))
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
||||
Image(systemName: "chevron.right")
|
||||
.font(.system(size: Design.BaseFontSize.caption))
|
||||
.foregroundStyle(.white.opacity(Design.Opacity.medium))
|
||||
}
|
||||
.padding(Design.Spacing.medium)
|
||||
.background(Color.Surface.primary, in: RoundedRectangle(cornerRadius: Design.CornerRadius.medium))
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
|
||||
// Branding Preview
|
||||
NavigationLink {
|
||||
BrandingPreviewView(
|
||||
iconConfig: .selfieCam,
|
||||
launchConfig: .selfieCam,
|
||||
appName: "SelfieCam"
|
||||
)
|
||||
} label: {
|
||||
HStack {
|
||||
VStack(alignment: .leading, spacing: Design.Spacing.xxSmall) {
|
||||
Text("Branding Preview")
|
||||
.font(.system(size: Design.BaseFontSize.body, weight: .medium))
|
||||
.foregroundStyle(.white)
|
||||
|
||||
Text("Preview app icon and launch screen")
|
||||
.font(.system(size: Design.BaseFontSize.caption))
|
||||
.foregroundStyle(.white.opacity(Design.Opacity.medium))
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
||||
Image(systemName: "chevron.right")
|
||||
.font(.system(size: Design.BaseFontSize.caption))
|
||||
.foregroundStyle(.white.opacity(Design.Opacity.medium))
|
||||
}
|
||||
.padding(Design.Spacing.medium)
|
||||
.background(Color.Surface.primary, in: RoundedRectangle(cornerRadius: Design.CornerRadius.medium))
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.4 MiB |
@ -1,6 +1,7 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "AppIcon.png",
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
|
||||
@ -0,0 +1,20 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.450",
|
||||
"green" : "0.250",
|
||||
"red" : "0.850"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
66
SelfieCam/Shared/BrandingConfig.swift
Normal file
66
SelfieCam/Shared/BrandingConfig.swift
Normal file
@ -0,0 +1,66 @@
|
||||
//
|
||||
// BrandingConfig.swift
|
||||
// SelfieCam
|
||||
//
|
||||
// App-specific branding configurations for icons and launch screens.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Bedrock
|
||||
|
||||
// MARK: - App Branding Colors
|
||||
|
||||
extension Color {
|
||||
/// SelfieCam branding colors for icon and launch screen.
|
||||
enum Branding {
|
||||
/// Primary gradient color - rich magenta/rose for a vibrant, modern selfie aesthetic.
|
||||
static let primary = Color(red: 0.85, green: 0.25, blue: 0.45)
|
||||
|
||||
/// Secondary gradient color - deeper magenta/purple for depth.
|
||||
static let secondary = Color(red: 0.45, green: 0.12, blue: 0.35)
|
||||
|
||||
/// Accent color - soft white/cream for clean, bright text and icons.
|
||||
static let accent = Color.white
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - App Icon Configuration
|
||||
|
||||
extension AppIconConfig {
|
||||
/// SelfieCam app icon configuration.
|
||||
/// A vibrant, modern icon perfect for a selfie camera app.
|
||||
static let selfieCam = AppIconConfig(
|
||||
title: "SELFIE",
|
||||
subtitle: "CAM",
|
||||
iconSymbol: "camera.fill",
|
||||
primaryColor: Color.Branding.primary,
|
||||
secondaryColor: Color.Branding.secondary,
|
||||
accentColor: Color.Branding.accent
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - Launch Screen Configuration
|
||||
|
||||
extension LaunchScreenConfig {
|
||||
/// SelfieCam launch screen configuration.
|
||||
/// Animated launch screen with camera-focused visuals and radial glow.
|
||||
static let selfieCam = LaunchScreenConfig(
|
||||
title: "SELFIE CAM",
|
||||
tagline: "Look Your Best",
|
||||
iconSymbols: ["camera.fill", "sparkles"],
|
||||
cornerSymbol: "sparkle",
|
||||
decorativeSymbol: "circle.fill",
|
||||
patternStyle: .radial,
|
||||
layoutStyle: .iconAboveTitle,
|
||||
primaryColor: Color.Branding.primary,
|
||||
secondaryColor: Color.Branding.secondary,
|
||||
accentColor: Color.Branding.accent,
|
||||
titleColor: .white,
|
||||
iconSize: 52,
|
||||
titleSize: 38,
|
||||
subtitleSize: 72,
|
||||
iconSpacing: 12,
|
||||
animationDuration: 0.6,
|
||||
showLoadingIndicator: false
|
||||
)
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user