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()
}
Explore the various UI frameworks available for building app interfaces. Discuss the use cases for different frameworks, share best practices, and get help with specific framework-related questions.
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Created
I'm using a ZStack with viewA() and a toast viewB(). I present a FullScreenView() using .fullScreenCover(isPresented:).
ZStack {
viewA()
viewB() // toast view
}
.fullScreenCover(isPresented: $isPresented) {
FullScreenView()
}
I want viewB (the toast) to appear above both viewA and the FullScreenView.
I only found this approach works but it has duplicate code.
ZStack {
viewA()
viewB() // toast view
}
.fullScreenCover(isPresented: $isPresented) {
ZStack {
FullScreenModalView()
viewB() // toast view
}
}
What are all possible approaches to achieve this in SwiftUI?
Any advice or code samples would be appreciated!
Topic:
UI Frameworks
SubTopic:
SwiftUI
Hi, I have started app development fairly recently and I decided to develop for Apple due to the fact that my school uses iPads and they were so convincing that I decided to buy more Apple products adding to the experience with the iPad such as a Mac where I learned Xcode, Swift and SwiftUI as far as I could comprehend.
I am working on a document based app for my school and I want to implement a toolbar that’s user customizable. There seems to be no way to get a customizable toolbar running on iPadOS26 though iPadOS16 has supported this feature and it has been till 18, I believe. I have set up a doc-based app in Xcode and that and all of its views do work except the toolbar that I added to my NavigationSplitView. The items show, when their defaultCustomization isn’t set to .hidden. The toolbar is given an id and the items are all wrapped in the ToolbarItem structure that also is given an id and the placement of .secondaryAction. The items show up and behave as expected in terms of grouping and making space if there isn’t enough room for all items so some appear in the overflow menu. From apps like Preview, Reminders and MindNode I know that the customization is supposed to still be available but it lives in the menu bar, where I haven’t seen it under view. It seems like there is a thing that my toolbar does not conform to or that there is a method that‘s called, when the user wants to customize the toolbar that I don’t know about. I would much appreciate a quick fix, solution or suggestion.
Hello,
I’ve created a custom SDK from my iOS application to be integrated into another app. The SDK depends on Google Maps and payment gateway libraries.
If I exclude these third-party libraries from the SDK, I encounter multiple errors. However, if I include them, the host app throws errors due to duplicate dependencies, since it already integrates the same libraries.
I understand that third-party dependencies can be downloaded separately by adding them through Swift Package Manager (SPM). However, the issue is that if I exclude these dependencies from my SDK, I get compilation errors wherever I’ve used import GoogleMaps or similar statements in my code.
Could you please guide me — or share documentation — on the correct approach to create an SDK that excludes third-party libraries?
Hi everyone,
I'm encountering an unexpected behavior with modal presentations in UIKit. Here’s what happens:
I have UIViewControllerA (let’s call it the "orange" VC) pushed onto a UINavigationController stack.
I present UIViewControllerB (the "red" VC, inside its own UINavigationController as a .formSheet) modally over UIViewControllerA.
After a short delay, I pop UIViewControllerA from the navigation stack.
Issue:
After popping UIViewControllerA, the modal UIViewControllerB remains visible on the screen and in memory. I expected that dismissing (popping) the presenting view controller would also dismiss the modal, but it stays.
Expected Behavior:
When UIViewControllerA (orange) is popped, I expect the modal UIViewControllerB (red) to be dismissed as well.
Actual Behavior:
The modal UIViewControllerB remains on screen and is not dismissed, even though its presenting view controller has been removed from the navigation stack.
Video example: https://youtube.com/shorts/sttbd6p_r_c
Question:
Is this the expected behavior? If so, what is the recommended way to ensure that the modal is dismissed when its presenting view controller is removed from the navigation stack?
Code snippet:
class MainVC: UIViewController {
private weak var orangeVC: UIViewController?
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = .blue
let dq = DispatchQueue.main
dq.asyncAfter(deadline: .now() + 1) { [weak self] in
let vc1 = UIViewController()
vc1.view.backgroundColor = .orange
vc1.modalPresentationStyle = .overCurrentContext
self?.navigationController?.pushViewController(vc1, animated: true)
self?.orangeVC = vc1
dq.asyncAfter(deadline: .now() + 1) { [weak self] in
let vc2 = UIViewController()
vc2.view.backgroundColor = .red
vc2.modalPresentationStyle = .formSheet
vc2.isModalInPresentation = true
let nav = UINavigationController(rootViewController: vc2)
if let sheet = nav.sheetPresentationController {
sheet.detents = [.medium()]
}
self?.orangeVC?.present(nav, animated: true)
dq.asyncAfter(deadline: .now() + 1) { [weak self] in
self?.navigationController?.popViewController(animated: true)
}
}
}
}
}
Thank you for your help!
If the cell is expandable in List View, it will show slowly after deletion swipe.
the image shows how it looks like when this issue happens
and it should be easy to reproduce with this sample codes
// Model for list items
struct ListItem: Identifiable {
let id = UUID()
let title: String
let createdDate: Date
let description: String
}
struct ExpandableListView: View {
// Sample constant data
private let items: [ListItem] = [
ListItem(
title: "First Item",
createdDate: Date().addingTimeInterval(-86400 * 5),
description: "This is a detailed description for the first item. It contains two lines of text to show when expanded."
),
ListItem(
title: "Second Item",
createdDate: Date().addingTimeInterval(-86400 * 3),
description: "This is a detailed description for the second item. It provides additional information about this entry."
),
ListItem(
title: "Third Item",
createdDate: Date().addingTimeInterval(-86400 * 1),
description: "This is a detailed description for the third item. Tap to expand and see more details here."
),
ListItem(
title: "Fourth Item",
createdDate: Date(),
description: "This is a detailed description for the fourth item. It shows comprehensive information when tapped."
)
]
// Track which item is currently selected/expanded
@State private var selectedItemId: UUID? = nil
var body: some View {
NavigationView {
List {
ForEach(items) { item in
ExpandableCell(
item: item,
isExpanded: selectedItemId == item.id
) {
// Handle tap - toggle selection
withAnimation(.easeInOut(duration: 0.3)) {
if selectedItemId == item.id {
selectedItemId = nil
} else {
selectedItemId = item.id
}
}
}
}
.onDelete { indexSet in
for index in indexSet {
print("delete \(items[index].title)")
}
}
}
.navigationTitle("Items")
}
}
}
struct ExpandableCell: View {
let item: ListItem
let isExpanded: Bool
let onTap: () -> Void
private var dateFormatter: DateFormatter {
let formatter = DateFormatter()
formatter.dateStyle = .medium
formatter.timeStyle = .short
return formatter
}
var body: some View {
VStack(alignment: .leading, spacing: 8) {
// Always visible: Title and Date
HStack {
VStack(alignment: .leading, spacing: 4) {
Text(item.title)
.font(.headline)
.foregroundColor(.primary)
Text(dateFormatter.string(from: item.createdDate))
.font(.caption)
.foregroundColor(.secondary)
}
Spacer()
// Chevron indicator
Image(systemName: isExpanded ? "chevron.up" : "chevron.down")
.foregroundColor(.secondary)
.font(.caption)
}
// Expandable: Description (2 lines)
if isExpanded {
Text(item.description)
.font(.body)
.foregroundColor(.secondary)
.lineLimit(2)
.fixedSize(horizontal: false, vertical: true)
.transition(.opacity.combined(with: .move(edge: .top)))
.padding(.top, 4)
}
}
.padding(.vertical, 8)
.contentShape(Rectangle())
.onTapGesture {
onTap()
}
}
}
#Preview {
ExpandableListView()
}
It seems to me that NSStagedMigrationManager has algorithmic issues. It doesn't perform staged migration, if all its stages are NSLightweightMigrationStage.
You can try it yourself. There is a test project with three model versions V1, V2, V3, V4. Migrating V1->V2 is compatible with lightweight migration, V2->V3, V3->V4 is also compatible, but V1->V3 is not. I have following output:
Migrating V1->V2, error: nil
Migrating V2->V3, error: nil
Migrating V3->V4, error: nil
Migrating V1->V3, no manager, error: Optional("Persistent store migration failed, missing mapping model.")
Migrating V1->V3, lightweight[1, 2, 3], error: Optional("Persistent store migration failed, missing mapping model.")
Migrating V1->V3, lightweight[1]->lightweight[2]->lightweight[3], error: Optional("Persistent store migration failed, missing mapping model.")
Migrating V1->V3, custom[1->2]->lightweight[3], error: nil
Migrating V1->V3, lightweight[1]->custom[2->3], error: nil
Migrating V1->V3, custom[1->2]->custom[2->3], error: nil
Migrating V1->V4, error: Optional("Persistent store migration failed, missing mapping model.")
Migrating V2->V4, error: nil
Migrating V1->V4, custom[1->2]->lightweight[3, 4], error: nil
Migrating V1->V4, lightweight[3, 4]->custom[1->2], error: Optional("A store file cannot be migrated backwards with staged migration.")
Migrating V1->V4, lightweight[1, 2]->lightweight[3, 4], error: Optional("Persistent store migration failed, missing mapping model.")
Migrating V1->V4, lightweight[1]->custom[2->3]->lightweight[4], error: nil
Migrating V1->V4, lightweight[1,4]->custom[2->3], error: nil
Migrating V1->V4, custom[2->3]->lightweight[1,4], error: Optional("Persistent store migration failed, missing mapping model.")
I think that staged migration should satisfy the following rules for two consecutive stages:
Any version of lightweight stage to any version of lightweight stage;
Any version of lightweight stage to current version of custom stage;
Next version of custom stage to any version of lightweight stage;
Next version of custom stage to current version of custom stage.
However, rule 1 doesn't work, because migration manager skips intermediate versions if they are inside lightweight stages, even different ones.
Note that lightweight[3, 4]->custom[1->2] doesn't work, lightweight[1,4]->custom[2->3] works, but custom[2->3]->lightweight[1,4] doesn't work again.
Would like to hear your opinion on that, especially, from Core Data team, if possible.
Thanks!
We are having an issue with our app after upgrading to WatchOS 26. After some time our complication disappears from the watch face. If you go to add it back, our app shows up with an empty icon placeholder and you are unable to tap it to add it back. Sometimes restarting the watch will bring it back, sometimes it does not. Has anyone experienced this? What should we be looking at to figure out why this is happening? Or could this be a bug in WatchOS 26?
I’m building a macOS document based app using SwiftUI’s DocumentGroup API. By default, when a document based app launches, macOS automatically shows a file open panel or creates a new untitled document window.
However, I want to suppress this default behavior and instead show a custom welcome window when the app starts — something similar to how Xcode or Final Cut Pro shows a “Welcome” or “Start Project” screen first.
So basically, when the user opens the app normally, it should not open the document selector or create a document automatically. Instead, it should show my custom SwiftUI or AppKit window. Here is my Code :-
//MyApp.swift
import SwiftUI
import AppKit
@main
struct PhiaApp: App {
@NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
DocumentGroup(newDocument: MyDocumentModel()) { file in
EditorView(document: file.document, filePath: file.fileURL)
}
Settings { EmptyView() }
}
}
Current I have this code setup for my MainApp.swift, where I am using the AppDelegate to create a custom recording window using appkit and also defining the DocumentGroup to handle the custom .myapp file opens. However, when I launch the app, its showing my appkit window as well as the macOs native file Selector to select the file I want to open.
I want when the user opens the app normally, it should not open the document selector or create a document automatically. Instead, it should show my custom SwiftUI or AppKit window. However, the app should still fully support opening .myapp documents by double clicking from Finder, using the standard File → Open and File → New menu options, also having multiple document windows open at once.
This is my AppDelegate.swift file :-
import AppKit
import SwiftUI
class AppDelegate: NSObject, NSApplicationDelegate {
var panel: Panel?
private var statusItem: NSStatusItem?
func applicationDidFinishLaunching(_ notification: Notification) {
showWindow()
}
// MARK: - Window control
func showWindow() {
if panel == nil {
let root = RecordingViewMain()
let newPanel = Panel(rootView: root)
if let screen = NSScreen.main {
let size = NSSize(width: 360, height: 240)
let origin = NSPoint(
x: screen.visibleFrame.midX - size.width / 2,
y: screen.visibleFrame.midY - size.height / 2
)
newPanel.setFrame(NSRect(origin: origin, size: size), display: true)
}
panel = newPanel
}
panel?.makeKeyAndOrderFront(nil)
}
func hideWindow() {
panel?.orderOut(nil)
}
@objc private func showPanelAction() {
showWindow()
}
@objc private func quitAction() {
NSApp.terminate(nil)
}
}
My app has the following UI layout:
NSSplitViewController as the windows contentViewController
NSPageController in the content (right) split item
NSTabViewController as the root items of the NSPageController
NSViewController with a collection view in the first tab of that NSTabViewController
The collection view is using a NSCollectionViewCompositionalLayout in which the sections are set up to have a header using NSCollectionLayoutBoundarySupplementaryItem with pinToVisibleBounds=true and alignment=top
With macOS 26, the pinned supplementary item automatically gets a blurred/semi-transparent background that seamlessly integrates with the toolbar. When the window's title bar has a NSTitlebarAccessoryViewController added, the said semi-transparent background gets a bottom hard edge and a hairline to provide more visual separation from the main content.
During runtime, my NSPageController transitions from the NSTabViewController to another view controller. When transitioning back, the semi-transparent blur bleeds into the entire section. This happens no matter if there's a NSTitlebarAccessoryViewController added or not.
It doesn't happen 100% of the cases, it seems to depend on section size, header visibility and/or scroll position. But it happens more often than not.
Most of the time, a second or so after the back transition - shortly after pageControllerDidEndLiveTransition: of the NSPageControllerDelegate is called - the view updates and the supplementary views are back to normal.
Sometimes, the issue also appears not when transitioning using NSPageController, but simply by scrolling through the collection view.
Anyone has an idea what is happening here? Below are two screenshots of both the "ok" and "not ok" state
I'm on macOS 26.0.1 and I'm using XCode 26.0.1
Good day!
Have anyone experienced sudden crashes increase cause by CALayerInvalidGeometry · CALayer position contains NaN: [nan 5] that gets set via -[AVMobileGlassVolumeControlsView layoutSubviews]?
94% of crashes belong to iOS 26.0.1, while rest to 26.0. What's weird, though, is that it is caused by some AVKit internal logic and cant' understand how to track it down, neither how to reproduce.
Stack trace looks as follows:
0 CoreFoundation +0xc98c4 ___exceptionPreprocess
1 libobjc.A.dylib +0x317c0 _objc_exception_throw
2 CoreFoundation +0x1548d0 +[NSException raise:format:]
3 QuartzCore +0x909b4 CA::Layer::set_position(CA::Vec2<double> const&, bool)
4 QuartzCore +0x2af294 -[CALayer setPosition:]
5 UIKitCore +0xe9fc18 -[UIView _backing_setPosition:]
6 UIKitCore +0x18f6158 -[UIView setCenter:]
7 UIKitCore +0x1eb704 0x188b5e704 (0x188b5e5f4 + 272)
8 UIKitCore +0x1eb56c 0x188b5e56c (0x188b5e4f0 + 124)
9 UIKitCore +0x18fd318 -[UIView(Geometry) _resizeWithOldSuperviewSize:]
10 CoreFoundation +0x1514c ___NSARRAY_IS_CALLING_OUT_TO_A_BLOCK__
11 CoreFoundation +0xdd18c -[__NSArrayM enumerateObjectsWithOptions:usingBlock:]
12 UIKitCore +0x18fc6dc -[UIView(Geometry) resizeSubviewsWithOldSize:]
13 UIKitCore +0x18fabd0 -[UIView(Geometry) setFrame:]
14 AVKit +0x1b5a88 -[AVMobileGlassVolumeControlsView layoutSubviews]
15 UIKitCore +0x27074 0x18899a074 (0x188999d3c + 824)
16 UIKitCore +0x27b34 0x18899ab34 (0x18899ab14 + 32)
17 UIKitCore +0x190df64 -[UIView(CALayerDelegate) layoutSublayersOfLayer:]
18 QuartzCore +0xac9a4 CA::Layer::perform_update_(CA::Layer*, CALayer*, unsigned int, CA::Transaction*)
19 QuartzCore +0x8f2f8 CA::Layer::update_if_needed(CA::Transaction*, unsigned int, unsigned int)
20 UIKitCore +0x57b0 -[UIView(Hierarchy) layoutBelowIfNeeded]
21 AVKit +0x1b6634 ___74-[AVMobileGlassVolumeControlsView _updateVolumeFluidSliderEmphasizedScale]_block_invoke
22 UIKitCore +0x1b3e58 -[UIViewPropertyAnimator _runAnimations]
23 UIKitCore +0x19025f0 +[UIView(Animation) _performWithState:trackingIdentifier:duration:delay:animations:]
24 UIKitCore +0x83a650 ___49-[UIViewPropertyAnimator startAnimationAsPaused:]_block_invoke_4
25 UIKitCore +0x83a460 ___49-[UIViewPropertyAnimator startAnimationAsPaused:]_block_invoke
26 UIKitCore +0x83a210 -[UIViewPropertyAnimator startAnimationAsPaused:]
27 AVKit +0x1b6574 -[AVMobileGlassVolumeControlsView setEmphasized:]
28 AVKit +0x127510 ___64-[AVMobileGlassControlsViewController _animateVolumeEmphasisTo:]_block_invoke
29 UIKitCore +0x1b3e58 -[UIViewPropertyAnimator _runAnimations]
30 UIKitCore +0x83a590 ___49-[UIViewPropertyAnimator startAnimationAsPaused:]_block_invoke_3
31 UIKitCore +0x83a460 ___49-[UIViewPropertyAnimator startAnimationAsPaused:]_block_invoke
32 UIKitCore +0x83a514 ___49-[UIViewPropertyAnimator startAnimationAsPaused:]_block_invoke_2
33 UIKitCore +0x839e60 -[UIViewPropertyAnimator _setupAnimationTracking:]
34 UIKitCore +0x83a260 -[UIViewPropertyAnimator startAnimationAsPaused:]
35 AVKit +0x127480 -[AVMobileGlassControlsViewController _animateVolumeEmphasisTo:]
36 AVKit +0x12ea2c ___56-[AVMobileGlassControlsViewController _observationSetup]_block_invoke_5
37 AVKit +0xcbcd0 ___106-[AVObservationController startObservingNotificationForName:object:notificationCenter:observationHandler:]_block_invoke
38 CoreFoundation +0x519ec ___CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__
39 CoreFoundation +0x51ab0 ____CFXRegistrationPost_block_invoke
40 CoreFoundation +0x5190c __CFXRegistrationPost
41 CoreFoundation +0x520ac __CFXNotificationPost
42 Foundation +0x94fc2c -[NSNotificationCenter postNotificationName:object:userInfo:]
43 AVKit +0x1b9bd8 -[AVSystemVolumeController _postNotificationForNameIfFullyInitialized:userInfo:]
44 AVKit +0x1b9dc0 -[AVSystemVolumeController setVolume:shouldShowHUD:]
45 AVKit +0x1b965c ___69-[AVSystemVolumeController _handleSystemVolumeDidChangeNotification:]_block_invoke
46 AVKit +0x1b8da4 -[AVSystemVolumeController _performOnMainThread:]
47 AVKit +0x1b95c0 -[AVSystemVolumeController _handleSystemVolumeDidChangeNotification:]
48 CoreFoundation +0x51a00 ___CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__
49 CoreFoundation +0x51ab0 ____CFXRegistrationPost_block_invoke
50 CoreFoundation +0x5190c __CFXRegistrationPost
51 CoreFoundation +0x520ac __CFXNotificationPost
52 Foundation +0x94fc2c -[NSNotificationCenter postNotificationName:object:userInfo:]
53 MediaExperience +0x1039b0 ___76+[AVSystemControllerCommon postNotificationOnMainQueue:notification:object:]_block_invoke
54 MediaExperience +0x6b64 ___MXDispatchAsync_block_invoke
55 libdispatch.dylib +0x1ad8 __dispatch_call_block_and_release
56 libdispatch.dylib +0x1b7e8 __dispatch_client_callout
57 libdispatch.dylib +0x38b20 __dispatch_main_queue_drain.cold.5
58 libdispatch.dylib +0x10ec4 __dispatch_main_queue_drain
59 libdispatch.dylib +0x10e00 __dispatch_main_queue_callback_4CF
60 CoreFoundation +0x6b51c ___CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__
61 CoreFoundation +0x1dd10 ___CFRunLoopRun
62 CoreFoundation +0x1cc40 __CFRunLoopRunSpecificWithOptions
63 GraphicsServices +0x1494 _GSEventRunModal
64 UIKitCore +0xa9dd8 -[UIApplication _run]
65 UIKitCore +0x4eb08 _UIApplicationMain
66 TuneIn Radio +0x1b318 main (main.m:28:22)
67 dyld +0x4e24 start
our app support silent push, and we use below code to get if app is launched by silent push:
if let remoteNotification = launchOptions?[UIApplication.LaunchOptionsKey.remoteNotification] as? [AnyHashable: Any],
let aps = remoteNotification["aps"] as? [AnyHashable: Any],
let contentAvailable = aps["content-available"] as? Int,
contentAvailable == 1 {
isSilentNotification = true
}
when app is launch and call:
application(_:didFinishLaunchingWithOptions:)
but when migrate to UIScene, the launchOptions is always nil, and we can not get to know if app is launched by silent push;
I read the develop doc:
it says:
If the app supports scenes, this is nil.
Continue using UIApplicationDelegate's application(_:didReceiveRemoteNotification:fetchCompletionHandler:) to process silent remote notifications after scene connection.
but the time for method calling
application(_:didReceiveRemoteNotification:fetchCompletionHandler:)
id too late.
So except in didReceiveRemoteNotification method calling:
application(_:didReceiveRemoteNotification:fetchCompletionHandler:)
are there any other ways to obtain the silent push flag when app is launch and has not didReceiveRemoteNotification call back.
I'm trying to create a UI with two button bars (top and bottom) inside the detail view of a NavigationSplitView, instead of using the built-in .toolbar() modifier. I'm using .ignoresSafeArea(.container, edges: .vertical) so the detail view can reach into that area.
However, in macOS and iOS 26 the top button is not clickable/tappable because it is behind an invisible View created by the non-existent toolbar. Interestingly enough, if I apply .buttonStyle(.borderless) to the top button it becomes clickable (in macOS).
On the iPad the behavior is different depending on the iPad OS version. In iOS 26, the button is tappable only by the bottom half. In iOS 18 the button is always tappable.
Here's the code for the screenshot:
import SwiftUI
struct ContentView2: View {
@State private var sidebarSelection: String?
@State private var contentSelection: String?
@State private var showContentColumn = true
@State private var showBars = true
var body: some View {
NavigationSplitView {
// Sidebar
List(selection: $sidebarSelection) {
Text("Show Content Column").tag("three")
Text("Hide Content Column").tag("two")
}
.navigationTitle("Sidebar")
} detail: {
VStack(spacing: 0) {
if showBars {
HStack {
Button("Click Me") {
withAnimation {
showBars.toggle()
}
}
.buttonStyle(.borderedProminent)
}
.frame(maxWidth: .infinity, minHeight: 50, idealHeight: 50, maxHeight: 50)
.background(.gray)
.transition(.move(edge: .top))
}
ZStack {
Text("Detail View")
}
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .init(horizontal: .center, vertical: .center))
.border(.red)
.onTapGesture(count: 2) {
withAnimation {
showBars.toggle()
}
}
if showBars {
HStack {
Button("Click Me") {
withAnimation {
showBars.toggle()
}
}
}
.frame(maxWidth: .infinity, minHeight: 50, idealHeight: 50, maxHeight: 50)
.background(.gray)
.transition(.move(edge: .bottom))
}
}
.ignoresSafeArea(.container, edges: .vertical)
.toolbarVisibility(.hidden)
}
.toolbarVisibility(.visible)
}
}
I'm confused by this very inconsistent behavior and I haven't been able to find a way to get this UI to work across both platforms.
Does anybody know how to remove the transparent toolbar that is preventing clicks/taps in this top section of the view? I'm hoping there's an idiomatic, native SwiftUI way to do it.
I have this MWE right here -- it has a toolbar with a random action on it, in addition to a scroll view as the content of the window, with random labels attached inside. Since the redeisgn of the NSToolbar stuff in Tahoe, I expect the share button be able to "float" on top of the scrolled out content as shown as the first image at https://developer.apple.com/documentation/TechnologyOverviews/adopting-liquid-glass.
#import <Cocoa/Cocoa.h>
@interface AppDelegate : NSObject <NSApplicationDelegate, NSToolbarDelegate>
@property (strong) NSWindow *window;
@end
@implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)notification {
NSRect frame = NSMakeRect(100, 100, 600, 400);
self.window = [[NSWindow alloc] initWithContentRect:frame
styleMask:(NSWindowStyleMaskTitled |
NSWindowStyleMaskClosable |
NSWindowStyleMaskResizable |
NSWindowStyleMaskMiniaturizable)
backing:NSBackingStoreBuffered
defer:NO];
[self.window setTitle:@"Scroll View + Toolbar Demo"];
NSToolbar *toolbar = [[NSToolbar alloc] initWithIdentifier:@"MainToolbar"];
toolbar.displayMode = NSToolbarDisplayModeIconAndLabel;
toolbar.delegate = self;
[self.window setToolbar:toolbar];
NSScrollView *scrollView = [[NSScrollView alloc] initWithFrame:self.window.contentView.bounds];
[scrollView setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)];
[scrollView setHasVerticalScroller:YES];
[scrollView setHasHorizontalScroller:YES];
NSView *documentView = [[NSView alloc] initWithFrame:NSMakeRect(0, 0, 1000, 1000)];
for (int i = 0; i < 10; i++) {
NSTextField *label = [[NSTextField alloc] initWithFrame:NSMakeRect(50, 950 - i*80, 400, 40)];
[label setStringValue:[NSString stringWithFormat:@"Sample Label #%d", i + 1]];
[label setBezeled:NO];
[label setDrawsBackground:NO];
[label setEditable:NO];
[label setSelectable:NO];
[documentView addSubview:label];
}
[scrollView setDocumentView:documentView];
[self.window setContentView:scrollView];
[self.window makeKeyAndOrderFront:nil];
}
- (NSArray<NSToolbarItemIdentifier> *)toolbarAllowedItemIdentifiers:(NSToolbar *)toolbar {
return @[NSToolbarFlexibleSpaceItemIdentifier, @"ShareItem"];
}
- (NSArray<NSToolbarItemIdentifier> *)toolbarDefaultItemIdentifiers:(NSToolbar *)toolbar {
return @[@"ShareItem"];
}
- (NSToolbarItem *)toolbar:(NSToolbar *)toolbar itemForItemIdentifier:(NSToolbarItemIdentifier)itemIdentifier willBeInsertedIntoToolbar:(BOOL)flag {
if ([itemIdentifier isEqualToString:@"ShareItem"]) {
NSToolbarItem *shareItem = [[NSToolbarItem alloc] initWithItemIdentifier:itemIdentifier];
shareItem.toolTip = @"Share this content";
shareItem.image = [NSImage imageNamed:NSImageNameShareTemplate];
shareItem.target = self;
shareItem.action = @selector(shareAction:);
return shareItem;
}
return nil;
}
- (void)shareAction:(id)sender {
NSLog(@"Share button clicked!");
// Here you could present a sharing service picker
NSSharingServicePicker *picker = [[NSSharingServicePicker alloc] initWithItems:@[@"Hello, world!"]];
[picker showRelativeToRect:[sender view].bounds ofView:[sender view] preferredEdge:NSRectEdgeMinY];
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSApplication *app = [NSApplication sharedApplication];
AppDelegate *delegate = [[AppDelegate alloc] init];
[app setDelegate:delegate];
[app run];
}
return EXIT_SUCCESS;
}
But it doesn't and produces this image:
https://imgur.com/a/kA7MzIe
I've tried to set various settings to make the top bar transparent, but all it does is that it makes it completely opaque instead. How can I make the share button float on top of the content?
P.S. the app is a single-file app, compile it with
clang -fobjc-arc -framework Cocoa -o ScrollApp toolbar.m
Topic:
UI Frameworks
SubTopic:
AppKit
Hey all in iOS26.1 i seem to be getting a bunch of Constraint errors when setting the rightBarButtonItem leftBarButtonItem.
Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.
Try this:
(1) look at each constraint and try to figure out which you don't expect;
(2) find the code that added the unwanted constraint or constraints and fix it.
(Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints)
(
"<NSAutoresizingMaskLayoutConstraint:0x6000021a2a30 h=--& v=--& _TtCC5UIKit19NavigationButtonBar15ItemWrapperView:0x13d84c080.width == 0 (active)>",
"<NSLayoutConstraint:0x6000021a1cc0 _TtCC5UIKit19NavigationButtonBar15ItemWrapperView:0x13d84c080.leading == _UIButtonBarButton:0x106cd3ed0.leading (active)>",
"<NSLayoutConstraint:0x6000021a1d10 H:[_UIButtonBarButton:0x106cd3ed0]-(0)-| (active, names: '|':_TtCC5UIKit19NavigationButtonBar15ItemWrapperView:0x13d84c080 )>",
"<NSLayoutConstraint:0x6000021a19f0 'IB_Leading_Leading' H:|-(2)-[_UIModernBarButton:0x106cc80b0] (active, names: '|':_UIButtonBarButton:0x106cd3ed0 )>",
"<NSLayoutConstraint:0x6000021a1a40 'IB_Trailing_Trailing' H:[_UIModernBarButton:0x106cc80b0]-(2)-| (active, names: '|':_UIButtonBarButton:0x106cd3ed0 )>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x6000021a1a40 'IB_Trailing_Trailing' H:[_UIModernBarButton:0x106cc80b0]-(2)-| (active, names: '|':_UIButtonBarButton:0x106cd3ed0 )>
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKitCore/UIView.h> may also be helpful.
Curious if im setting something wrong, or maybe i can just ignore these (granted they are annoying in my logs) This wasnt happening in iOS 26.0
Topic:
UI Frameworks
SubTopic:
UIKit
I have a SwiftUI View containing a basic NavigationStack structure. I host this in a UIHostingController and display modally as a sheet in UIKit using UIViewController.present().
Problem: When I rotate my iOS device to landscape from portrait, the navigation bar's back button extends past the safe area into the left corner of the device. Then, rotating back to portrait the back button is pushed further towards the center - not matching the intended position. (see images below).
Is there an API call I am missing to handle this rotation case? Thanks!
Code Example
SwiftUI view - ContentView.swift
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationStack {
Form {
NavigationLink {
Form {
Text("Hello")
}
.navigationTitle("Filter")
.navigationBarTitleDisplayMode(.inline)
} label: {
Text("Filter")
}
}
.navigationTitle("Filter")
.navigationBarTitleDisplayMode(.inline)
}
}
}
Main UIKit ViewController - ViewController.swift
import UIKit
import SwiftUI
class ViewController: UIViewController {
let swiftUIView = ContentView()
var hostingController: UIHostingController<ContentView>?
override func viewDidLoad() {
super.viewDidLoad()
hostingController = UIHostingController(rootView: swiftUIView)
guard let hostingController else { return }
hostingController.modalPresentationStyle = .formSheet
// Immediately present modally
DispatchQueue.main.async {
self.present(hostingController, animated: true)
}
}
}
More Detail
The issue only seems to appear for sheet modal presentation including these UIModalPresentationStyle:
.formSheet
.pageSheet
.popover
The issue does not appear in an app with SwiftUI app lifecycle or when using UIKit and adding the hosting controller as a child view controller. This specifically happens when presenting modally and when the view gets rendered as a sheet.
I tried various combinations of UIHostingController.sizingOptions and UIHostingController.safeAreaRegions, but never found something that fixed the issue.
To Build and Reproduce
Make a new Xcode project > iOS > App > Storyboard UI
Add file ContentView.swift with contents above.
Replace contents of existing file ViewController.swift with contents above.
Set minimum deployment target to iOS 18
Build and deploy to device running iOS 18.
Hold device in portrait.
Rotate device to landscape - observe back button extends past safe area.
Rotate device to portrait - observe back button is shifted too far towards the center.
Device & Builds Specs
iOS 18.6.2 - minimum deployment of 18.0
Devices Observed:
iPhone 12 Pro Max
iPhone 16 Pro Max
Xcode 26
Image Examples
I have a UITableView with multiple screens worth of cells and the height of the cells vary. At a certain point in the user's workflow, I want to prevent a specific cell from being scrolled out of view. I want it to allow it to scroll from the top of the screen to the bottom but not out of view.
I'm trying to solve this by updating the tableView.contentInset property with the following logic...
let visibleContentHeight = tableView.bounds.height
var insetTop = (cell.frame.origin.y - visibleContentHeight)
insetTop = (insetTop < 0) ? 0 : -insetTop
var insetBottom = tableView.contentSize.height - (cell.frame.origin.y + cell.frame.height + visibleContentHeight)
insetBottom = (insetBottom < 0) ? 0 : -insetBottom
tableView.contentInset = UIEdgeInsets(top: insetTop, left: 0, bottom: insetBottom, right: 0)
This doesn't seem to work exactly right. When scrolling, the cell's top/bottom doesn't line up with the view's top/bottom. And the difference between those margins is not consistent.
Printing out some the raw values, it looks like the cell's frame.origin sometimes changes as well as tableView.contentSize.height. I assume the system is using tableView.estimatedRowHeight to calculate the table's content height for the cells that are not loaded, so that's probably another factor.
Is setting contentInset not the way to do this? Is there another way? Is there an error in my math/logic? I must be missing something. Any help is appreciated.
Thanks.
Topic:
UI Frameworks
SubTopic:
UIKit
We have a custom implementation of what we call a “Scrollable Header” in our app. After building with Xcode 26, we’ve observed a change in behavior with the List component.
The issue can be seen in the attached GIF:
As the user scrolls up, the header is expected to collapse smoothly, and it does—until the moment the next list item becomes visible. At that point, the header collapses prematurely, without any apparent reason.
We’ve identified that this behavior occurs after the list’s data-fetching logic runs, which loads additional items as the user scrolls.
Below is the ViewModifier responsible for handling the collapsing header logic:
@available(iOS 18.0, *)
public struct L3CollapseHeaderIOS18: ViewModifier {
private let minHeight: Double = 0
private let expandedHeight: CGFloat
private let L3Height = 44.0
private let isViewVisible: Bool
@Binding private var currentHeight: CGFloat
@State private var lastOffset: ScrollOffsetInfo = ScrollOffsetInfo(offset: 0.0, offsetToBottom: 0.0, scrollableContent: 0.0)
init(currentHeight: Binding<CGFloat>, expectedHeight: CGFloat, isViewVisible: Bool) {
self._currentHeight = currentHeight
self.expandedHeight = expectedHeight
self.isViewVisible = isViewVisible
}
public func body(content: Content) -> some View {
content
.onScrollGeometryChange(for: ScrollOffsetInfo.self, of: { geometry in
if isViewVisible {
return ScrollOffsetInfo(offset: geometry.contentOffset.y, offsetToBottom: (geometry.contentSize.height) - (geometry.contentOffset.y + geometry.containerSize.height), scrollableContent: geometry.contentSize.height - geometry.containerSize.height)
} else {
return lastOffset
}
}, action: { oldValue, newValue in
if isViewVisible {
expandOrCollapseHeader(oldValue: oldValue, newValue: newValue)
}
})
}
private func expandOrCollapseHeader(oldValue: ScrollOffsetInfo, newValue: ScrollOffsetInfo) {
let oldScrollableContent = round(oldValue.scrollableContent)
let newScrollableContent = round(newValue.scrollableContent)
print("@@ scrollable content: \(newScrollableContent), old value: \(oldScrollableContent)")
if newScrollableContent != oldScrollableContent {/*abs(newValue.offset) - abs(oldValue.offset) > 80*/
return
}
let isInitialPosition = newValue.offset == 0 && lastOffset.offset == 0
let isTryingToBounceUp = newValue.offset < 0
let isTryingToBounceDown = newValue.offsetToBottom < 0
// As the header collapses, the scrollable content decreases its size
let remainingHeaderSpaceVisible = expandedHeight - currentHeight
// remainingHeaderSpaceVisible is summed to know the exact scrollableContent size
let isScrollableContentSmallToAnimate = (newValue.scrollableContent + remainingHeaderSpaceVisible) < (expandedHeight * 2 + currentHeight)
if isInitialPosition || isScrollableContentSmallToAnimate {
expandHeader(0)
return
}
let scrollDirection = scrollDirection(newValue, oldOffset: oldValue)
switch scrollDirection {
case .up(let value):
if isTryingToBounceUp {
expandHeader(0)
return
}
print("@@ will collapse with value: \(value)")
collapseHeader(value)
case .down(let value):
if isTryingToBounceDown {
collapseHeader(0)
return
}
print("@@ will expand with value: \(value)")
expandHeader(value)
case .insignificant:
return
}
}
private func expandHeader(_ value: CGFloat) {
currentHeight = min(68.0, currentHeight - value)
}
private func collapseHeader(_ value: CGFloat) {
currentHeight = max(0, currentHeight - value)
}
private func scrollDirection(_ currentOffset: ScrollOffsetInfo, oldOffset: ScrollOffsetInfo) -> ScrollDirection {
let scrollOffsetDifference = abs(currentOffset.offset) - abs(oldOffset.offset)
print("@@ currentOffset: \(currentOffset.offset), oldOffset: \(oldOffset.offset), difference: \(scrollOffsetDifference)")
let status: ScrollDirection = scrollOffsetDifference > 0
? .up(scrollOffsetDifference)
: .down(scrollOffsetDifference)
lastOffset = currentOffset
return status
}
enum ScrollDirection {
case up(CGFloat)
case down(CGFloat)
case insignificant
}
}
public struct ScrollOffsetInfo: Equatable {
public let offset: CGFloat
public let offsetToBottom: CGFloat
public let scrollableContent: CGFloat
}
public struct ScrollOffsetInfoPreferenceKey: PreferenceKey {
public static var defaultValue: ScrollOffsetInfo = ScrollOffsetInfo(offset: 0, offsetToBottom: 0, scrollableContent: 0)
public static func reduce(value: inout ScrollOffsetInfo, nextValue: () -> ScrollOffsetInfo) {
}
}
We use this ViewModifier to monitor updates to the List’s frame via the onScrollGeometryChange method.
From our investigation (see the screenshot below), it appears that onScrollGeometryChange is triggered just before the content size updates. The first event we receive corresponds to the view’s offset, and only nanoseconds later do we receive updates about the content’s scrollable height.
I’ll share the code for the List component using this modifier in the comments (it exceeded the character limit here).
Can anyone help us understand why this change in behavior occurs after building with Xcode 26?
Thanks in advance for any insights.
Hi!
I wrote a SwiftUI based app which does use a layout similar to a calendar. A week view with 5 day-views. Each day view may contain several record views. I've implemented a drag for the records which works well inside a day view (up and down) but I can't get it working between the days.
Here is an example how it looks:
Any idea or example how to get the proper coordinate information related to the week view inside the rectangle view? A GeometryReader inside the Rectangle view does not give me the needed information. I have currently no idea how to implement it properly…
Topic:
UI Frameworks
SubTopic:
SwiftUI
Filed in Feedback as FB20772137
Zoom transition originating from inside tabViewBottomAccessory, when the binding passed to fullScreenCover's item is a Binding other than a "$-synthesized" binding, the animation fails with the following error (and crucially fails to perform the desired animation):
Starting a zoom transition from a nil view will trigger a fallback transition. To get the best possible teansition, be sure to provide a view that's visible and in a window.
What I want to do is pass a binding to a property inside an ObservableObject (or @Observable, but it doesn't matter) to hold the item representing the presentation. But this stopped working as of 26.1b4. It worked in 26.1b3 and in 26.0 (and 26.0.1)
Here's the gist of code that will reproduce the issue (I've omitted irrelevant details in the interest of brevity):
struct ContentView: View {
@Binding var presentation: PresentationDestination?
@Namespace private var animation
var body: some View {
// Omitted TabView stuff…
.tabViewBottomAccessory {
miniPlayer
.matchedTransitionSource(
id: "player",
in: animation
)
}
.fullScreenCover(
item: $presentation,
content: { _ in
fullScreenPlayer
.navigationTransition(
.zoom(
sourceID: "player",
in: animation
)
)
})
}
As you can see, ContentView takes a Binding to the presentation, but it matters how this binding is constructed.
This works:
@State private var presentation: PresentationDestination
…
ContentView(presentation: $presentation)
This fails (as does ObservableObject with @Published):
@Observable
class Router2 {
var presentation: PresentationDestination?
}
…
@State private var router2 = Router2()
…
ContentView(presentation: $router2.presentation)
Also, this fails:
@State private var presentation: PresentationDestination
…
ContentView(
presentation: .init(get: {
presentation
}, set: { newValue in
presentation = newValue
})
)
These differences are unexpected, of course. I consider this a regression in 26.1b4
I should add that if I move the source of the transition to somewhere outside tabViewBottomAccessory things seem to work fine.