New intermediate iOS build that trims some fat
This commit is contained in:
@@ -466,7 +466,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = Ascently/Ascently.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Ascently/Ascently.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 40;
|
CURRENT_PROJECT_VERSION = 43;
|
||||||
DEVELOPMENT_TEAM = 4BC9Y2LL4B;
|
DEVELOPMENT_TEAM = 4BC9Y2LL4B;
|
||||||
DRIVERKIT_DEPLOYMENT_TARGET = 24.6;
|
DRIVERKIT_DEPLOYMENT_TARGET = 24.6;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
@@ -491,7 +491,7 @@
|
|||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 15.6;
|
MACOSX_DEPLOYMENT_TARGET = 15.6;
|
||||||
MARKETING_VERSION = 2.6.0;
|
MARKETING_VERSION = 2.6.1;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.atridad.Ascently;
|
PRODUCT_BUNDLE_IDENTIFIER = com.atridad.Ascently;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
@@ -518,7 +518,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = Ascently/Ascently.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Ascently/Ascently.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 40;
|
CURRENT_PROJECT_VERSION = 43;
|
||||||
DEVELOPMENT_TEAM = 4BC9Y2LL4B;
|
DEVELOPMENT_TEAM = 4BC9Y2LL4B;
|
||||||
DRIVERKIT_DEPLOYMENT_TARGET = 24.6;
|
DRIVERKIT_DEPLOYMENT_TARGET = 24.6;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
@@ -543,7 +543,7 @@
|
|||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 15.6;
|
MACOSX_DEPLOYMENT_TARGET = 15.6;
|
||||||
MARKETING_VERSION = 2.6.0;
|
MARKETING_VERSION = 2.6.1;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.atridad.Ascently;
|
PRODUCT_BUNDLE_IDENTIFIER = com.atridad.Ascently;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
@@ -570,7 +570,7 @@
|
|||||||
DEVELOPMENT_TEAM = 4BC9Y2LL4B;
|
DEVELOPMENT_TEAM = 4BC9Y2LL4B;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.atri.dad.OpenClimb.Watch.AscentlyTests;
|
PRODUCT_BUNDLE_IDENTIFIER = com.atridad.Ascently.AscentlyTests;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
STRING_CATALOG_GENERATE_SYMBOLS = NO;
|
STRING_CATALOG_GENERATE_SYMBOLS = NO;
|
||||||
SWIFT_APPROACHABLE_CONCURRENCY = YES;
|
SWIFT_APPROACHABLE_CONCURRENCY = YES;
|
||||||
@@ -591,7 +591,7 @@
|
|||||||
DEVELOPMENT_TEAM = 4BC9Y2LL4B;
|
DEVELOPMENT_TEAM = 4BC9Y2LL4B;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.atri.dad.OpenClimb.Watch.AscentlyTests;
|
PRODUCT_BUNDLE_IDENTIFIER = com.atridad.Ascently.AscentlyTests;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
STRING_CATALOG_GENERATE_SYMBOLS = NO;
|
STRING_CATALOG_GENERATE_SYMBOLS = NO;
|
||||||
SWIFT_APPROACHABLE_CONCURRENCY = YES;
|
SWIFT_APPROACHABLE_CONCURRENCY = YES;
|
||||||
|
|||||||
Binary file not shown.
@@ -1,56 +0,0 @@
|
|||||||
{
|
|
||||||
"images": [
|
|
||||||
{
|
|
||||||
"filename": "app_logo_256.png",
|
|
||||||
"idiom": "universal",
|
|
||||||
"scale": "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"appearances": [
|
|
||||||
{
|
|
||||||
"appearance": "luminosity",
|
|
||||||
"value": "dark"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"filename": "app_logo_256_dark.png",
|
|
||||||
"idiom": "universal",
|
|
||||||
"scale": "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename": "app_logo_256.png",
|
|
||||||
"idiom": "universal",
|
|
||||||
"scale": "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"appearances": [
|
|
||||||
{
|
|
||||||
"appearance": "luminosity",
|
|
||||||
"value": "dark"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"filename": "app_logo_256_dark.png",
|
|
||||||
"idiom": "universal",
|
|
||||||
"scale": "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename": "app_logo_256.png",
|
|
||||||
"idiom": "universal",
|
|
||||||
"scale": "3x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"appearances": [
|
|
||||||
{
|
|
||||||
"appearance": "luminosity",
|
|
||||||
"value": "dark"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"filename": "app_logo_256_dark.png",
|
|
||||||
"idiom": "universal",
|
|
||||||
"scale": "3x"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"info": {
|
|
||||||
"author": "xcode",
|
|
||||||
"version": 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 3.1 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 3.1 KiB |
@@ -4,6 +4,8 @@
|
|||||||
{
|
{
|
||||||
"layers" : [
|
"layers" : [
|
||||||
{
|
{
|
||||||
|
"blend-mode" : "normal",
|
||||||
|
"glass" : true,
|
||||||
"image-name" : "AscetlyTriangle2.png",
|
"image-name" : "AscetlyTriangle2.png",
|
||||||
"name" : "AscetlyTriangle2",
|
"name" : "AscetlyTriangle2",
|
||||||
"position" : {
|
"position" : {
|
||||||
|
|||||||
@@ -1,115 +0,0 @@
|
|||||||
import Combine
|
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
class AppIconHelper: ObservableObject {
|
|
||||||
|
|
||||||
static let shared = AppIconHelper()
|
|
||||||
|
|
||||||
@Published var isDarkMode: Bool = false
|
|
||||||
|
|
||||||
private init() {
|
|
||||||
}
|
|
||||||
|
|
||||||
func updateDarkModeStatus(for colorScheme: ColorScheme) {
|
|
||||||
isDarkMode = colorScheme == .dark
|
|
||||||
}
|
|
||||||
|
|
||||||
func isInDarkMode(for colorScheme: ColorScheme) -> Bool {
|
|
||||||
return colorScheme == .dark
|
|
||||||
}
|
|
||||||
|
|
||||||
var supportsModernIconFeatures: Bool {
|
|
||||||
if #available(iOS 17.0, *) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func getRecommendedIconVariant(for colorScheme: ColorScheme) -> IconVariant {
|
|
||||||
if colorScheme == .dark {
|
|
||||||
return .dark
|
|
||||||
}
|
|
||||||
return .standard
|
|
||||||
}
|
|
||||||
|
|
||||||
var supportsAlternateIcons: Bool {
|
|
||||||
if #available(iOS 10.3, *) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum IconVariant {
|
|
||||||
case standard
|
|
||||||
case dark
|
|
||||||
case tinted
|
|
||||||
|
|
||||||
var description: String {
|
|
||||||
switch self {
|
|
||||||
case .standard:
|
|
||||||
return "Standard"
|
|
||||||
case .dark:
|
|
||||||
return "Dark Mode"
|
|
||||||
case .tinted:
|
|
||||||
return "Tinted"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum AppIconError: Error, LocalizedError {
|
|
||||||
case notSupported
|
|
||||||
case invalidIconName
|
|
||||||
case systemError(Error)
|
|
||||||
|
|
||||||
var errorDescription: String? {
|
|
||||||
switch self {
|
|
||||||
case .notSupported:
|
|
||||||
return "Alternate icons are not supported on this device"
|
|
||||||
case .invalidIconName:
|
|
||||||
return "The specified icon name is invalid"
|
|
||||||
case .systemError(let error):
|
|
||||||
return "System error: \(error.localizedDescription)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct IconAppearanceModifier: ViewModifier {
|
|
||||||
@Environment(\.colorScheme) private var colorScheme
|
|
||||||
@ObservedObject private var iconHelper = AppIconHelper.shared
|
|
||||||
let onChange: (IconVariant) -> Void
|
|
||||||
|
|
||||||
func body(content: Content) -> some View {
|
|
||||||
content
|
|
||||||
.onChange(of: colorScheme) {
|
|
||||||
iconHelper.updateDarkModeStatus(for: colorScheme)
|
|
||||||
onChange(iconHelper.getRecommendedIconVariant(for: colorScheme))
|
|
||||||
}
|
|
||||||
.onAppear {
|
|
||||||
iconHelper.updateDarkModeStatus(for: colorScheme)
|
|
||||||
onChange(iconHelper.getRecommendedIconVariant(for: colorScheme))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension View {
|
|
||||||
func onIconAppearanceChange(_ onChange: @escaping (IconVariant) -> Void) -> some View {
|
|
||||||
modifier(IconAppearanceModifier(onChange: onChange))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
extension AppIconHelper {
|
|
||||||
static var preview: AppIconHelper {
|
|
||||||
let helper = AppIconHelper()
|
|
||||||
helper.isDarkMode = false
|
|
||||||
return helper
|
|
||||||
}
|
|
||||||
|
|
||||||
static var darkModePreview: AppIconHelper {
|
|
||||||
let helper = AppIconHelper()
|
|
||||||
helper.isDarkMode = true
|
|
||||||
return helper
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
@@ -1,578 +0,0 @@
|
|||||||
import Combine
|
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
|
|
||||||
struct IconTestView: View {
|
|
||||||
@ObservedObject private var iconHelper = AppIconHelper.shared
|
|
||||||
@Environment(\.colorScheme) private var colorScheme
|
|
||||||
@State private var showingTestSheet = false
|
|
||||||
@State private var testResults: [String] = []
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
NavigationStack {
|
|
||||||
List {
|
|
||||||
StatusSection()
|
|
||||||
|
|
||||||
IconDisplaySection()
|
|
||||||
|
|
||||||
TestingSection()
|
|
||||||
|
|
||||||
DebugSection()
|
|
||||||
|
|
||||||
ResultsSection()
|
|
||||||
}
|
|
||||||
.navigationTitle("Icon Testing")
|
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
|
||||||
.toolbar {
|
|
||||||
ToolbarItem(placement: .navigationBarTrailing) {
|
|
||||||
Button("Run Tests") {
|
|
||||||
runIconTests()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.sheet(isPresented: $showingTestSheet) {
|
|
||||||
IconComparisonSheet()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ViewBuilder
|
|
||||||
private func StatusSection() -> some View {
|
|
||||||
Section("System Status") {
|
|
||||||
StatusRow(title: "Color Scheme", value: colorScheme.description)
|
|
||||||
StatusRow(
|
|
||||||
title: "Dark Mode Detected",
|
|
||||||
value: iconHelper.isInDarkMode(for: colorScheme) ? "Yes" : "No")
|
|
||||||
StatusRow(
|
|
||||||
title: "iOS 17+ Features",
|
|
||||||
value: iconHelper.supportsModernIconFeatures ? "Supported" : "Not Available")
|
|
||||||
StatusRow(
|
|
||||||
title: "Alternate Icons",
|
|
||||||
value: iconHelper.supportsAlternateIcons ? "Supported" : "Not Available")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ViewBuilder
|
|
||||||
private func IconDisplaySection() -> some View {
|
|
||||||
Section("Icon Display Test") {
|
|
||||||
VStack(spacing: 20) {
|
|
||||||
// App Icon Representation
|
|
||||||
HStack(spacing: 20) {
|
|
||||||
VStack {
|
|
||||||
RoundedRectangle(cornerRadius: 16)
|
|
||||||
.fill(.blue.gradient)
|
|
||||||
.frame(width: 60, height: 60)
|
|
||||||
.overlay {
|
|
||||||
Image(systemName: "mountain.2.fill")
|
|
||||||
.foregroundColor(.white)
|
|
||||||
.font(.title2)
|
|
||||||
}
|
|
||||||
Text("Standard")
|
|
||||||
.font(.caption)
|
|
||||||
}
|
|
||||||
|
|
||||||
VStack {
|
|
||||||
RoundedRectangle(cornerRadius: 16)
|
|
||||||
.fill(.blue.gradient)
|
|
||||||
.colorInvert()
|
|
||||||
.frame(width: 60, height: 60)
|
|
||||||
.overlay {
|
|
||||||
Image(systemName: "mountain.2.fill")
|
|
||||||
.foregroundColor(.white)
|
|
||||||
.font(.title2)
|
|
||||||
}
|
|
||||||
Text("Dark Mode")
|
|
||||||
.font(.caption)
|
|
||||||
}
|
|
||||||
|
|
||||||
VStack {
|
|
||||||
RoundedRectangle(cornerRadius: 16)
|
|
||||||
.fill(.secondary)
|
|
||||||
.frame(width: 60, height: 60)
|
|
||||||
.overlay {
|
|
||||||
Image(systemName: "mountain.2.fill")
|
|
||||||
.foregroundColor(.primary)
|
|
||||||
.font(.title2)
|
|
||||||
}
|
|
||||||
Text("Tinted")
|
|
||||||
.font(.caption)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// In-App Icon Test
|
|
||||||
HStack(spacing: 16) {
|
|
||||||
Text("In-App Icon:")
|
|
||||||
.font(.subheadline)
|
|
||||||
.fontWeight(.medium)
|
|
||||||
|
|
||||||
Image("AppLogo")
|
|
||||||
.resizable()
|
|
||||||
.frame(width: 24, height: 24)
|
|
||||||
.background(Circle().fill(.quaternary))
|
|
||||||
|
|
||||||
Text("24x24")
|
|
||||||
.font(.caption)
|
|
||||||
.foregroundColor(.secondary)
|
|
||||||
|
|
||||||
Image("AppLogo")
|
|
||||||
.resizable()
|
|
||||||
.frame(width: 32, height: 32)
|
|
||||||
.background(Circle().fill(.quaternary))
|
|
||||||
|
|
||||||
Text("32x32")
|
|
||||||
.font(.caption)
|
|
||||||
.foregroundColor(.secondary)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.padding(.vertical, 8)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ViewBuilder
|
|
||||||
private func DebugSection() -> some View {
|
|
||||||
Section("Dark Mode Debug") {
|
|
||||||
HStack {
|
|
||||||
Text("System Color Scheme:")
|
|
||||||
.foregroundColor(.secondary)
|
|
||||||
Spacer()
|
|
||||||
Text(colorScheme == .dark ? "Dark" : "Light")
|
|
||||||
.fontWeight(.medium)
|
|
||||||
.foregroundColor(colorScheme == .dark ? .green : .orange)
|
|
||||||
}
|
|
||||||
|
|
||||||
HStack {
|
|
||||||
Text("IconHelper Dark Mode:")
|
|
||||||
.foregroundColor(.secondary)
|
|
||||||
Spacer()
|
|
||||||
Text(iconHelper.isDarkMode ? "Dark" : "Light")
|
|
||||||
.fontWeight(.medium)
|
|
||||||
.foregroundColor(iconHelper.isDarkMode ? .green : .orange)
|
|
||||||
}
|
|
||||||
|
|
||||||
HStack {
|
|
||||||
Text("Recommended Variant:")
|
|
||||||
.foregroundColor(.secondary)
|
|
||||||
Spacer()
|
|
||||||
Text(iconHelper.getRecommendedIconVariant(for: colorScheme).description)
|
|
||||||
.fontWeight(.medium)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Current app icon preview
|
|
||||||
VStack {
|
|
||||||
Text("Current App Icon Preview")
|
|
||||||
.font(.headline)
|
|
||||||
.padding(.top)
|
|
||||||
|
|
||||||
HStack(spacing: 20) {
|
|
||||||
VStack {
|
|
||||||
RoundedRectangle(cornerRadius: 16)
|
|
||||||
.fill(colorScheme == .dark ? .black : Color(.systemGray6))
|
|
||||||
.frame(width: 60, height: 60)
|
|
||||||
.overlay {
|
|
||||||
// Mock app icon based on current mode
|
|
||||||
if colorScheme == .dark {
|
|
||||||
ZStack {
|
|
||||||
// Left mountain (yellow/amber) - Android #FFC107
|
|
||||||
Polygon(points: [
|
|
||||||
CGPoint(x: 0.2, y: 0.8), CGPoint(x: 0.45, y: 0.3),
|
|
||||||
CGPoint(x: 0.7, y: 0.8),
|
|
||||||
])
|
|
||||||
.fill(Color(red: 1.0, green: 0.76, blue: 0.03))
|
|
||||||
.stroke(
|
|
||||||
Color(red: 0.11, green: 0.11, blue: 0.11),
|
|
||||||
lineWidth: 1
|
|
||||||
)
|
|
||||||
.frame(width: 50, height: 50)
|
|
||||||
|
|
||||||
// Right mountain (red) - Android #F44336, overlapping
|
|
||||||
Polygon(points: [
|
|
||||||
CGPoint(x: 0.5, y: 0.8), CGPoint(x: 0.75, y: 0.2),
|
|
||||||
CGPoint(x: 1.0, y: 0.8),
|
|
||||||
])
|
|
||||||
.fill(Color(red: 0.96, green: 0.26, blue: 0.21))
|
|
||||||
.stroke(
|
|
||||||
Color(red: 0.11, green: 0.11, blue: 0.11),
|
|
||||||
lineWidth: 1
|
|
||||||
)
|
|
||||||
.frame(width: 50, height: 50)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ZStack {
|
|
||||||
// Left mountain (yellow/amber) - Android #FFC107
|
|
||||||
Polygon(points: [
|
|
||||||
CGPoint(x: 0.2, y: 0.8), CGPoint(x: 0.45, y: 0.3),
|
|
||||||
CGPoint(x: 0.7, y: 0.8),
|
|
||||||
])
|
|
||||||
.fill(Color(red: 1.0, green: 0.76, blue: 0.03))
|
|
||||||
.stroke(
|
|
||||||
Color(red: 0.11, green: 0.11, blue: 0.11),
|
|
||||||
lineWidth: 1
|
|
||||||
)
|
|
||||||
.frame(width: 50, height: 50)
|
|
||||||
|
|
||||||
// Right mountain (red) - Android #F44336, overlapping
|
|
||||||
Polygon(points: [
|
|
||||||
CGPoint(x: 0.5, y: 0.8), CGPoint(x: 0.75, y: 0.2),
|
|
||||||
CGPoint(x: 1.0, y: 0.8),
|
|
||||||
])
|
|
||||||
.fill(Color(red: 0.96, green: 0.26, blue: 0.21))
|
|
||||||
.stroke(
|
|
||||||
Color(red: 0.11, green: 0.11, blue: 0.11),
|
|
||||||
lineWidth: 1
|
|
||||||
)
|
|
||||||
.frame(width: 50, height: 50)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Text(colorScheme == .dark ? "Dark Mode" : "Light Mode")
|
|
||||||
.font(.caption)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.padding(.vertical, 8)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ViewBuilder
|
|
||||||
private func TestingSection() -> some View {
|
|
||||||
Section("Testing Tools") {
|
|
||||||
Button("Compare Light/Dark Modes") {
|
|
||||||
showingTestSheet = true
|
|
||||||
}
|
|
||||||
|
|
||||||
Button("Test Icon Appearance Changes") {
|
|
||||||
testIconAppearanceChanges()
|
|
||||||
}
|
|
||||||
|
|
||||||
Button("Validate Asset Configuration") {
|
|
||||||
validateAssetConfiguration()
|
|
||||||
}
|
|
||||||
|
|
||||||
Button("Check Bundle Resources") {
|
|
||||||
checkBundleResources()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ViewBuilder
|
|
||||||
private func ResultsSection() -> some View {
|
|
||||||
if !testResults.isEmpty {
|
|
||||||
Section("Test Results") {
|
|
||||||
ForEach(testResults.indices, id: \.self) { index in
|
|
||||||
HStack {
|
|
||||||
Image(
|
|
||||||
systemName: testResults[index].contains("PASS")
|
|
||||||
? "checkmark.circle.fill" : "exclamationmark.triangle.fill"
|
|
||||||
)
|
|
||||||
.foregroundColor(testResults[index].contains("PASS") ? .green : .orange)
|
|
||||||
|
|
||||||
Text(testResults[index])
|
|
||||||
.font(.caption)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Button("Clear Results") {
|
|
||||||
testResults.removeAll()
|
|
||||||
}
|
|
||||||
.foregroundColor(.red)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func runIconTests() {
|
|
||||||
testResults.removeAll()
|
|
||||||
|
|
||||||
// Test 1: Check iOS version compatibility
|
|
||||||
if iconHelper.supportsModernIconFeatures {
|
|
||||||
testResults.append("PASS: iOS 17+ features supported")
|
|
||||||
} else {
|
|
||||||
testResults.append(
|
|
||||||
"WARNING: Running on iOS version that doesn't support modern icon features")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test 2: Check dark mode detection
|
|
||||||
let detectedDarkMode = iconHelper.isInDarkMode(for: colorScheme)
|
|
||||||
let systemDarkMode = colorScheme == .dark
|
|
||||||
if detectedDarkMode == systemDarkMode {
|
|
||||||
testResults.append("PASS: Dark mode detection matches system setting")
|
|
||||||
} else {
|
|
||||||
testResults.append("WARNING: Dark mode detection mismatch")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test 3: Check recommended variant
|
|
||||||
let variant = iconHelper.getRecommendedIconVariant(for: colorScheme)
|
|
||||||
testResults.append("PASS: Recommended icon variant: \(variant.description)")
|
|
||||||
|
|
||||||
// Test 4: Test asset availability
|
|
||||||
validateAssetConfiguration()
|
|
||||||
|
|
||||||
// Test 5: Test bundle resources
|
|
||||||
checkBundleResources()
|
|
||||||
}
|
|
||||||
|
|
||||||
private func testIconAppearanceChanges() {
|
|
||||||
iconHelper.updateDarkModeStatus(for: colorScheme)
|
|
||||||
let variant = iconHelper.getRecommendedIconVariant(for: colorScheme)
|
|
||||||
testResults.append(
|
|
||||||
"PASS: Icon appearance test completed - Current variant: \(variant.description)")
|
|
||||||
}
|
|
||||||
|
|
||||||
private func validateAssetConfiguration() {
|
|
||||||
// Check if main bundle contains the expected icon assets
|
|
||||||
let expectedAssets = [
|
|
||||||
"AppIcon",
|
|
||||||
"AppLogo",
|
|
||||||
]
|
|
||||||
|
|
||||||
for asset in expectedAssets {
|
|
||||||
testResults.append("PASS: Asset '\(asset)' configuration found")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func checkBundleResources() {
|
|
||||||
// Check bundle identifier
|
|
||||||
let bundleId = Bundle.main.bundleIdentifier ?? "Unknown"
|
|
||||||
testResults.append("PASS: Bundle ID: \(bundleId)")
|
|
||||||
|
|
||||||
// Check app version
|
|
||||||
let version =
|
|
||||||
Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "Unknown"
|
|
||||||
let build = Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "Unknown"
|
|
||||||
testResults.append("PASS: App version: \(version) (\(build))")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct StatusRow: View {
|
|
||||||
let title: String
|
|
||||||
let value: String
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
HStack {
|
|
||||||
Text(title)
|
|
||||||
.foregroundColor(.secondary)
|
|
||||||
Spacer()
|
|
||||||
Text(value)
|
|
||||||
.fontWeight(.medium)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct IconComparisonSheet: View {
|
|
||||||
@Environment(\.dismiss) private var dismiss
|
|
||||||
@Environment(\.colorScheme) private var colorScheme
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
NavigationStack {
|
|
||||||
VStack(spacing: 30) {
|
|
||||||
Text("Icon Appearance Comparison")
|
|
||||||
.font(.title2)
|
|
||||||
.fontWeight(.bold)
|
|
||||||
|
|
||||||
VStack(spacing: 20) {
|
|
||||||
// Current Mode
|
|
||||||
VStack {
|
|
||||||
Text("Current Mode: \(colorScheme.description)")
|
|
||||||
.font(.headline)
|
|
||||||
|
|
||||||
HStack(spacing: 20) {
|
|
||||||
Image("AppLogo")
|
|
||||||
.resizable()
|
|
||||||
.frame(width: 64, height: 64)
|
|
||||||
.background(
|
|
||||||
RoundedRectangle(cornerRadius: 12)
|
|
||||||
.fill(.quaternary)
|
|
||||||
)
|
|
||||||
|
|
||||||
VStack(alignment: .leading) {
|
|
||||||
Text("AppLogo")
|
|
||||||
.font(.subheadline)
|
|
||||||
.fontWeight(.medium)
|
|
||||||
Text("In-app icon display")
|
|
||||||
.font(.caption)
|
|
||||||
.foregroundColor(.secondary)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Divider()
|
|
||||||
|
|
||||||
// Mock App Icons
|
|
||||||
VStack {
|
|
||||||
Text("App Icon Variants")
|
|
||||||
.font(.headline)
|
|
||||||
|
|
||||||
HStack(spacing: 20) {
|
|
||||||
VStack {
|
|
||||||
RoundedRectangle(cornerRadius: 16)
|
|
||||||
.fill(.white)
|
|
||||||
.frame(width: 64, height: 64)
|
|
||||||
.overlay {
|
|
||||||
ZStack {
|
|
||||||
// Left mountain (yellow/amber)
|
|
||||||
Polygon(points: [
|
|
||||||
CGPoint(x: 0.2, y: 0.8),
|
|
||||||
CGPoint(x: 0.45, y: 0.3),
|
|
||||||
CGPoint(x: 0.7, y: 0.8),
|
|
||||||
])
|
|
||||||
.fill(Color(red: 1.0, green: 0.76, blue: 0.03))
|
|
||||||
.stroke(
|
|
||||||
Color(red: 0.11, green: 0.11, blue: 0.11),
|
|
||||||
lineWidth: 0.5)
|
|
||||||
|
|
||||||
// Right mountain (red), overlapping
|
|
||||||
Polygon(points: [
|
|
||||||
CGPoint(x: 0.5, y: 0.8),
|
|
||||||
CGPoint(x: 0.75, y: 0.2),
|
|
||||||
CGPoint(x: 1.0, y: 0.8),
|
|
||||||
])
|
|
||||||
.fill(Color(red: 0.96, green: 0.26, blue: 0.21))
|
|
||||||
.stroke(
|
|
||||||
Color(red: 0.11, green: 0.11, blue: 0.11),
|
|
||||||
lineWidth: 0.5)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Text("Light")
|
|
||||||
.font(.caption)
|
|
||||||
}
|
|
||||||
|
|
||||||
VStack {
|
|
||||||
RoundedRectangle(cornerRadius: 16)
|
|
||||||
.fill(Color(red: 0.1, green: 0.1, blue: 0.1))
|
|
||||||
.frame(width: 64, height: 64)
|
|
||||||
.overlay {
|
|
||||||
ZStack {
|
|
||||||
// Left mountain (yellow/amber)
|
|
||||||
Polygon(points: [
|
|
||||||
CGPoint(x: 0.2, y: 0.8),
|
|
||||||
CGPoint(x: 0.45, y: 0.3),
|
|
||||||
CGPoint(x: 0.7, y: 0.8),
|
|
||||||
])
|
|
||||||
.fill(Color(red: 1.0, green: 0.76, blue: 0.03))
|
|
||||||
.stroke(
|
|
||||||
Color(red: 0.11, green: 0.11, blue: 0.11),
|
|
||||||
lineWidth: 0.5)
|
|
||||||
|
|
||||||
// Right mountain (red), overlapping
|
|
||||||
Polygon(points: [
|
|
||||||
CGPoint(x: 0.5, y: 0.8),
|
|
||||||
CGPoint(x: 0.75, y: 0.2),
|
|
||||||
CGPoint(x: 1.0, y: 0.8),
|
|
||||||
])
|
|
||||||
.fill(Color(red: 0.96, green: 0.26, blue: 0.21))
|
|
||||||
.stroke(
|
|
||||||
Color(red: 0.11, green: 0.11, blue: 0.11),
|
|
||||||
lineWidth: 0.5)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Text("Dark")
|
|
||||||
.font(.caption)
|
|
||||||
}
|
|
||||||
|
|
||||||
VStack {
|
|
||||||
RoundedRectangle(cornerRadius: 16)
|
|
||||||
.fill(.clear)
|
|
||||||
.frame(width: 64, height: 64)
|
|
||||||
.overlay {
|
|
||||||
ZStack {
|
|
||||||
// Left mountain (monochrome)
|
|
||||||
Polygon(points: [
|
|
||||||
CGPoint(x: 0.2, y: 0.8),
|
|
||||||
CGPoint(x: 0.45, y: 0.3),
|
|
||||||
CGPoint(x: 0.7, y: 0.8),
|
|
||||||
])
|
|
||||||
.fill(.black.opacity(0.8))
|
|
||||||
.stroke(.black, lineWidth: 0.5)
|
|
||||||
|
|
||||||
// Right mountain (monochrome), overlapping
|
|
||||||
Polygon(points: [
|
|
||||||
CGPoint(x: 0.5, y: 0.8),
|
|
||||||
CGPoint(x: 0.75, y: 0.2),
|
|
||||||
CGPoint(x: 1.0, y: 0.8),
|
|
||||||
])
|
|
||||||
.fill(.black.opacity(0.9))
|
|
||||||
.stroke(.black, lineWidth: 0.5)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Text("Tinted")
|
|
||||||
.font(.caption)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
|
|
||||||
VStack(spacing: 8) {
|
|
||||||
Text("Switch between light/dark mode in Settings")
|
|
||||||
.font(.subheadline)
|
|
||||||
.foregroundColor(.secondary)
|
|
||||||
.multilineTextAlignment(.center)
|
|
||||||
|
|
||||||
Text("The icon should adapt automatically")
|
|
||||||
.font(.caption)
|
|
||||||
.foregroundColor(.secondary)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.padding()
|
|
||||||
.navigationTitle("Icon Test")
|
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
|
||||||
.toolbar {
|
|
||||||
ToolbarItem(placement: .navigationBarTrailing) {
|
|
||||||
Button("Done") {
|
|
||||||
dismiss()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ColorScheme {
|
|
||||||
var description: String {
|
|
||||||
switch self {
|
|
||||||
case .light:
|
|
||||||
return "Light"
|
|
||||||
case .dark:
|
|
||||||
return "Dark"
|
|
||||||
@unknown default:
|
|
||||||
return "Unknown"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#Preview {
|
|
||||||
IconTestView()
|
|
||||||
}
|
|
||||||
|
|
||||||
#Preview("Dark Mode") {
|
|
||||||
IconTestView()
|
|
||||||
.preferredColorScheme(.dark)
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Polygon: Shape {
|
|
||||||
let points: [CGPoint]
|
|
||||||
|
|
||||||
func path(in rect: CGRect) -> Path {
|
|
||||||
var path = Path()
|
|
||||||
|
|
||||||
guard !points.isEmpty else { return path }
|
|
||||||
|
|
||||||
let scaledPoints = points.map { point in
|
|
||||||
CGPoint(
|
|
||||||
x: point.x * rect.width,
|
|
||||||
y: point.y * rect.height
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
path.move(to: scaledPoints[0])
|
|
||||||
for point in scaledPoints.dropFirst() {
|
|
||||||
path.addLine(to: point)
|
|
||||||
}
|
|
||||||
path.closeSubpath()
|
|
||||||
|
|
||||||
return path
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -44,19 +44,18 @@ class ThemeManager: ObservableObject {
|
|||||||
accentColor = .blue
|
accentColor = .blue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Curated list of preset colors that maintain good contrast
|
|
||||||
static let presetColors: [Color] = [
|
static let presetColors: [Color] = [
|
||||||
.blue, // Default Blue
|
.blue,
|
||||||
.purple, // Purple
|
.purple,
|
||||||
.pink, // Pink
|
.pink,
|
||||||
.red, // Red
|
.red,
|
||||||
.orange, // Orange
|
.orange,
|
||||||
.green, // Green
|
.green,
|
||||||
.teal, // Teal
|
.teal,
|
||||||
.indigo, // Indigo
|
.indigo,
|
||||||
.mint, // Mint
|
.mint,
|
||||||
Color(uiColor: .systemBrown), // Brown
|
Color(uiColor: .systemBrown),
|
||||||
Color(uiColor: .systemCyan) // Cyan
|
Color(uiColor: .systemCyan)
|
||||||
]
|
]
|
||||||
|
|
||||||
var contrastingTextColor: Color {
|
var contrastingTextColor: Color {
|
||||||
@@ -68,10 +67,8 @@ class ThemeManager: ObservableObject {
|
|||||||
|
|
||||||
uiColor.getRed(&red, green: &green, blue: &blue, alpha: &alpha)
|
uiColor.getRed(&red, green: &green, blue: &blue, alpha: &alpha)
|
||||||
|
|
||||||
// Calculate relative luminance
|
|
||||||
let luminance = 0.299 * red + 0.587 * green + 0.114 * blue
|
let luminance = 0.299 * red + 0.587 * green + 0.114 * blue
|
||||||
|
|
||||||
// Return black for light colors, white for dark colors
|
|
||||||
return luminance > 0.5 ? .black : .white
|
return luminance > 0.5 ? .black : .white
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -427,9 +427,6 @@ struct ExportDataView: View {
|
|||||||
if let fileURL = tempFileURL {
|
if let fileURL = tempFileURL {
|
||||||
ShareLink(
|
ShareLink(
|
||||||
item: fileURL,
|
item: fileURL,
|
||||||
preview: SharePreview(
|
|
||||||
"Ascently Data Export",
|
|
||||||
image: Image("AppLogo"))
|
|
||||||
) {
|
) {
|
||||||
Label("Share Data", systemImage: "square.and.arrow.up")
|
Label("Share Data", systemImage: "square.and.arrow.up")
|
||||||
.font(.headline)
|
.font(.headline)
|
||||||
|
|||||||
Reference in New Issue
Block a user