Improve Catalyst appearance

- Revert to more "stable" iPad idiom

- Set accent color the proper way

- Use .tint when available

- Unify navigation style by idiom

- Retain navigation bars in sidebar/detail

- Lighten sidebar appearance

- Fix Menu style (dropdown -> button)

- Use native Picker (dropdown)

- Use switch toggles rather than checkboxes

- Replace .actionSheet with .alert

- Increase minimum row height

CAVEAT: on Mac with iPad idiom, having a Section in .sidebar
produces artifacts. Header keeps changing height for no reason.
Retain Section on iPad multitasking only to not break navigation.
This commit is contained in:
Davide De Rosa 2022-05-04 19:38:44 +02:00
parent eaaa1fe260
commit 743facca6b
5 changed files with 45 additions and 39 deletions

View File

@ -1229,6 +1229,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Passepartout/App/App.entitlements;
CODE_SIGN_STYLE = Manual;
@ -1245,6 +1246,7 @@
PROVISIONING_PROFILE_SPECIFIER = "match Development com.algoritmico.ios.Passepartout";
"PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "match Development com.algoritmico.ios.Passepartout catalyst";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
@ -1252,6 +1254,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Passepartout/App/App.entitlements;
CODE_SIGN_STYLE = Manual;
@ -1267,6 +1270,7 @@
PRODUCT_NAME = Passepartout;
PROVISIONING_PROFILE_SPECIFIER = "match Development com.algoritmico.ios.Passepartout";
"PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "match Development com.algoritmico.ios.Passepartout catalyst";
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};

View File

@ -22,7 +22,7 @@ internal typealias AssetImageTypeAlias = ImageAsset.Image
// swiftlint:disable identifier_name line_length nesting type_body_length type_name
internal enum Asset {
internal enum Assets {
internal static let accentColor = ColorAsset(name: "accentColor")
internal static let accentColor = ColorAsset(name: "AccentColor")
internal static let lightTextColor = ColorAsset(name: "lightTextColor")
internal static let logo = ImageAsset(name: "logo")
internal static let primaryColor = ColorAsset(name: "primaryColor")

View File

@ -57,16 +57,30 @@ extension View {
extension View {
func themeGlobal() -> some View {
#if targetEnvironment(macCatalyst)
self
#else
let color = themeAccentColor
return accentColor(color)
.toggleStyle(SwitchToggleStyle(tint: color))
.themeNavigationViewStyle()
#endif
themeNavigationViewStyle()
.themeTint()
.themeToggleStyle()
.menuStyle(.borderlessButton)
}
@ViewBuilder
private func themeTint() -> some View {
if #available(iOS 15, *) {
tint(.accentColor)
} else {
self
}
}
@ViewBuilder
private func themeToggleStyle() -> some View {
if #available(iOS 15, *) {
toggleStyle(.switch)
} else {
toggleStyle(SwitchToggleStyle(tint: .accentColor))
}
}
@ViewBuilder
private func themeNavigationViewStyle() -> some View {
switch themeIdiom {
@ -80,29 +94,22 @@ extension View {
func themePrimaryView() -> some View {
#if targetEnvironment(macCatalyst)
navigationBarHidden(true)
navigationBarTitleDisplayMode(.inline)
#else
navigationBarTitleDisplayMode(.large)
.navigationTitle(Unlocalized.appName)
#endif
}
func themeSecondaryView() -> some View {
#if targetEnvironment(macCatalyst)
navigationBarHidden(true)
#else
navigationBarTitleDisplayMode(.inline)
.listStyle(.insetGrouped)
#endif
}
}
// MARK: Colors
extension View {
fileprivate var themeAccentColor: Color {
Color(Asset.Assets.accentColor.color)
}
fileprivate var themePrimaryBackgroundColor: Color {
Color(Asset.Assets.primaryColor.color)
}
@ -307,15 +314,15 @@ extension String {
// MARK: Styles
extension View {
func themeAccentForegroundStyle() -> some View {
foregroundColor(.accentColor)
}
var themePrimaryBackground: some View {
themePrimaryBackgroundColor
.ignoresSafeArea()
}
func themeAccentForegroundStyle() -> some View {
foregroundColor(themeAccentColor)
}
func themeSecondaryTextStyle() -> some View {
foregroundColor(themeSecondaryColor)
}

View File

@ -112,7 +112,6 @@ struct OrganizerView: View {
allowsMultipleSelection: false,
onCompletion: onHostFileImporterResult
).onOpenURL(perform: onOpenURL)
.navigationTitle(Unlocalized.appName)
.themePrimaryView()
}

View File

@ -83,26 +83,22 @@ extension ProfileView {
deleteProfileButton
} label: {
themeSettingsMenuImage.asSystemImage
}.actionSheet(item: $actionSheetType) {
}.alert(item: $actionSheetType) {
switch $0 {
case .uninstallVPN:
return ActionSheet(
title: Text(L10n.Profile.Alerts.UninstallVpn.message),
message: nil,
buttons: [
.destructive(Text(uninstallVPNTitle), action: uninstallVPN),
.cancel(Text(cancelTitle))
]
return Alert(
title: Text(uninstallVPNTitle),
message: Text(L10n.Profile.Alerts.UninstallVpn.message),
primaryButton: .destructive(Text(uninstallVPNTitle), action: uninstallVPN),
secondaryButton: .cancel(Text(cancelTitle))
)
case .deleteProfile:
return ActionSheet(
title: Text(L10n.Organizer.Alerts.RemoveProfile.message(header.name)),
message: nil,
buttons: [
.destructive(Text(deleteProfileTitle), action: removeProfile),
.cancel(Text(cancelTitle))
]
return Alert(
title: Text(deleteProfileTitle),
message: Text(L10n.Organizer.Alerts.RemoveProfile.message(header.name)),
primaryButton: .destructive(Text(deleteProfileTitle), action: removeProfile),
secondaryButton: .cancel(Text(cancelTitle))
)
}
}