Skip to content

Instantly share code, notes, and snippets.

@lifeutilityapps
Created November 10, 2024 13:29
Show Gist options
  • Save lifeutilityapps/d0c5eb3a80a6a101ec80257c80bb9316 to your computer and use it in GitHub Desktop.
Save lifeutilityapps/d0c5eb3a80a6a101ec80257c80bb9316 to your computer and use it in GitHub Desktop.
A simple setting to control visible tabs
// 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