Hi,
Toggling a SwiftUI menu in iOS 26 significantly reduces the framerate of an underlying SKView or ARView.
Below are test cases for SpriteKit and RealityKit. I ran these tests on iOS 26.1 Beta using an iPhone 13 (A15 chip). Results were similar on iOS 26.0.1.
Both scenes consist of circles and balls bouncing on the ground. The restitution of the physics bodies is set for near-perfect elasticity, so they keep bouncing indefinitely.
In both SKView and ARView, the framerate drops significantly whenever the SwiftUI menu is toggled. The menu itself is simple and uses standard SwiftUI animations and styling.
SpriteKit
import SpriteKit
import SwiftUI
class SKRestitutionScene: SKScene {
override func didMove(to view: SKView) {
view.contentMode = .center
size = view.bounds.size
scaleMode = .resizeFill
backgroundColor = .darkGray
anchorPoint = CGPoint(x: 0.5, y: 0.5)
let groundWidth: CGFloat = 300
let ground = SKSpriteNode(color: .gray, size: CGSize(width: groundWidth, height: 10))
ground.physicsBody = SKPhysicsBody(rectangleOf: ground.size)
ground.physicsBody?.isDynamic = false
addChild(ground)
let circleCount = 5
let spacing: CGFloat = 60
let totalWidth = CGFloat(circleCount - 1) * spacing
let startX = -totalWidth / 2
for i in 0..<circleCount {
let circle = SKShapeNode(circleOfRadius: 18)
circle.fillColor = .systemOrange
circle.lineWidth = 0
circle.physicsBody = SKPhysicsBody(circleOfRadius: 18)
circle.physicsBody?.restitution = 1
circle.physicsBody?.linearDamping = 0
let x = startX + CGFloat(i) * spacing
circle.position = CGPoint(x: x, y: 150)
addChild(circle)
}
}
override func willMove(from view: SKView) {
self.removeAllChildren()
}
}
struct SKRestitutionView: View {
var body: some View {
ZStack {
SpriteView(scene: SKRestitutionScene(), preferredFramesPerSecond: 120)
.ignoresSafeArea()
VStack {
Spacer()
Menu {
Button("Edit", systemImage: "pencil") {}
Button("Share", systemImage: "square.and.arrow.up") {}
Button("Delete", systemImage: "trash") {}
} label: {
Text("Menu")
}
.buttonStyle(.glass)
}
.padding()
}
}
}
#Preview {
SKRestitutionView()
}
RealityKit
import RealityKit
import SwiftUI
struct ARViewPhysicsRestitution: UIViewRepresentable {
let arView = ARView()
func makeUIView(context: Context) -> some ARView {
arView.contentMode = .center
arView.cameraMode = .nonAR
arView.automaticallyConfigureSession = false
arView.environment.background = .color(.gray)
// MARK: Root
let anchor = AnchorEntity()
arView.scene.addAnchor(anchor)
// MARK: Camera
let camera = Entity()
camera.components.set(PerspectiveCameraComponent())
camera.position = [0, 1, 4]
camera.look(at: .zero, from: camera.position, relativeTo: nil)
anchor.addChild(camera)
// MARK: Ground
let groundWidth: Float = 3.0
let ground = Entity()
let groundMesh = MeshResource.generateBox(width: groundWidth, height: 0.1, depth: groundWidth)
let groundModel = ModelComponent(mesh: groundMesh, materials: [SimpleMaterial(color: .white, roughness: 1, isMetallic: false)])
ground.components.set(groundModel)
let groundShape = ShapeResource.generateBox(width: groundWidth, height: 0.1, depth: groundWidth)
let groundCollision = CollisionComponent(shapes: [groundShape])
ground.components.set(groundCollision)
let groundPhysicsBody = PhysicsBodyComponent(
material: PhysicsMaterialResource.generate(friction: 0, restitution: 0.97),
mode: .static
)
ground.components.set(groundPhysicsBody)
anchor.addChild(ground)
// MARK: Balls
let ballCount = 5
let spacing: Float = 0.4
let totalWidth = Float(ballCount - 1) * spacing
let startX = -totalWidth / 2
let radius: Float = 0.12
let ballMesh = MeshResource.generateSphere(radius: radius)
let ballMaterial = SimpleMaterial(color: .systemOrange, roughness: 1, isMetallic: false)
let ballShape = ShapeResource.generateSphere(radius: radius)
for i in 0..<ballCount {
let ball = Entity()
let ballModel = ModelComponent(mesh: ballMesh, materials: [ballMaterial])
ball.components.set(ballModel)
let ballCollision = CollisionComponent(shapes: [ballShape])
ball.components.set(ballCollision)
var ballPhysicsBody = PhysicsBodyComponent(
material: PhysicsMaterialResource.generate(friction: 0, restitution: 0.97), /// 0.97 for near perfect elasticity
mode: .dynamic
)
ballPhysicsBody.linearDamping = 0
ballPhysicsBody.angularDamping = 0
ball.components.set(ballPhysicsBody)
let shadow = GroundingShadowComponent(castsShadow: true)
ball.components.set(shadow)
let x = startX + Float(i) * spacing
ball.position = [x, 1, 0]
anchor.addChild(ball)
}
return arView
}
func updateUIView(_ uiView: UIViewType, context: Context) {
}
}
struct PhysicsRestitutionView: View {
var body: some View {
ZStack {
ARViewPhysicsRestitution()
.ignoresSafeArea()
.background(.black)
VStack {
Spacer()
Menu {
Button("Edit", systemImage: "pencil") {}
Button("Share", systemImage: "square.and.arrow.up") {}
Button("Delete", systemImage: "trash") {}
} label: {
Text("Menu")
}
.buttonStyle(.glass)
}
.padding()
}
}
}
#Preview {
PhysicsRestitutionView()
}