I'm developing a SwiftUI app with a custom BottomNavigationBar view and a Home view. The Home view is designed to display a background color and image that extend all the way up to the status bar, which works perfectly when accessed on its own. However, when I navigate to Home through the BottomNavigationBar, the background no longer reaches the status bar area.
My goal is to make the BottomNavigationBar affect only the bottom of the screen, without interfering with the status bar or the full-screen display of Home.
BottomNavigationBar.swift
import SwiftUI
struct BottomNavigationBar: View {
@State private var selectedTab: Int
@State private var selectedRole: UserRole
init(initialRole: UserRole = .maker) {
_selectedRole = State(initialValue: initialRole)
_selectedTab = State(initialValue: 0)
}
var body: some View {
VStack(spacing: 0) {
TabView(selection: $selectedTab) {
roleBasedHomeView(for: selectedTab)
.tag(0)
// Task View
TaskView()
.tag(1)
// Token View
TokenView()
.tag(2)
// Dashboard View
Dashboard()
.tag(3)
// Profile View
Profile()
.tag(4)
}
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
HStack {
BottomNavItem(icon: "house", title: "Home", isSelected: selectedTab == 0) {
selectedTab = 0
}
BottomNavItem(icon: "list.bullet", title: "Task", isSelected: selectedTab == 1) {
selectedTab = 1
}
BottomNavItem(icon: "key.fill", title: "Token", isSelected: selectedTab == 2) {
selectedTab = 2
}
BottomNavItem(icon: "chart.bar", title: "Dashboard", isSelected: selectedTab == 3) {
selectedTab = 3
}
BottomNavItem(icon: "person", title: "Profile", isSelected: selectedTab == 4) {
selectedTab = 4
}
}
.padding()
.background(Color.white)
.shadow(color: Color.gray.opacity(0.2), radius: 5, x: 0, y: -2)
}
.edgesIgnoringSafeArea(.bottom)
.statusBar(hidden: true)
}
@ViewBuilder
private func roleBasedHomeView(for tab: Int) -> some View {
switch tab {
case 0:
switch selectedRole {
case .maker:
Home()
case .approver:
HomeAR(role: .approver)
case .releaser:
HomeAR(role: .releaser)
case .approverReleaser:
HomeAR(role: .approverReleaser)
case .makerApprover:
HomeAR(role: .makerApprover)
case .makerApproverReleaserSME:
HomeAR(role: .makerApproverReleaserSME)
case .sysAdmin:
HomeSysAdmin()
default:
WelcomeView()
}
case 1:
TaskView()
case 2:
TokenView()
case 3:
Dashboard()
case 4:
Profile()
default:
WelcomeView()
}
}
}
struct BottomNavItem: View {
var icon: String
var title: String
var isSelected: Bool
var action: () -> Void
var body: some View {
Button(action: action) {
VStack(spacing: 4) {
Image(systemName: icon)
.font(.system(size: 24))
.foregroundColor(isSelected ? Color(hex: "#1E8F7D") : .gray)
Text(title)
.font(.caption)
.foregroundColor(isSelected ? Color(hex: "#1E8F7D") : .gray)
}
.frame(maxWidth: .infinity)
}
}
}
Home.swift
import SwiftUI
struct Home: View {
@State private var selectedTab: Int = 0
@State private var selectedRole: UserRole = .maker
@State private var showStickyHeader = false
@State private var isHidden = true
@State private var selectedCompany: String = "PT Pertamina Tbk"
@State private var showDropdown = false
let companies = ["PT Pertamina Tbk", "PLN", "PT Timbul Tenggelam"]
var body: some View {
ZStack(alignment: .top) {
Color.primary
.ignoresSafeArea(edges: .all)
.frame(maxWidth: .infinity, maxHeight: .infinity)
ScrollView(showsIndicators: false) {
ZStack(alignment: .top) {
VStack(spacing: 0) {
Color.primary
.frame(height: 250)
.overlay(
Image("pattern_bg_transparent")
.resizable()
.scaledToFill()
.frame(height: 400)
.opacity(0.8)
.clipped()
.ignoresSafeArea(.all)
)
}
.overlay(
GeometryReader { proxy in
Color.clear
.preference(key: ScrollOffsetPreferenceKey.self, value: proxy.frame(in: .global).minY)
}
)
.onPreferenceChange(ScrollOffsetPreferenceKey.self) { offset in
withAnimation(.spring(response: 1.2, dampingFraction: 0.7, blendDuration: 0.8).delay(0.8)) {
showStickyHeader = offset < -50
}
}
// Main contents
VStack(spacing: 16) {
VStack(spacing: 2) {
HStack {
Text("Fredrick Pardosi")
.font(.system(size: 20, weight: .bold))
.foregroundColor(.white)
Spacer()
HStack(spacing: 4) {
Image(systemName: "building.2")
.foregroundColor(.white)
Image(systemName: showDropdown ? "chevron.up" : "chevron.down")
.foregroundColor(.white)
}
.padding(.horizontal, 8)
.padding(.vertical, 6)
.background(RoundedRectangle(cornerRadius: 12).fill(Color.white.opacity(0.2)))
.onTapGesture {
withAnimation {
showDropdown.toggle()
}
}
Image(systemName: "bell")
.foregroundColor(.white)
.padding(8)
.background(Circle().fill(Color.white.opacity(0.2)))
}
.padding(.horizontal, 16)
HStack {
Text("Maker")
.font(.system(size: 14, weight: .medium))
.foregroundColor(.white)
.padding(4)
Spacer()
}
.padding(.horizontal, 16)
}
VStack(spacing: 16) {
TotalBalance(isHidden: $isHidden)
.padding(.top, 8)
DepositLoanView(isHidden: $isHidden)
VStack(alignment: .leading, spacing: 16) {
PendingTaskView(pending: 52, rejected: 1100, executed: 983, title: "Status Transaksi")
ServiceSlider(title: "Status Transaksi by Service")
FeaturedMenu()
}
}
}
}
.background(Color(UIColor.systemGray6).ignoresSafeArea())
}
.refreshable {
refreshContent()
}
if showStickyHeader {
StickyHeader()
.transition(.move(edge: .top))
}
}
.navigationBarBackButtonHidden(true)
}
private func refreshContent() {
print("Content refreshed!")
}
}
The Problem
When navigating to Home directly, the background color and image extend perfectly up to the status bar. However, accessing Home through the BottomNavigationBar prevents the background from extending to the status bar.
See the difference between the two images above.
I want the layout to look exactly like it does in Home.swift
How can I ensure that the BottomNavigationBar does not interfere with the full-screen behavior of Home, so that Home can reach up to the status bar?
Any suggestions or guidance on how to properly configure BottomNavigationBar or Home would be greatly appreciated. Thank you!
Because TabView respects the safe area insets by default, child views (like Home
) may be restricted inside the safe area. When these limits are already enforced by the parent view (the TabView
), using .edgesIgnoringSafeArea(.all)
or .ignoresSafeArea()
in your Home
view is insufficient.
We must make sure that the TabView and its child views appropriately disregard the safe area limits in order to resolve this issue. There are two primary approaches that you can attempt.
TabView(selection: $selectedTab) {
roleBasedHomeView(for: selectedTab)
.ignoresSafeArea() // Add this line
.tag(0)
TaskView()
.ignoresSafeArea()
.tag(1)
TokenView()
.ignoresSafeArea()
.tag(2)
Dashboard()
.ignoresSafeArea()
.tag(3)
Profile()
.ignoresSafeArea()
.tag(4)
}
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
Then the second one would be to warp the TabView
in a ZStack and add the .ignoresSafeArea()
after the ZStack closing braces. Let me know if this works
Something else to consider is changing the ..navigationBarBackButtonHidden(true)
to navigationBarHidden(true)