Views placed inside tabViewBottomAccessory that access @Environment values or contain @State properties experience unstable identity, as shown by Self._printChanges(). The identity changes on every structural update to the TabView, even though the view's actual identity should remain stable. This causes unnecessary view recreation and breaks SwiftUI's expected identity and lifecycle behavior.
Environment
Xcode Version 26.2 (17C52)
iOS 26.2 simulator and device
struct ContentView: View {
@State var showMoreTabs = true
struct DemoTab: View {
var body: some View { Text(String(describing: type(of: self))) }
}
var body: some View {
TabView {
Tab("Home", systemImage: "house") { DemoTab() }
Tab("Alerts", systemImage: "bell") { DemoTab() }
if showMoreTabs {
TabSection("Categories") {
Tab("Climate", systemImage: "fan") { DemoTab() }
Tab("Lights", systemImage: "lightbulb") { DemoTab() }
}
}
Tab("Settings", systemImage: "gear") {
List {
Toggle("Show more Tabs", isOn: $showMoreTabs)
}
}
}
.tabViewBottomAccessory {
AccessoryView()
}
.task {
while true {
try? await Task.sleep(for: .seconds(5))
if Task.isCancelled { break }
print("toggling showMoreTabs")
showMoreTabs.toggle()
}
}
.tabBarMinimizeBehavior(.onScrollDown)
}
}
struct AccessoryView: View {
var body: some View {
HStack {
EnvironmentAccess()
WithState()
Stateless()
}
}
}
struct EnvironmentAccess: View {
@Environment(\.tabViewBottomAccessoryPlacement) var placement
var body: some View {
// FIXME: EnvironmentAccess: @self, @identity, _placement changed.
// Identity should be stable
let _ = Self._printChanges()
Text(String(describing: type(of: self))) }
}
struct WithState: View {
@State var int = Int.random(in: 0...100)
var body: some View {
// FIXME: WithState: @self, @identity, _id changed.
// Identity should be stable
let _ = Self._printChanges()
Text(String(describing: type(of: self)))
}
}
struct Stateless: View {
var body: some View {
// Works as expected: Stateless: @self changed.
let _ = Self._printChanges()
Text(String(describing: type(of: self)))
}
}
This bug seems to make accessing TabViewBottomAccessoryPlacement impossible without losing view identity; the demo code is affected by the bug. Accessing a value like @Environment(\.colorScheme) is similarly affected, making visually adapting the contents of the accessory to the TabView content without losing identity challenging if not impossible.
Submitted as FB21627918.
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
Seemingly innocuous contents passed to tabViewBottomAccessory can trigger inscrutable AttributeGraph cycles, which can then cause unexplained broken behavior of views that may be participating in these cycles.
These cycles can be introduced by adding common elements to the tabViewBottomAccessory view hierarchy, like Slider, Button, Toggle, and even things simple if statements surrounding Text elements. These cycles can even also be triggered in a manner that causes the tabViewBottomAccessoryPlacement Environment value to be nil, which can then cause views that depend on this value to render incorrectly or not at all.
The errors logged to the Xcode console are of the form:
=== AttributeGraph: cycle detected through attribute 29528 ===
=== AttributeGraph: cycle detected through attribute 324264 ===
No further information about this attribute is available in any public Xcode tools.
Environment
XCode Version 26.0 (17A324)
iOS 26.0 (23A343)
Steps to reproduce
Run the sample above in Simulator
Observe no AttributeGraph cycles in Xcode console.
Uncomment any of the commented out examples in SliderView.body
Observe Xcode console for AttributeGraph cycle messages.
Observe glitchy animation behavior
Expected Behavior
No AttributeGraph cycle diagnostics for ordinary state changes.
tabViewBottomAccessoryPlacement always present (non-nil) while accessory is attached.
Dependent views update consistently.
Errors logged to the Console would help guide me towards a resolution
Impact
Undermines confidence in adopting tabViewBottomAccessory.
Hard to debug: cycle traces are opaque and environment silently degrades (becomes nil) instead of asserting.
Nearly shipped a UI where accessory layout fails sporadically.
What would help
Underlying fix to prevent cycles for ordinary accessory content mutations.
Guarantee (or documented contract) that tabViewBottomAccessoryPlacement is never nil while accessory is active, or an assert if invariants break.
Option to enable detailed environment propagation trace when a cycle is detected.
Symbolic source identifiers in cycle backtraces.
Documentation note on current limitations (if certain view types are not yet supported in accessory regions).
Topic:
UI Frameworks
SubTopic:
SwiftUI