Question
I'm trying to replicate the iOS 26 Photos app behavior where a segmented control appears in the tab bar area when scrolling. Specifically, when the tab bar minimizes to inline mode, I want to show a
Picker
with .segmented
style between the minimized tab button and search button.
Expected Behavior (iOS 26 Photos App)
When scrolling up in the Photos app:
- Tab bar minimizes to inline mode
- A segmented control (Years/Months/All) appears in the center
- Layout:
[Tab Button] [Segmented Control] [Search Button]
Current Implementation
I'm using tabViewBottomAccessory
with tabBarMinimizeBehavior
:
struct PhotosMainView: View {
@Environment(ModelData.self) private var modelData
@State private var searchText: String = ""
var body: some View {
@Bindable var modelData = modelData
TabView {
Tab("Library", systemImage: "photo.on.rectangle") {
NavigationStack {
PhotosGridView()
.navigationTitle("Library")
}
}
Tab("Albums", systemImage: "square.grid.2x2") {
NavigationStack {
AlbumsGridView()
.navigationTitle("Albums")
}
}
Tab("Search", systemImage: "magnifyingglass", role: .search) {
NavigationStack {
PhotosGridView()
.navigationTitle("Search")
.searchable(text: $searchText)
}
}
}
.tabBarMinimizeBehavior(.onScrollUp)
.tabViewBottomAccessory {
TimelineAccessoryView()
.environment(modelData)
}
}
}
struct TimelineAccessoryView: View {
@Environment(ModelData.self) private var modelData
@Environment(\.tabViewBottomAccessoryPlacement) var placement
var body: some View {
Group {
if let placement = placement {
switch placement {
case .inline:
inlineView
case .expanded:
expandedView
}
}
}
}
@ViewBuilder
private var inlineView: some View {
@Bindable var modelData = modelData
Picker("View", selection: $modelData.timelineFilter) {
Text("Years").tag(TimelineFilter.year)
Text("Months").tag(TimelineFilter.month)
Text("All").tag(TimelineFilter.all)
}
.pickerStyle(.segmented)
.frame(maxWidth: 200)
}
@ViewBuilder
private var expandedView: some View {
Text("Expanded state")
}
}
Issues Encountered
1. Console logs show placement changes correctly:
placement: nil → expanded → inline
2. However, the segmented control doesn't appear visually in inline mode
- The accessory view seems to render, but nothing is visible on screen
- Only a small empty space appears where the control should be
3. AttributeGraph cycle warnings appear:
=== AttributeGraph: cycle detected through attribute 27160 ===
=== AttributeGraph: cycle detected through attribute 26304 ===
What I've Tried
1. ✅ Separating inline/expanded views into @ViewBuilder properties to avoid cycles
2. ✅ Using .onAppear and .onChange for debugging instead of direct prints in body
3. ✅ Confirming placement environment value changes correctly
4. ❌ The segmented control still doesn't display in inline mode
Questions
1. Is tabViewBottomAccessory the correct API to achieve this Photos app effect?
2. How should content be structured in .inline placement to display properly between tab button and search?
3. Are there additional modifiers or constraints needed for inline accessories?
4. Is there documentation or sample code showing this pattern?
Environment
- Xcode 17.0
- iOS 26.0
- iPhone 16 Simulator
- SwiftUI
Any guidance on the correct approach to implement this would be greatly appreciated. Thank you!