Repository navigation
Move the iOS tab bar to the side rail on iPhone Duo - #275
Merged
Merged
Conversation
On iOS 26+ the app root is now a real UITabBarController with empty tabs and the Compose view controller on top. Only the bar of a UITabBarController follows the system placement, so it moves to the side rail on iPhone Duo and stays at the bottom elsewhere. Navigation stays in Compose. A vertical bar reports a height of 0, so screens don't pad for it. The custom Large Content Viewer is removed, UITabBarController provides it. Fixes #274 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
UITabBarController treats every child view controller as a tab, so the Compose view controller became an empty third tab. Selecting it passed index 2 to Compose, which only has two tabs, and crashed. Declare the tabs with UITab instead, so the Compose view controller stays a child without becoming a tab, and map selection by tab identifier. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
The side rail still falls back to system blue because the accent tint is applied only to the bottom tab bar, not the controller’s root view.
1 open issue
What changed in this PR
Moves iOS 26+ navigation into a native UITabBarController so UIKit can place tabs on the iPhone Duo side rail while navigation remains managed by Compose.
Changes:
- Wraps the Compose controller with native tab management and content-layer handling.
- Updates tab selection, visibility, and bottom spacing for adaptive placement.
- Removes the custom Large Content Viewer in favor of UIKit’s built-in support; retains Material navigation on older iOS versions and Android.
| File | Description |
|---|---|
shared/src/iosMain/kotlin/main.kt |
Wraps the Compose entry point in the native tab controller. |
core/ui/src/iosMain/kotlin/nl/q42/template/core/ui/compose/composables/navigation/TabBarLargeContentViewer.kt |
Removes custom Large Content Viewer support. |
core/ui/src/iosMain/kotlin/nl/q42/template/core/ui/compose/composables/navigation/NativeTabBarController.kt |
Adds adaptive native tabs while preserving Compose content. |
core/ui/src/iosMain/kotlin/nl/q42/template/core/ui/compose/composables/navigation/NativeTabBar.kt |
Connects Compose state to native tabs and reports bottom spacing. |
core/ui/src/iosMain/kotlin/nl/q42/template/core/ui/compose/composables/navigation/AppNavigationBar.ios.kt |
Uses the native controller when available, otherwise Material. |
core/ui/src/commonMain/kotlin/nl/q42/template/core/ui/compose/composables/navigation/AppNavigationBar.kt |
Documents adaptive placement and side-rail height reporting. |
Review effort: Balanced
The side rail is not part of controller.tabBar, so it fell back to system blue. Set the tint on the controller's view so both placements inherit it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
mbernson
requested changes
Nov 2, 2026
mbernson
left a comment
Member
There was a problem hiding this comment.
One change request for the tab bar delegate. Looks good other than that :)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
AnneKlapwijk
approved these changes
Nov 3, 2026
AnneKlapwijk
left a comment
Member
There was a problem hiding this comment.
Code looks clear. Tested https://github.com/Q42/GoingsOn.CMP/pull/47 on iPhone Duo simulator. Looks good 👍
mbernson
approved these changes
Nov 4, 2026
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Fixes #274
Why is this important?
On iPhone Duo the system puts tab bars in a vertical rail on the side of the screen, to leave more height for content. Only the inner display in portrait keeps the tab bar at the bottom:
video
rotation.mp4
poses
The issue described the open state as "bottom" without mentioning orientation. In landscape the open Duo uses the side rail too. Sources: the HIG on vertical controls, and Apple's iPhone Duo developer forums summary, which says the inner display in portrait "maintain[s] standard horizontal bars", while in landscape and Split View the "controls move to the side edge".
I also tested this myself using the iphone duo at the rotterdam office
The system decides the placement, so the app needs no code per pose. But our Liquid Glass tab bar was a standalone
UITabBar, which always stays at the bottom. Only the bar of aUITabBarControllerfollows the system placement.Notes
UITabBarController(NativeTabBarController) with empty tabs and the Compose view controller on top. Navigation stays in Compose;NativeTabBaronly sets the tabs and the selected tab.UITabAPI, notsetViewControllers: withsetViewControllersevery child view controller is a tab, so Compose became an empty third tab, and selecting it crashed the app.setTabsdetaches it once, so it is re-attached and told it appeared again.UITabBarControllerprovides it.Tested in the simulator (iphone duo + iphone 17) and real iphone duo:
UITabchange): both the Compose content and the tabs are reachable, on both devices.🤖 Generated with Claude Code