-1

To change TabBar background and other properties you have about 2 ways to process.

  1. via the init() of the view containing the TabView (see: https://stackoverflow.com/a/56971573/2192483)
  2. via an .onAppear() directly on the TabView (see: https://stackoverflow.com/a/63414605/2192483 - thanks to @Asperi ; see: https://schwiftyui.com/swiftui/customizing-your-tabviews-bar-in-swiftui/ - thanks to @Dan O'Leary)

These solutions are efficients at View load and if you reload Tab Bar by touching tabs.

But IT DOES'NT WORK, if you want to change a TabBar properties programmatically, without User interaction on the TabBar Buttons, through the regular way of properties defined in @Published values. This doesn't work because either though init() or .onAppear(), these 2 methods are not subscribers of prop. publishers so the view embedding the TabView doesn't reload.

Luc-Olivier
  • 3,715
  • 2
  • 29
  • 29

1 Answers1

0

I finally found a way to trigger the TabView reload not from user tab button touches, with regular @Published properties.

Here is example based on an app with a settings panel allowing to change app skin, including the TabBar background color.

1) The Main struct embed the ContentView that embed the TabView

• SettingsModel embed the properties that share a) the skin values b) a flag to trigger the reload

• ContentView owns one param: skinChanged

@main
struct MyApp: App {
    @ObservedObject private var settings = SettingsModel.shared
    
    var body: some Scene {
        WindowGroup {
            ContentView(skinChanged: settings.skinChanged)
        }
    }
}

.

2) The ContentView struct embed the TabView

• init(skinChanged: Bool) allows to custom TabBar appearance that will reload if settings.skinChanged changes of value

• init / if !skinChanged { } applies TabBar appearance only if needed

• body / if !self.settings.skinChanged { } allows to create a change in the view alternating from TabView to a Rectangle (as a background)

struct ContentView: View {
    @ObservedObject private var settings = SettingsModel.shared

    init(skinChanged: Bool) {
        if !skinChanged {
            let tabBarAppearance = UITabBar.appearance()
            tabBarAppearance.backgroundColor = UIColor(settings.skin.tabBarBg)
            tabBarAppearance.unselectedItemTintColor = UIColor(settings.skin.tabBarUnselected)
        }
    }
    
    var body: some View {
        if !self.settings.skinChanged {
            TabView(selection: $someStateProp) {
                ...
            }
        } else {
            Rectangle()
                .fill(settings.skin.tabBarBg)
        }
    }
}

.

3) The SettingModel struct publishes properties that trigger skin changes and skin values

• In order to create a change in ContentView(skinChanged:) with settings.skinChanged, the value must pass from false to true then back to false

• To let the UI applies each changes the 2 changes must be parallelized with 2 synchronized tasks

class SettingsModel: ObservableObject {
    static public let shared = SettingsModel()
    ...
    
    @Published var skin = Skin(style: .modern)
    @Published var skinId = 0 {
        didSet {
            ...
            self.skin = Skin(style: Skin.Style.allCases[self.skinId])
            DispatchQueue.main.asyncAfter(deadline: .now()) {
                self.skinChanged = true
            }
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
                self.skinChanged = false
            }
        }
    }
    @Published var skinChanged = false
    
    ...
}
Luc-Olivier
  • 3,715
  • 2
  • 29
  • 29