Xcode Version 26.0 (17A324)
iOS 26.0 (23A343)
Affects both devices (iPhone 16 Pro) and simulator (iPhone 17 Pro)
Here's an example that demonstrates how these AttributeGraph: cycle logs are correlated with broken / unexpected behavior of views involved in the cycle. Clicking on the Button to toggle showOptionA doesn't update which "Slider" is displayed:
import SwiftUI
struct ContentView: View {
var body: some View {
TabView {
Tab("Home", systemImage: "house") { DemoView(prefix: "Home") }
Tab("Alerts", systemImage: "bell") { DemoView(prefix: "Alerts") }
TabSection("Categories") {
Tab("Climate", systemImage: "fan") { DemoView(prefix: "Climate") }
Tab("Lights", systemImage: "lightbulb") { DemoView(prefix: "Lights") }
}
}
.tabViewBottomAccessory { MusicPlaybackView() }
.tabBarMinimizeBehavior(.onScrollDown)
}
}
struct DemoView: View {
let prefix: String
var body: some View {
List(0..<50, id: \.self) { i in
Text("\(prefix) Item #\(i + 1)")
}
}
}
struct MusicPlaybackView: View {
@Environment(\.tabViewBottomAccessoryPlacement) var placement
var body: some View {
if placement == .inline { ControlsPlaybackView() } else { SliderPlaybackView() }
}
}
struct ControlsPlaybackView: View {
var body: some View { Text("Controls") }
}
struct SliderPlaybackView: View {
@State private var showOptionA = false
var body: some View {
HStack {
if showOptionA { Text("Slider A") } else { Text("Slider B") }
Button {
showOptionA.toggle()
} label: {
if showOptionA { Text("Option A") } else { Text("Option B") }
}
}
.animation(.default, value: showOptionA)
}
}
Expected
Tapping the button toggles showOptionA and immediately swaps the displayed "Slider".
No AttributeGraph cycle warnings.
Actual
The label fails to update.
These errors are logged in the console:
=== AttributeGraph: cycle detected through attribute 126800 ===
=== AttributeGraph: cycle detected through attribute 29376 ===
=== AttributeGraph: cycle detected through attribute 126800 ===
=== AttributeGraph: cycle detected through attribute 126800 ===
=== AttributeGraph: cycle detected through attribute 126800 ===
=== AttributeGraph: cycle detected through attribute 32488 ===
=== AttributeGraph: cycle detected through attribute 29376 ===
=== AttributeGraph: cycle detected through attribute 32488 ===
=== AttributeGraph: cycle detected through attribute 29376 ===
=== AttributeGraph: cycle detected through attribute 30024 ===
=== AttributeGraph: cycle detected through attribute 29376 ===
=== AttributeGraph: cycle detected through attribute 32488 ===
=== AttributeGraph: cycle detected through attribute 29376 ===
=== AttributeGraph: cycle detected through attribute 30024 ===
=== AttributeGraph: cycle detected through attribute 29376 ===
=== AttributeGraph: cycle detected through attribute 30024 ===
=== AttributeGraph: cycle detected through attribute 29376 ===
=== AttributeGraph: cycle detected through attribute 32488 ===
=== AttributeGraph: cycle detected through attribute 29376 ===
=== AttributeGraph: cycle detected through attribute 32488 ===
The "Slider" is a placeholder Text element because any use of Slider seems to trigger the same warnings.
The same SliderPlaybackView works fine in an .overlay().
Simplifying SliderPlaybackView in this manner seems to avoid the cycles in the tabViewBottomAccessory:
struct SliderPlaybackView: View {
@State private var showOptionA: Bool = false
var body: some View {
HStack {
Text(showOptionA ? "Slider A" : "Slider B")
Text(showOptionA ? "Option A" : "Option B")
.onTapGesture {
showOptionA.toggle()
}
}
.animation(.default, value: showOptionA)
}
}
The seemingly limited functionality available in tabViewBottomAccessory's Content closure requires a lot of trial and error to discover and significantly increases the level of effort required to adopt this new API.