How to display SegmentControl in tabViewBottomAccessory inline mode like iOS 26 Photos app?

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:

  1. Tab bar minimizes to inline mode
  2. A segmented control (Years/Months/All) appears in the center
  3. 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!

Hi @377632523@qq.com,

I'm unable to reproduce the issue, as reported, in my own Xcode project. Because the provided example above will not build successfully due to missing types, I replaced your implementation with the following:

import SwiftUI

struct PhotosMainView: View {
    @State private var searchText: String = ""
 
    var body: some View {
        TabView {
            Tab("Library", systemImage: "photo.on.rectangle") {
                NavigationStack {
                    scrollView(colors: [.red, .orange, .yellow])
                    .navigationTitle("Library")
                }
            }
            Tab("Albums", systemImage: "square.grid.2x2") {
                NavigationStack {
                    scrollView(colors: [.yellow, .green, .teal])
                        .navigationTitle("Albums")
                }
            }
            Tab("Search", systemImage: "magnifyingglass", role: .search) {
                NavigationStack {
                    scrollView(colors: [.blue, .purple, .pink])
                        .navigationTitle("Search")
                        .searchable(text: $searchText)
                }
            }
        }
        .tabBarMinimizeBehavior(.onScrollUp)
        .tabViewStyle(.sidebarAdaptable)
        .tabViewBottomAccessory {
            TimelineAccessoryView()
        }
    }
    
    // Trivial helper view to visualize scrolling.
    @ViewBuilder func scrollView(colors: [Color]) -> some View {
        ScrollView{
            Rectangle()
                .fill(Gradient(colors: colors))
                .frame(height: 2000)
        }
    }
}
 
struct TimelineAccessoryView: View {
    @SceneStorage("selectedTimeline") var selection: Int = 0
    @Environment(\.tabViewBottomAccessoryPlacement) var placement
 
    var body: some View {
        Group {
            if let placement = placement {
                switch placement {
                case .inline:
                    inlineView
                case .expanded:
                    expandedView
                @unknown default:
                    fatalError()
                }
            }
        }
    }
 
    @ViewBuilder
    private var inlineView: some View {
        Picker("View", selection: $selection) {
            Text("Years").tag(0)
            Text("Months").tag(1)
            Text("All").tag(2)
        }
        .pickerStyle(.segmented)
        .frame(maxWidth: 200)
    }
 
    @ViewBuilder
    private var expandedView: some View {
        Text("Expanded state")
    }
}

#Preview {
    PhotosMainView()
}

With the above changes, I was able to see the inline segmented control on an iPhone 16 as well as the iPhone 16 Simulator. If these changes resolve the issue in your project, then I'd suggest refocusing your debugging efforts to the contents of the tabs—perhaps binary searching by abstracting or simplifying child views until you narrow down the API inhibiting the display of the inline segmented control.

However, if the code above does not resolve your issue, please submit a report via Feedback Assistant, including a demo Xcode project, tested iOS and Xcode versions (including build numbers), and reproduction steps. Once submitted, please reply here with the Feedback ID so I can resume my investigation.

Cheers,

Paris X Pinkney |  WWDR | DTS Engineer

How to display SegmentControl in tabViewBottomAccessory inline mode like iOS 26 Photos app?
 
 
Q