Created
November 10, 2024 13:29
-
-
Save lifeutilityapps/d0c5eb3a80a6a101ec80257c80bb9316 to your computer and use it in GitHub Desktop.
A simple setting to control visible tabs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Inside your root TabView, add conditional logic to | |
// hide and show the tab icons if the associated | |
// user default hide is set to hidden. You might need | |
// to force a re-render of the entire TabView to | |
// prevent buggy effects. My implementation shows | |
// a progress view for 750ms to force the re-rendered tabs. | |
import SwiftUI | |
struct TabVisibilitySetting: View { | |
@EnvironmentObject var dm: UIDataModel | |
// Sheet | |
@State private var isSheetOpen = false | |
// User Defaults State | |
@AppStorage(EApplicationUITab.debts.userDefaultKey) var isUDDebtsHidden: Bool = false | |
@AppStorage(EApplicationUITab.spending.userDefaultKey) var isUDSpendingHidden: Bool = false | |
@AppStorage(EApplicationUITab.savings.userDefaultKey) var isUDSavingsHidden: Bool = false | |
@AppStorage(EApplicationUITab.learn.userDefaultKey) var isUDLearnHidden: Bool = false | |
// Tab State | |
@State private var isDebtsHidden: Bool = false | |
@State private var isSpendingHidden: Bool = false | |
@State private var isSavingsHidden: Bool = false | |
@State private var isLearnHidden: Bool = false | |
// Original Tabs state | |
@State private var initialHiddenTabs: [Bool] = [false, false, false, false] | |
var body: some View { | |
Button { | |
handleOpen() | |
} label: { | |
HStack { | |
Label("Hide Tabs", systemImage: SCL.icons.bottomTabs) | |
Spacer() | |
Text(disabledTabsUD.isEmpty ? "All Shown" : "\(disabledTabsUD.count) Hidden") | |
.font(.caption) | |
.foregroundStyle(SCL.colors.labelSecondaryColor) | |
} | |
.onAppear { | |
handleAppear() | |
} | |
} | |
.sheet(isPresented: $isSheetOpen) { | |
VStack(alignment: .leading, spacing: 0) { | |
StandardSheetTitle(title: "Tab Visibility", subtitle: "Hide or Show \(Global.Legal.appDisplayName) Features", onClose: handleClose) | |
VStack(alignment: .center, spacing: 0) { | |
Spacer() | |
if(!disabledTabsLocal.isEmpty){ | |
Text("Hidden Tab\(String.getPlural(disabledTabsLocal.count))") | |
.font(.caption) | |
.foregroundStyle(SCL.colors.labelSecondaryColor) | |
.padding(.bottom) | |
HStack(spacing: SCL.ui.spacing * 2) { | |
Spacer() | |
if(isDebtsHidden) { | |
TabIcon(tab: .debts, onClick: handleToggleTab, canHide: true) | |
} | |
if(isSpendingHidden) { | |
TabIcon(tab: .spending, onClick: handleToggleTab, canHide: true) | |
} | |
if(isSavingsHidden){ | |
TabIcon(tab: .savings, onClick: handleToggleTab, canHide: true) | |
} | |
if(isLearnHidden) { | |
TabIcon(tab: .learn, onClick: handleToggleTab, canHide: true) | |
} | |
Spacer() | |
} | |
} else { | |
Text("No tabs hidden") | |
.font(.caption) | |
.foregroundStyle(SCL.colors.labelSecondaryColor) | |
.padding(.bottom) | |
TabIcon(tab: .learn, onClick: handleToggleTab, canHide: false) | |
.opacity(0) | |
} | |
Spacer() | |
HStack { | |
Spacer() | |
Text(disabledTabsLocal.isEmpty ? "Tap to Hide" : "") | |
.font(.caption) | |
.foregroundStyle(SCL.colors.labelSecondaryColor) | |
Spacer() | |
} | |
.padding() | |
VStack(alignment: .center, spacing: SCL.ui.spacing * 1.25) { | |
HStack() { | |
if(disabledTabsLocal.count == 3){ | |
Spacer() | |
} | |
if(!isDebtsHidden) { | |
TabIcon(tab: .debts, onClick: handleToggleTab, canHide: canHideAdditionalTab) | |
Spacer() | |
} | |
if(!isSpendingHidden) { | |
TabIcon(tab: .spending, onClick: handleToggleTab, canHide: canHideAdditionalTab) | |
Spacer() | |
} | |
if(!isSavingsHidden){ | |
TabIcon(tab: .savings, onClick: handleToggleTab, canHide: canHideAdditionalTab) | |
Spacer() | |
} | |
if(!isLearnHidden) { | |
TabIcon(tab: .learn, onClick: handleToggleTab, canHide: canHideAdditionalTab) | |
Spacer() | |
} | |
TabIcon(tab: .settings, onClick: handleToggleTab, canHide: false) | |
if(disabledTabsLocal.count == 3){ | |
Spacer() | |
} | |
} | |
RoundedRectangle(cornerRadius: 10).fill(SCL.colors.labelSecondaryColor) | |
.frame(width: SCL.ui.screenWidth * 0.35, height: 4) | |
.padding(.top, SCL.ui.spacing / 2) | |
} | |
.padding(.horizontal) | |
.padding(.vertical, SCL.ui.spacing) | |
.background( | |
RoundedRectangle(cornerRadius: 10).fill(SCL.colors.backgroundColor) | |
.shadow(color: SCL.colors.labelColor.opacity(0.1), radius: 10) | |
) | |
Spacer() | |
} | |
.padding() | |
.background( | |
SCL.colors.backgroundFormSectionColor | |
) | |
StandardSheetControls(saveLabel: "Save Tabs", cancelLabel: "Back", isSaveDisabled: !didUserChangeState, onClose: handleClose, onSave: handleSave) | |
}.interactiveDismissDisabled() | |
} | |
} | |
struct TabIcon: View { | |
let tab: EApplicationUITab | |
let onClick: (EApplicationUITab) -> Void | |
let canHide: Bool | |
var isLocked:Bool { | |
return tab.isTabLocked | |
} | |
var icon: String { | |
if(tab.icon.contains(".fill") || tab == .debts || tab == .learn) { | |
return tab.icon | |
} else { | |
return tab.icon.iconFill() | |
} | |
} | |
var body: some View { | |
Button { | |
if(!isLocked) { | |
onClick(tab) | |
} | |
} label: { | |
VStack(spacing: SCL.ui.spacing * 0.8) { | |
HStack { | |
} | |
.frame(width: 24, height: 24) | |
.overlay { | |
Image(systemName: icon) | |
.font(.title2) | |
} | |
Text(tab.displayName) | |
.font(.caption) | |
.scaleEffect(0.9) | |
} | |
.foregroundStyle(SCL.colors.labelSecondaryColor) | |
.opacity(isLocked || !canHide ? 0.35 : 1) | |
}.disabled(isLocked || !canHide) | |
} | |
} | |
} | |
// MARK: On Appear Logic | |
extension TabVisibilitySetting { | |
func handleAppear() { | |
syncWithUserDefaults() | |
setInitialHiddenState() | |
} | |
func syncWithUserDefaults() { | |
isDebtsHidden = isUDDebtsHidden | |
isSpendingHidden = isUDSpendingHidden | |
isSavingsHidden = isUDSavingsHidden | |
isLearnHidden = isUDLearnHidden | |
} | |
func setInitialHiddenState() { | |
initialHiddenTabs = [isUDDebtsHidden, isUDSpendingHidden, isUDSavingsHidden, isLearnHidden] | |
} | |
} | |
// MARK: Tab UI Helpers | |
extension TabVisibilitySetting { | |
func handleToggleTab(_ tab: EApplicationUITab) { | |
withAnimation { | |
switch tab { | |
case .debts: | |
if(isDebtsHidden) { | |
isDebtsHidden = false | |
} else if (canHideAdditionalTab) { | |
isDebtsHidden = true | |
} | |
case .spending: | |
isSpendingHidden.toggle() | |
case .savings: | |
isSavingsHidden.toggle() | |
case .learn: | |
isLearnHidden.toggle() | |
default: | |
print("Do nothing") | |
} | |
} | |
} | |
var localStateArray: [Bool] { | |
return [isDebtsHidden, isSpendingHidden, isSavingsHidden, isLearnHidden] | |
} | |
var disabledTabsUD: [Bool] { | |
return [isUDDebtsHidden, isUDSpendingHidden, isUDSavingsHidden, isUDLearnHidden].allTrue | |
} | |
var disabledTabsLocal: [Bool] { | |
return [isDebtsHidden, isSpendingHidden, isSavingsHidden, isLearnHidden].allTrue | |
} | |
var canHideAdditionalTab: Bool { | |
return disabledTabsLocal.count < 3 | |
} | |
var didUserChangeState: Bool { | |
var result = false | |
for (index, _) in initialHiddenTabs.enumerated() { | |
if(initialHiddenTabs[index] != localStateArray[index]) { | |
result = true | |
break | |
} | |
} | |
return result | |
} | |
} | |
// MARK: Sheet controls | |
extension TabVisibilitySetting { | |
func handleOpen() { | |
isSheetOpen = true | |
syncWithUserDefaults() | |
} | |
func handleClose() { | |
isSheetOpen = false | |
} | |
} | |
// MARK: Save Tabs | |
extension TabVisibilitySetting { | |
func setUserDefaults() { | |
isUDDebtsHidden = isDebtsHidden | |
isUDSpendingHidden = isSpendingHidden | |
isUDSavingsHidden = isSavingsHidden | |
isUDLearnHidden = isLearnHidden | |
} | |
func handleSave() { | |
handleClose() | |
dm.refreshApp(delay: 1.75) | |
SCL.util.asyncAfter { | |
setUserDefaults() | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment