{
"$type": "site.standard.document",
"coverImage": {
"$type": "blob",
"ref": {
"$link": "bafkreifg6nddhq5f2xf2au7obid4zhopighcy2eize4xn4xj6snyvzvdde"
},
"mimeType": "image/webp",
"size": 27220
},
"description": "The Tab Bar Strikes Back.",
"path": "/2025-08-17-adopting-liquid-glass-part-iv-singapore-buses-revisiting-the-tab-bar/",
"publishedAt": "2025-08-17T10:00:48.000Z",
"site": "at://did:plc:ex23caczr45rodrfcxrwps6h/site.standard.publication/self",
"tags": [
"design-diaries",
"singapore-buses",
"liquid-glass"
],
"textContent": "In my first article in this series, Adopting Liquid Glass, Part I (Singapore Buses), I ran into a critical bug that made it problematic to adopt the new Tab Bar design:\n\nOn my Map tab I was using a tabViewBottomAccessory to show the nearest bus stop to the user—to give them an easy way to tap and show arrival information. However, the accessory was available across all tabs where it made no sense (like Settings). Trying to hide the accessory (using EmptyView) when the selected tab wasn’t the map worked, but navigating back to the map caused the map to stop extending into the safe area, resulting in a lovely white bar along the top.\n\nLuckily, this bug has been fixed in the recent betas so I've revisited my original design and the outcomes are positive.\n\nTab Bar Bottom Accessory Visibility\n\nFrom a usability perspective, it only makes sense to display the accessory when two conditions are met:\n\nThe map tab is displayed; and,\n\nThe user has given permission to track location\n\nThe simplified code below shows how this is achieved when tracking the tab bar selection:\n\nenum Tabs: Equatable, Hashable {\ncase maps\ncase settings\ncase search\n}\n\n@main\nstruct sgbusesApp: App {\n\n// MARK: State\n@State private var selectedTabIndex: Tabs = .maps\n\nvar body: some Scene {\nWindowGroup {\nTabView(selection: $selectedTabIndex) {\nTab(\"label.title.map\", systemImage: \"map\", value: .maps) {\nBusStopMapView()\n}\n\nTab(\"label.title.settings\", systemImage: \"gear\", value: .settings) {\nSettingsView()\n}\n\nTab(\"label.title.search\", systemImage: \"location.magnifyingglass\", value: .search, role: .search) {\nBusStopListView()\n}\n}\n.tabViewBottomAccessory {\nif selectedTabIndex == .maps {\nHStack {\nImage(systemName: \"bus.fill\")\n.font(.body)\nVStack(alignment: .leading) {\nText(verbatim: database.allStops.first!.busStopDescription)\n.font(.caption)\n.bold()\nText(verbatim: (database.allStops.first!.busStopCode) + \" | \" + (database.allStops.first!.roadName) + \" | \" + \"Nearest Stop\")\n.font(.caption)\n.foregroundStyle(.secondary)\n}\nSpacer()\n}\n.padding(.vertical, 4)\n.padding(.horizontal)\n.contentShape(Rectangle())\n.onTapGesture {\n// show bus arrivals\n}\n} else {\nEmptyView()\n}\n}\n}\n}\n}\n\nEnsuring Legibility\n\nOne of the main concerns with Liquid Glass is legibility. This is even more a of concern when using a tab bar bottom accessory as there is now text where there wasn't any in my previous toolbar-based design.\n\nTo keep things legible, Singapore Buses uses a muted map style. This provides a reasonable balance: the glass effect remains fun to look at without becoming a distraction, even when the map is busy with content.\n\n0:00\n\n/0:08\n\n1×\n\nThe 'Detailed City Experience' is so, well, detailed!\n\nAssuming the tab bar doesn't undergo some wild design changes between now and the iOS 26 release candidate, my intention is to ship the next version of Singapore Buses with a tab bar.\n\nThe next (and final) article in this series will look at the remaining views: Arrivals, Routes, and Settings. I'll post that closer to release.",
"title": "Adopting Liquid Glass, Part IV (Singapore Buses, Revisiting the Tab Bar)"
}