Prior to iOS 26, ToolbarItems with .bottomBar placement were convenient for tab-specific frequent actions.
With iOS 26’s new tab layering now obscuring such ToolbarItems, it’s unclear whether .tabViewBottomAccessory is the intended replacement or if another pattern (like persistent floating buttons) is encouraged instead.
What’s the recommended way to support quick, tab-specific actions under the new system?
I’ve tried conditionally rendering a .tabViewBottomAccessory based on the active tab, but this causes a crash, which I’ve reported as FB18479195.
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
Activity
NSVisualEffectView in AppKit has two main properties: material and blendingMode.
Material is well supported in SwiftUI, but I can't seem to find an equivalent for blendingMode.
What is the SwiftUI equivalent to NSVisualEffect.BlendingMode?
[Submitted as FB21078443]
When using .matchedTransitionSource with .navigationTransition(.zoom), swiping back from the left edge to return from a detail view causes the source item to disappear once the transition finishes. It’s only a visual issue—the item is still there and can be tapped to open again.
This doesn’t happen when using the Back button; only the swipe-back gesture triggers it. Also, it only reproduces on a physical device, not in Simulator.
SYSTEM INFO
Xcode 26.1.1 (17B100)
macOS 26.1 (25B78)
iOS 26.1 (23B85)
iOS 26.2 (23C5044b)
REPRO STEPS
Run the code below on a physical device, tap an image, then swipe from the left edge to dismiss the detail view.
ACTUAL
The image zooms back to its origin, then disappears once the animation settles.
EXPECTED
The image card remains visible.
SCREENSHOTS
CODE
import SwiftUI
struct Item: Identifiable, Hashable {
let id = UUID()
let imageName: String
let title: String
}
struct ContentView: View {
@Namespace private var namespace
let items = [
Item(imageName: "SampleImage", title: "Sample Card 1"),
Item(imageName: "SampleImage2", title: "Sample Card 2")
]
var body: some View {
NavigationStack {
ScrollView {
VStack(spacing: 16) {
ForEach(items) { item in
NavigationLink(value: item) {
CardView(item: item)
.matchedTransitionSource(id: item.id, in: namespace)
}
.buttonStyle(.plain)
}
}
.padding()
}
.navigationTitle("Zoom Transition Issue")
.navigationSubtitle("Tap image, then swipe back from left edge")
.navigationDestination(for: Item.self) { item in
DetailView(item: item, namespace: namespace)
.navigationTransition(.zoom(sourceID: item.id, in: namespace))
}
}
}
}
struct CardView: View {
let item: Item
var body: some View {
GeometryReader { geometry in
ZStack(alignment: .bottom) {
Image(item.imageName)
.resizable()
.scaledToFill()
.frame(width: geometry.size.width, height: geometry.size.height)
.clipped()
}
}
.frame(height: 200)
.clipShape(RoundedRectangle(cornerRadius: 16))
}
}
struct DetailView: View {
let item: Item
let namespace: Namespace.ID
var body: some View {
Image(item.imageName)
.resizable()
.scaledToFill()
.clipped()
}
}
Topic:
UI Frameworks
SubTopic:
SwiftUI
This is really odd. If you setup a UISearchController with a preferredSearchBarPlacement of .stacked and you setup the search bar with scope buttons, then when the view controller is initially displayed, the currently hidden scope buttons block touch events from reaching the main view just below the search bar. But once the search is activated and dismissed, then the freshly hidden scope buttons no longer cause an issue.
This is easily demonstrated by putting a UITableViewController in a UINavigationController. Setup the table view to show a few simple rows. Then setup a search controller using the following code:
func setupSearch() {
// Setup a stacked search bar with scope buttons
// Before the search is ever activated, the hidden scope buttons block any touches in the main view controller
// in the area just below the search bar.
// Once the search is activated and dismissed, the problem goes away. It seems that displaying and hiding the
// scope buttons at least once fixes the issue that exists beforehand.
// This issue only exists in iOS/iPadOS 26, not iOS/iPadOS 18 or earlier.
let search = UISearchController(searchResultsController: UIViewController())
search.hidesNavigationBarDuringPresentation = true
search.obscuresBackgroundDuringPresentation = true
search.scopeBarActivation = .onSearchActivation // Ensure button appear immediately
let searchBar = search.searchBar
searchBar.scopeButtonTitles = [ "One", "Two", "Three" ]
self.navigationItem.searchController = search
self.navigationItem.hidesSearchBarWhenScrolling = false // Issue appears even if this is true
self.navigationItem.preferredSearchBarPlacement = .stacked
}
When first shown, before any attempt is made to activate the search, any attempt to tap on the upper 2/3 of the first row in the table view (which is just below the search bar) fails. If you tap on the lower 1/3 of the first row it works fine. If you then activate the search (now the scope buttons appear) and then dismiss the search (now the scope buttons are hidden again), then there is no issue tapping anywhere on the first row of the table. But if you restart the app, the problem starts over again.
This problem happens on any iPhone or iPad, real or simulated, running iOS/iPadOS 26 RC. This is a regression from iOS 18 or earlier.
It seems like it is no longer possible to open the main window of an app after the app has been launched by the system if the "Auto Start" functionality has been enabled.
I am using SMAppService.mainApp to enable to auto start of my app. It is shown in the macOS system settings and the app is automatically started - but the main window is not visible.
How can I change this behaviour so the main window of the app is always visible when started automatically?
I have not noticed this behaviour before the release of macOS Sequoia. My app is using Swift 6 and the latest version of macOS and Xcode.
Regards
Topic:
UI Frameworks
SubTopic:
SwiftUI
UITextView crash when setting attributed text that contains substring ffi and attributedText contains NSFontAttributeName, NSForegroundColorAttributeName
Reproducible case:
UITextView *textView = [[UITextView alloc] init];
textView.attributedText = [[NSAttributedString alloc] initWithString:@"ffi" attributes:@{
NSParagraphStyleAttributeName: [self createParagraphOfLineHeight:20],
NSFontAttributeName: [UIFont systemFontOfSize:fontSize weight:UIFontWeightRegular],
NSForegroundColorAttributeName: UIColor.black
}];
An app that is capable of running on iPad can be usually run on Mac if properly designed and that's great. Recently I've tried to launch one of my old apps on macOS 26 in "Designed for iPad" mode and noticed that image picker behaves oddly. Images are barely selectable, you have to click several times and yet it might select image and might not. On iPhone and on iPad any kind of image picking works fine. I've tried all kinds of native pickers (PhotosPicker, PHPickerViewController, UIImagePickerController), but the result is almost the same.
So how should I nowadays do image picking in (mostly) iOS app when it is run on macOS?
Below is the most canonical and modern code I've tried. The issue is reproduced even with such bare minimum of code with the label not being put to a Form/List or any other factors.
import SwiftUI
import PhotosUI
struct ContentView: View {
@State private var selectedItem: PhotosPickerItem?
@State private var selectedImage: UIImage?
var body: some View {
VStack {
if let selectedImage {
Image(uiImage: selectedImage)
.resizable()
.scaledToFit()
}
// Most modern photo library picker, not `PHPickerViewController`, not `UIImagePickerController` based pickerPhotosPicker(
selection: $selectedItem,
matching: .images
) {
Label("Select Photo", systemImage: "photo")
}
.onChange(of: selectedItem) { newItem inTask {
if let data = try? await newItem?.loadTransferable(type: Data.self),
let uiImage = UIImage(data: data) {
selectedImage = uiImage
}
}
}
}
}
}
The following shows minimal example to reproduce the issue:
Menu {
Button("Test"){}
} label: {
Text("Menu")
} primaryAction: {
// Some action
}
primaryAction modifier will not be called when pressing the menu button/view on iOS 26 beta, long pressing it will open the menu.
Was tested on latest iOS 26 beta 8
Topic:
UI Frameworks
SubTopic:
SwiftUI
I am trying to build a text editor that shrinks to its content size. The closest I have been able to get has been to add the .scrollDisabled(true) and .fixedSize(horizontal: false, vertical: true) modifiers.
This almost achieves what I need. There are two problems though:
long single line text gets cut off at the end
creating line breaks causes the text editor to grow vertically as expected (uncovering the cut off text in point 1 above). However, when you delete the line breaks, the TextEditor does not shrink again.
I have had a radar open for some time: FB13292506. Hopefully opening a thread here will get more visibility.
And here is some sample code to easily reproduce the issue:
import SwiftUI
struct ContentView: View {
@State var text = "[This is some long text that will be cut off at the end of the text editor]"
var body: some View {
TextEditor(text: $text)
.scrollDisabled(true)
.fixedSize(horizontal: false, vertical: true)
}
}
#Preview {
ContentView()
}
Here is a gif of the behavior:
Hi everyone!
I've encountered an issue when using Sheet + ScrollView on Mac Catalyst: the buttons in the toolbar appear with an abnormal gray color.
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
}
.sheet(isPresented: .constant(true)) {
Sheet()
}
}
}
struct Sheet: View {
var body: some View {
NavigationStack {
ScrollView { // <-- no issue if use List
}
.toolbar {
Button(action: {}) { // <-- 👀 weird gray color
Image(systemName: "checkmark")
}
}
}
}
}
Steps to Reproduce:
On macOS 26.0 beta 9, use Xcode 26.0 beta 7 to create an iOS project and enable Mac Catalyst.
Paste the code above.
Select the Mac Catalyst scheme and run the project.
The buttons in the toolbar show a strange gray appearance.
If you change the ScrollView to a List in the code, the issue does not occur.
FB20120285
I've been trying out the new .safeAreaBar modifier for iOS 26, but I cannot seem to notice any difference between that and .safeAreaInset?
The documentation says:
the bar modifier configures the content to support views to automatically extend the edge effect of any scroll view’s the bar adjusts safe area of.
But I can't seem to see that in action.
please fix!
I'm running into an issue where my application will hang when switching tabs. The issue only seems to occur when I include a Swift Chart in a navigation label. The application does not hang If I replace the chart with a text field.
This appears to only hang when running on macOS 26. When running on iOS (simulator) or visionOS (simulator, on-device) I do not observe a hang. The same code does not hang on macOS 15.
Has any one seen this behavior?
The use case is that my root view is a TabView where the first tab is a summary of events that have occurred. This summary is embedded in a NavigationStack and has a graph of events over the last week. I want the user to be able to click that graph to get additional information regarding the events (ie: a detail page or break down of events).
Initially, the summary view loads fine and displays appropriately. However, when I switch to a different tab, the application will hang when I switch back to the summary view tab.
In Xcode I see the following messages
=== AttributeGraph: cycle detected through attribute 162104 ===
=== AttributeGraph: cycle detected through attribute 162104 ===
=== AttributeGraph: cycle detected through attribute 162104 ===
=== AttributeGraph: cycle detected through attribute 162104 ===
=== AttributeGraph: cycle detected through attribute 162104 ===
A simple repro is the following
import SwiftUI
import Charts
@main
struct chart_cycle_reproApp: App {
var body: some Scene {
WindowGroup {
TabView {
Tab("Chart", systemImage: "chart.bar") {
NavigationStack {
NavigationLink {
Text("this is an example of clicking the chart")
}
label: {
Chart {
BarMark(
x: .value("date", "09/03"),
y: .value("birds", 3)
)
// additional marks trimmed
}
.frame(minHeight: 200, maxHeight: .infinity)
}
}
}
Tab("List", systemImage: "list.bullet") {
Text("This is an example")
}
}
}
}
}
Hello!
My question is about 1) if we can use any and or all accessibility features within a sandboxed app and 2) what steps we need to take to do so.
Using accessibility permissions, my app was working fine in Xcode. It used NSEvent.addGlobalMonitorForEvents and localMoniter, along with CGEvent.tapCreate. However, after downloading the same app from the App Store, the code was not working. I believe this was due to differences in how permissions for accessibility are managed in Xcode compared to production.
Is it possible for my app to get access to all accessibility features, while being distributed on the App Store though? Do I need to add / request any special entitlements like com.apple.security.accessibility?
Thanks so much for the help. I have done a lot of research on this online but found some conflicting information, so wanted to post here for a clear answer.
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()
}
In the example below, VoiceOver (in both iOS 18 and 26) reads the text contained within the image after the .accessibilityLabel, introduced by a “beep.”
VoiceOver: Purple rounded square with the word 'Foo' in white letters. Image [beep] foo.
I’d like it to only read the accessibility label. As a developer focused on accessibility, I make sure every image already has an appropriate label, so having iOS read the image text is redundant.
Sample Code
import SwiftUI
struct ContentView: View {
var body: some View {
Image("TextInImage")
.resizable()
.scaledToFit()
.frame(width: 64, height: 64)
.accessibilityLabel("Purple rounded square with the word 'Foo' in white letters.")
}
}
Sample Image
Drop this image in to Assets.xcassets and confirm it's named TextInImage.
This line in the WWDC25: Build a SwiftUI app with the new design talk, Doesn't work. no .rect is not member and neither corner a parameter or containerConcentric listed. Help
.background(.tint, in: .rect(corner: .containerConcentric))
Topic:
UI Frameworks
SubTopic:
SwiftUI
The "Cancel" button for VNDocumentCameraViewController is not displayed on iPadOS 26. This issue appears to be specific to iPad, as the button appears correctly on iPhone.
Super easy to reproduce.
Swiping to delete on the last remaining item in the list causes an index out of bounds exception. If you have 2 items in your list, it will only happen when you delete the last remaining item.
From my testing, this issue occurs on 18.3.1 and onward (the RC it happens). I didn't test 18.3.0 so it might happen there as well.
The only workarounds I have found is to add a delay before calling my delete function:
OR
to comment out the Toggle.
So it seems as though iOS 18.3.x added a race condition in the way the ForEach accesses the values in its binding.
Another thing to note, this also happens with .swipeActions, EditMode, etc... any of the built in ways to delete an item from a list.
import SwiftUI
struct ContentView: View {
@StateObject var viewModel = ContentViewModel()
var body: some View {
List {
ForEach($viewModel.items) { $item in
HStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundStyle(.tint)
Text($item.text.wrappedValue)
Spacer()
Toggle(String(""), isOn: $item.isActive)
.labelsHidden()
}
}
.onDelete(perform: delete)
}
}
func delete(at offsets: IndexSet) {
// uncomment task to make code not crash
// Task {
viewModel.deleteItem(at: offsets)
// }
}
}
struct MyItem: Identifiable {
var id: UUID = UUID()
var text: String
var isActive: Bool
}
class ContentViewModel: ObservableObject {
@Published var items: [MyItem] = [MyItem(text: "Hello, world!", isActive: false)]
func deleteItem(at offset: IndexSet) {
items.remove(atOffsets: offset)
}
}
Already filed a feedback in case this is a bug, but posting here in case I'm doing something wrong?
I'd like the search field to automatically be displayed with the keyboard up when the view appears. This sample code works in iOS 18, but it does not work in iOS 26 beta 7
I also tried adding a delay to setting searchIsFocused = true but that did not help
struct ContentView: View {
var body: some View {
NavigationStack {
NavigationLink(destination: ListView()) {
Label("Go to list", systemImage: "list.bullet")
}
}
.ignoresSafeArea()
}
}
struct ListView: View {
@State private var searchText: String = ""
@State private var searchIsPresented: Bool = false
@FocusState private var searchIsFocused: Bool
var body: some View {
ScrollView {
Text("Test")
}
.searchable(text: $searchText, isPresented: $searchIsPresented, placement: .automatic, prompt: "Search")
.searchFocused($searchIsFocused)
.onAppear {
searchIsFocused = true
}
}
}