Provide views, controls, and layout structures for declaring your app's user interface using SwiftUI.

Posts under SwiftUI tag

200 Posts

Post

Replies

Boosts

Views

Activity

ManipulationComponent + Warning messages in RealityView
Hi guys! I wanted to study this new ManipulationComponent(), but I keep getting a warning that I don’t understand, even in a very simple scenario. i don't have any collisions just binding the Manipulation the warning message is : ** Entity returned from EntityWrapper.makeEntity(context:) was already parented to another entity. This is not supported and may lead to unexpected behavior. SwiftUI adds entities to internally-managed entity hierarchies.** RealityView { content, attachments in if let loadedModel = try? await Entity(named: "cloud_glb", in: realityKitContentBundle) { content.add(loadedModel) loadedModel.components.set(ManipulationComponent()) } Thanks !
4
0
778
4w
Pinch gesture not recognized on MTKView when attaching it to a RealityView using a ViewAttachmentComponent in a immersive space
Hello! We are seeing a problem with a SwiftUI view that wraps an MTKView and that MTKView uses gesture recognizers from UIKit. One of those gestures we are using is UIPinchGestureRecognizer. And that gesture isn’t recognized at all when the SwiftUI view is attached to a RealityView using the ViewAttachmentComponent AND the RealityView is being shown in an ImmersiveSpace. If the SwiftUI view is attached to the RealityView using the init that has an attachment closure then pinching works fine there. So this definitely seems like a bug. Here is some code to help you reproduce the problem. Run this on a Vision Pro device. A simple red square will be rendered and if a single tap or pinch gesture is recognized on the red square, it will print to the console. App Code: import SwiftUI @main struct VisionPinchProblemsApp: App { var body: some Scene { WindowGroup { MenuView() } ImmersiveSpace(id: "RedSquare") { RedSquareView() } } } View code: import MetalKit import RealityKit import SwiftUI import UIKit struct MenuView: View { @Environment(\.openImmersiveSpace) private var openImmersiveSpace @Environment(\.dismissImmersiveSpace) private var dismissImmersiveSpace @State private var showImmersiveSpace = false @State private var immersiveSpaceIsOpen = false var body: some View { Form { Toggle("Show red square", isOn: $showImmersiveSpace) .task(id: showImmersiveSpace) { if showImmersiveSpace { await openImmersiveSpace(id: "RedSquare") immersiveSpaceIsOpen = true } else { if immersiveSpaceIsOpen { await dismissImmersiveSpace() immersiveSpaceIsOpen = false } } } } .onDisappear { // Attempt to close the immersive space on the way out. Task { if immersiveSpaceIsOpen { await dismissImmersiveSpace() } } } } } struct RedSquareView: View { let metalViewAttachmentID = "metalID" var body: some View { // Adds SwiftUI view using attachments closure. // Pinching and single taps are recognized here! // RealityView { content, attachments in // if let metalViewEntity = attachments.entity(for: metalViewAttachmentID) { // metalViewEntity.position = [0, 1, -1.25] // content.add(metalViewEntity) // } // } placeholder: { // ProgressView() // } attachments: { // Attachment(id: metalViewAttachmentID) { // MetalView() // } // } // Add SwiftUI view using ViewAttachmentComponent. // Pinching is not recognized here! // Single tapping is recognized ! // Why doesn't the red square show up in the Vision Pro simulator? RealityView { content in let metalViewEntity = Entity() let metalView = MetalView() .frame(width: 500, height: 500) let component = ViewAttachmentComponent(rootView: metalView) metalViewEntity.components.set(component) metalViewEntity.position = [0, 1, -1.25] content.add(metalViewEntity) } placeholder: { ProgressView() } } } struct MetalView: UIViewRepresentable { var device: MTLDevice? init() { self.device = MTLCreateSystemDefaultDevice() } func makeUIView(context: Context) -> MTKView { let mtkView = MTKView() mtkView.device = device mtkView.clearColor = MTLClearColor(red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0) mtkView.delegate = context.coordinator let pinchGesture = UIPinchGestureRecognizer(target: context.coordinator, action: #selector(context.coordinator.handlePinch(_:))) mtkView.addGestureRecognizer(pinchGesture) let tapGesture = UITapGestureRecognizer(target: context.coordinator, action: #selector(context.coordinator.handleTap(_:))) mtkView.addGestureRecognizer(tapGesture) return mtkView } func updateUIView(_ uiView: MTKView, context: Context) { } func makeCoordinator() -> Coordinator { Coordinator(self) } class Coordinator: NSObject, MTKViewDelegate { var parent: MetalView init(_ parent: MetalView) { self.parent = parent } func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) { } func draw(in view: MTKView) { guard let drawable = view.currentDrawable else { return } guard let descriptor = view.currentRenderPassDescriptor else { return } let commandQueue = parent.device?.makeCommandQueue() let commandBuffer = commandQueue?.makeCommandBuffer() let renderEncoder = commandBuffer?.makeRenderCommandEncoder(descriptor: descriptor) renderEncoder?.endEncoding() commandBuffer?.present(drawable) commandBuffer?.commit() } @objc func handlePinch(_ sender: UIPinchGestureRecognizer) { print("Pinch detected") } @objc func handleTap(_ sender: UITapGestureRecognizer) { print("Tap detected") } } }
1
0
689
4w
ManipulationComponent causes makeUIView(context:) to get called twice
Here I have some demo code that is rendering a cylinder "platter" using RealityKit and there is a red circle rendered on top of it which uses Metal and SwiftUI. When the platter appears you will see in the console that makeUIView(context:) is called twice while it is documented that it will only be called once when the view appears for the first time. So this seems like a bug. If you remove ManipulationComponent from the platter's components you will see that this problem goes away so it seems like that is the cause of the problem. Any insight here would be appreciated! Thank you. Here is what is printed in the console: Entity returned from EntityWrapper.makeEntity(context:) was already parented to another entity. This is not supported and may lead to unexpected behavior. SwiftUI adds entities to internally-managed entity hierarchies. Make UI View! This should be called once. Make UI View! This should be called once. Here is the app code: import SwiftUI @main struct SomeApp: App { var body: some Scene { WindowGroup { ContentView() } ImmersiveSpace(id: "TableTop") { TableTopPlatterView() } } } Here is the view code: import MetalKit import RealityKit import SwiftUI struct ContentView: View { @Environment(\.openImmersiveSpace) private var openImmersiveSpace @Environment(\.dismissImmersiveSpace) private var dismissImmersiveSpace @State private var showImmersiveSpace = false @State private var immersiveSpaceIsOpen = false var body: some View { Form { Toggle("Show table top", isOn: $showImmersiveSpace) .task(id: showImmersiveSpace) { if showImmersiveSpace { await openImmersiveSpace(id: "TableTop") immersiveSpaceIsOpen = true } else { if immersiveSpaceIsOpen { await dismissImmersiveSpace() immersiveSpaceIsOpen = false } } } } .onDisappear { // Attempt to close the immersive space on the way out. Task { if immersiveSpaceIsOpen { await dismissImmersiveSpace() } } } } } struct TableTopPlatterView: View { private var attachmentID: String { "RedCircle" } var body: some View { RealityView { content, attachments in if let redCircleEntity = attachments.entity(for: attachmentID) { // Lays the red circle in the platter. let rotation = Rotation3D(redCircleEntity.orientation) .rotated(by: .init(angle: .degrees(-90), axis: .x)) redCircleEntity.setOrientation(.init(rotation), relativeTo: nil) redCircleEntity.position.y = 0.026 platterEntity.addChild(redCircleEntity) content.add(platterEntity) } } placeholder: { ProgressView() } attachments: { Attachment(id: attachmentID) { MetalView() .clipShape(.circle) } } } /// The platter entity that the red circle lays on top of. private let platterEntity: ModelEntity = { let anchor = AnchorEntity( .plane( .horizontal, classification: .table, minimumBounds: [0.01, 0.01] ) ) let material = SimpleMaterial( color: .lightGray, roughness: 0.5, isMetallic: false ) let platter = ModelEntity( mesh: .generateCylinder(height: 0.05, radius: 0.475), materials: [material] ) platter.generateCollisionShapes(recursive: false) let components: [any Component] = [ InputTargetComponent(), GroundingShadowComponent(castsShadow: true), ManipulationComponent() // MARK: This is causing makeUIView to get called twice! ] platter.components.set(components) // Placed closer to the user when booted up. platter.position = [0, 1, -1.25] anchor.addChild(platter) return platter }() } // Metal view that renders a red square. struct MetalView: UIViewRepresentable { var device: MTLDevice? init() { self.device = MTLCreateSystemDefaultDevice() } func makeUIView(context: Context) -> MTKView { print("Make UI View! This should be called once.") let mtkView = MTKView() mtkView.device = device mtkView.clearColor = MTLClearColor(red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0) mtkView.delegate = context.coordinator return mtkView } func updateUIView(_ uiView: MTKView, context: Context) { } func makeCoordinator() -> Coordinator { Coordinator(self) } class Coordinator: NSObject, MTKViewDelegate { var metalView: MetalView init(_ metalView: MetalView) { self.metalView = metalView } func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) { } func draw(in view: MTKView) { guard let drawable = view.currentDrawable else { return } guard let descriptor = view.currentRenderPassDescriptor else { return } let commandQueue = metalView.device?.makeCommandQueue() let commandBuffer = commandQueue?.makeCommandBuffer() let renderEncoder = commandBuffer?.makeRenderCommandEncoder(descriptor: descriptor) renderEncoder?.endEncoding() commandBuffer?.present(drawable) commandBuffer?.commit() } } }
3
0
1.5k
4w
Intermittent UIHostingController layout regression after app launch on iPadOS 27 beta 4
On iPadOS 27.0 beta 4, SwiftUI views hosted inside UIKit-managed containers/overlays can be laid out incorrectly. The same app and same code work correctly on iPadOS 26.x release versions. One visible example is the app’s About dialog. The dialog is implemented as a UIKit UIViewController presented with UIModalPresentationFormSheet. Inside that controller, a UIHostingController is added as a child view controller, and the hosting controller’s view is constrained to all four edges of the parent view. The SwiftUI root view is a VStack containing the app icon, app name, version, text, links, and buttons. Expected behavior: The SwiftUI content should be vertically centered inside the form sheet. The app icon should appear above the app title, followed by the version, text, links, and buttons. This is the behavior on iPadOS 26.x. Actual behavior on iPadOS 27.0 beta 4: When the About dialog is opened immediately after launching the app, this issue occurs intermittently. The form sheet itself appears, but the SwiftUI content is shifted upward. The app icon is missing or clipped, the title starts too close to the top edge of the sheet, and a large empty area appears at the bottom of the sheet. This is not limited to the About dialog. Similar layout issues can also appear in other SwiftUI-hosted UI surfaces in the app. The issue also appears to be affected by system-level UI changes: If I put the app into windowed mode and resize the whole app window, the layout inside the dialog recovers and becomes correct. The issue only occurs with some probability the first time this dialog is opened after launching the app. After the layout is restored by resizing the app window, the issue does not appear again as long as the app process is not terminated. This suggests the problem may be related to an incorrect initial layout pass, cached geometry, trait/safe-area propagation, or UIHostingController layout invalidation after the app/window scene is first created. Environment: Device: iPad OS with issue: iPadOS 27.0 beta 4 OS without issue: iPadOS 26.x release App: VoidLink - Extreme UI stack: UIKit containers/overlays hosting SwiftUI through UIHostingController Relevant code: AboutView.swift: https://github.com/The-Fried-Fish/VoidLink-previously-moonlight-zwm/blob/Integration/VoidLink/AboutView.swift AboutViewController.swift: https://github.com/The-Fried-Fish/VoidLink-previously-moonlight-zwm/blob/Integration/VoidLink/AboutViewController.swift Attachments: IMG_0291.PNG: correct layout on iPadOS 26.x IMG_0292.PNG: incorrect layout on iPadOS 27.0 beta 4
1
0
440
4w
Is it normal that mounted @Query cause every object of that type to re-render, even after unrelated saves?
I have a SwiftUI + SwiftData app where scrolling became very slow, and I've traced it to something about @Query I didn't expect. The app was running save for each lazy list item appearing in viewport (a separate bug, but it did highlight the problem), which made the whole list re-render on each save. After tracing why this happens I have discovered that it is because another model watched in the list parent is invalidated after every save, and the problem was that this another model has @Query in a separate view (sidebar), removing that @Query fixed the problem. Here is a minimal reproduction of this https://github.com/aytigra/QueryRefaultRepro I wonder if it is a bug or an expected behavior?
0
0
528
Aug ’26
How to achieve the UIEditMenuInteraction (?) for Link Preview used in iOS 27 Messages
I've been using the iOS 27 beta and noticed in Messages app that the link presentation is now customisable. Upon tapping on the link it opens (what I assume is) an edit menu interaction, which allows customising which metadata is shown. I've seen some links offer more customisation than others, presumably based on available metadata. There's also an option in the menu to convert to a text link, and when highlighting a link in text there's an option to "show link preview" which converts it to an LPLinkView. I've been wondering for a while now if it was possible to add a similar feature to my own app, allowing the user more control over the link previews. How can I achieve similar? Especially "Customise Link" sheet seen in the middle two screenshots?
0
0
666
Aug ’26
Adding a ManipulationComponent detaches the whole RealityView content hierarchy on visionOS 27
On visionOS 27 (27.0 Seed 4, 24M5326g), adding a ManipulationComponent to a single entity makes RealityKit remove the RealityView's ENTIRE content hierarchy from the scene and re-add it about 30ms later. Not just the manipulated entity — everything. Same app binary on visionOS 26: never happens. FB24092291. Bisected on the same build and device: two entities, neither with manipulation -> no detach add one entity with a ManipulationComponent -> detach, every time same entity, manipulation swapped for my own drag/rotate/ scale gesture handling -> no detach Everything else is identical between the last two cases — same entity, same collision shape, same InputTargetComponent, same ViewAttachmentComponent, same scene, same view hierarchy. The only variable is whether the component is installed. Setup: an immersive space containing a SwiftUI RealityView. One root entity added to the RealityViewContent in the make closure; all app content built as its descendants. The detach is brief but not harmless: every entity in the hierarchy gets SceneEvents.WillRemoveEntity and then DidAddEntity, so anything keyed to those events runs a full teardown and re-add for content that was never meant to go anywhere. In my app that cascade is what makes the scene visibly empty and rebuild. Nothing in my code removes it. Stack captured from a WillRemoveEntity subscription on the root — frame 0 is my callback, everything above it is framework: SwiftUI (AttributeGraph StatefulRule.withObservation ...) -> RealityFoundation -> CoreRE -> entity removal Ruled out before landing on the component: make runs exactly once, the root re-enters a scene with the same ObjectIdentifier (not a re-host), the hosting controller is never deallocated, there is exactly one RealityViewContent.add(_:) in the whole app, and with Self._printChanges() in the view's body the detach happens in an update pass where the body is not re-evaluated at all. Also ruled out: transient overlay UI (suppressed entirely, still detached) and entity count. Workaround if this is biting you: skip ManipulationComponent on 27 and handle drag/rotate/scale yourself. That is what I am doing for now. If you are debugging something similar: SceneEvents.WillRemoveEntity also fires when an entity merely leaves a scene, so a teardown-looking log is not proof anything was destroyed. Check whether make ran twice and whether the scene identity changed before suspecting your own code.
1
2
1.2k
Aug ’26
Extract Subview option missing in Xcode 26
Hi everyone, I recently updated to Xcode 26.0 and noticed that the “Extract Subview” refactoring option seems to be missing. Now, in Xcode 26, the only options I see under Editor -> Refactor -> are: Extract to Selection File Extract to Method Extract to Variable Extract All Occurrences But there’s no Extract Subview as there was before. Was Extract Subview intentionally removed in Xcode 26? Or is it hidden behind a new menu location or renamed?
6
8
1.1k
Aug ’26
SwiftUI: List cells flicker during scroll when .searchPresentationToolbarBehavior(.avoidHidingContent) is used with searchable modifier inside a sheet (iOS 26)
Description When a searchable List is presented inside a sheet and uses .searchPresentationToolbarBehavior(.avoidHidingContent) to keep the navigation bar visible during search, the list cells flicker/blink under the keyboard This reproduces with public SwiftUI API only — no UIKit, no appearance-proxy customization. Steps to Reproduce Create a new iOS App (SwiftUI) project (deployment target iOS 17.1+). Replace the generated App file with the sample code below. Run on iOS 26 (reproduces on both device and Simulator). Tap "Open transfer methods" to present the sheet. Tap the search field so the keyboard is shown (search becomes active). Scroll the list down, then up into the top bounce (overscroll) Observe the cells under the keyboard. Expected Cells scroll smoothly under the keyboard; no flicker. Actual Cells under the keyboard flicker/blink during top overscroll. See the attachments with shots of the process: 1. screen before a blink, 2. Screen in the moment of blinking Sample Code import SwiftUI @main struct FlickerReproApp: App { var body: some Scene { WindowGroup { RootView() } } } struct RootView: View { @State private var isSheetPresented = false var body: some View { Button("Open transfer methods") { isSheetPresented = true } .sheet(isPresented: $isSheetPresented) { NavigationStack { SimpleSearchScreen() } } } } struct SimpleSearchScreen: View { @State private var searchText = "" private let items = (0..<40).map { "Recipient \($0)" } private var filteredItems: [String] { searchText.isEmpty ? items : items.filter { $0.localizedCaseInsensitiveContains(searchText) } } var body: some View { List { ForEach(filteredItems, id: \.self) { item in Text(item) } } .listStyle(.plain) .navigationTitle("Transfer methods") .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .topBarLeading) { Button("Close", systemImage: "xmark") {} } } .searchable(text: $searchText, placement: .automatic, prompt: "Search") // Remove the line below -> flicker disappears, but the navigation bar // (title + toolbar items) then hides during search, which we need to keep. .searchPresentationToolbarBehavior(.avoidHidingContent) } } Notes / What was ruled out Removing .searchPresentationToolbarBehavior(.avoidHidingContent) eliminates the flicker — but then the navigation bar hides during search, which is exactly the behavior the modifier is meant to prevent. Presenting the same screen NOT inside a sheet does not flicker — the sheet presentation is required to reproduce. Independent of app-level customization: reproduces with no UINavigationBar/UITabBar/UISearchBar appearance proxies and no UIKit. Also tried, did NOT help: .scrollEdgeEffectStyle(.hard, for: .all), .toolbarBackground(.hidden, for: .navigationBar), keeping the bar visible via UISearchController.hidesNavigationBarDuringPresentation = false instead, .geometryGroup() on rows, .scrollDismissesKeyboard(.never), removing safe area insets.
0
0
210
Jul ’26
Animations become choppy in NSStatusItem when other window contains ScrollView
Feedback ID: FB23984230 This issue for some reason does not happen on my external 165 Hz display, but happens on my built-in MacBook Air display (60Hz). See attached example project: Contains two parts NSStatusItem with animation that triggers during ‘.onTapGesture()’ a window with ContentView that contains List and ScrollView while any ScrollView / List is in the view hierarchy in ContentView, triggering an animation in NSStatusItem (on a built-in MacBook display) is very choppy once removing ScrollView / List from view hierarchy from ContentView, animation in NSStatusItem is very smooth Project: Link macOS 26.5.2 (25F84)
1
0
519
Jul ’26
Looking on feedback on the UI Design
Hi everyone, I'm developing a simple iOS app that solves quadratic equations using SwiftUI. I've attached a screenshot of the current interface. I'd appreciate feedback on the UI and user experience, especially from the perspective of Apple's Human Interface Guidelines. Thanks!
Topic: Design SubTopic: General Tags:
1
0
887
Jul ’26
Unexpected behavior in the interaction between LazyVStack and GeometryReader
Hello! I'd like to share a problem and its potential solution. Steps to reproduce: The issue can be reproduced with the following minimal example: struct TestConditionalScrollView: View { var body: some View { ConditionalScrollView { LazyVStack(spacing: 16) { Text("Text 1") .frame(height: 20) Text("Text 2") .frame(height: 30) Text("Text 3") .frame(height: 40) Text("Text 4") .frame(height: 500) Text("Text 5") .frame(height: 400) } .padding() } } } struct ConditionalScrollView<Content: View>: View { let content: Content init(@ViewBuilder content: () -> Content) { self.content = content() } @State private var contentHeight: CGFloat = 0 var body: some View { GeometryReader { geo in _ = print("Height: \(contentHeight)") return Group { if contentHeight > geo.size.height { ScrollView { measuredContent } } else { measuredContent } } } } private var measuredContent: some View { content .background( GeometryReader { geo in Color.clear .preference( key: ContentHeightKey.self, value: geo.size.height ) } ) .onPreferenceChange(ContentHeightKey.self) { contentHeight = $0 } } } struct ContentHeightKey: PreferenceKey { static var defaultValue: CGFloat = 0 static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) { value = max(value, nextValue()) } } If we hit the breakpoint on the following line: _ = print("Height: (contentHeight)") the output looks like this: Problems observed Different values are reported, and it is unclear where those values originate from. The selected execution branch appears to change multiple times during the layout process. Possible reason As Rens Breur mentioned in the WWDC26 session "Dive into lazy stacks and scrolling with SwiftUI", LazyVStack relies on estimated layout information during certain phases of the layout process. I do not want to rely on or investigate SwiftUI's non-public implementation details, but I would like to explain what I believe is happening internally. To do that, let me show how the value reaches the GeometryReader closure: Step 1 In AttributeGraph, the GeometryReader<...> node and the LazyVStack node appear to be connected as shown below: Step 2 When the LazyVStack node is updated, the layout process appears to follow roughly this logic: `SwiftUICore 'SwiftUI.ForEachState.forEachItem:` n = number of cells to evaluate (initially n == 2) For first n cells: SwiftUICore`SwiftUI.ViewLayoutEngine.sizeThatFits(...) SwiftUI.EstimationCache.add(...) The total size of the lazy stack is then estimated: SwiftUI.LazyStack<...>.sizeThatFits(...): averageCellInfo = EstimationCache.average averageCellInfo.height = (firstCellHeight + secondCellHeight) / 2 totalHeight = firstCellHeight + secondCellHeight + averageCellInfo.height * remainingCells For the sample project, this produces an estimated height of 189. This value then appears to be cached inside a LazyLayoutComputer node. Step 3 When GeometryReader is updated and its closure executes, geo.size.height appears to be resolved from the cached value stored by LazyLayoutComputer. As a result, the reported height is: 189 + 32 (padding) = 221 Step 4 My assumption is that LazyVStack subsequently validates the estimated layout against the actual layout results. It seems to compare: The maximum Y position of the last list's cell The cached sizeThatFits value If those values differ sufficiently, the transaction is not committed and another layout pass is triggered. During a later pass, the real sizes become available and GeometryReader eventually reports the final correct value. If this interpretation is correct, the behavior shown in the logs would be expected: followed later by: Possible solution I could not find a public SwiftUI API that provides an accurate content size during the initial layout pass. I tried the following options: LazyVStack + GeometryReader LazyVStack + ViewThatFits LazyVStack + .scrollBounceBehavior(...) At the same time, SwiftUI itself appears to have information about layout validity. For example, the layout logs contain entries such as: placed(...) -> ... invalid: true This suggests that SwiftUI can determine when an estimated layout result is no longer valid and requires additional layout passes. If SwiftUI knows that the current layout is invalid, is there a way to access this information from within a GeometryReader closure or by some other means? Otherwise, clients may perform layout calculations based on invalid geometry, which can result in a broken dependent layout. Have a good day!
0
2
183
Jul ’26
iOS 26 Beta bug - keyboard toolbar with bottom safe area inset
Hello! I have experienced a weird bug in iOS 26 Beta (8) and previous beta versions. The safe area inset is not correctly aligned with the keyboard toolbar on real devices and simulators. When you focus a new textfield the bottom safe area is correctly placed aligned the keyboard toolbar. On real devices the safe area inset view is covered slightly by the keyboard toolbar, which is even worse than on the simulator. Here's a clip from a simulator: Here's the code that reproduced the bug I experienced in our app. #Preview { NavigationStack { ScrollView { TextField("", text: .constant("")) .padding() .background(Color.secondary) TextField("", text: .constant("")) .padding() .background(Color.green) } .padding() .safeAreaInset(edge: .bottom, content: { Color.red .frame(maxWidth: .infinity) .frame(height: 40) }) .toolbar { ToolbarItem(placement: .keyboard) { Button {} label: { Text("test") } } } } }
4
12
1.6k
Jul ’26
SwiftUI Button with Image view label has smaller hit target
[Also submitted as FB20213961] SwiftUI Button with a label: closure containing only an Image view has a smaller tap target than buttons created with a Label or the convenience initializer. The hit area shrinks to the image bounds instead of preserving the standard minimum tappable size. SCREEN RECORDING On a physical device, the difference is obvious—it’s easy to miss the button. Sometimes it even shows the button-tapped bounce animation but doesn’t trigger the action. SYSTEM INFO Xcode Version 26.0 (17A321) macOS 15.6.1 (24G90) iOS 26.0 (23A340) SAMPLE CODE The following snippet shows the difference in hit targets between the convenience initializer, a Label, and an Image (the latter two in a label: closure). // ✅ Hit target is entire button Button("Button 1", systemImage: "1.square.fill") { print("Button 1 tapped") } // ✅ Hit target is entire button Button { print("Button 2 tapped") } label: { Label("Button 2", systemImage: "2.square.fill") } // ❌ Hit target is smaller than button Button { print("Button 3 tapped") } label: { Image(systemName: "3.square.fill") }
7
4
966
Jul ’26
SwiftUI animation is laggy in NSStatusItem since macOS 26 Tahoe
My app is a bit of a special case and relies on a custom view in a NSStatusItem. I use a NSHostingView and add it as a subview to my NSStatusItem's .button property. Since macOS 26 Tahoe, even simple animations like a .frame change of a Circle won't animate smoothly even though the same SwiftUI animates normally in a WindowGroup. class AppDelegate: NSObject, NSApplicationDelegate { private let statusItem: NSStatusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength) func applicationDidFinishLaunching(_ aNotification: Notification) { let subview = NSHostingView(rootView: AnimationView()) let view = self.statusItem.button view?.addSubview(subview) subview.translatesAutoresizingMaskIntoConstraints = false guard let view = view else { return } NSLayoutConstraint.activate([ subview.centerXAnchor.constraint(equalTo: view.centerXAnchor), subview.centerYAnchor.constraint(equalTo: view.centerYAnchor), subview.widthAnchor.constraint(equalToConstant: 22), subview.heightAnchor.constraint(equalToConstant: 22) ]) } } struct AnimationView: View { @State private var isTapped = false @State private var size: CGSize = .init(width: 4, height: 4) var body: some View { Circle() .fill(.pink) .frame(width: size.width, height: size.height) .frame(width: 20, height: 20) // .frame(maxHeight: .infinity) // .padding(.horizontal, 9) // .frame(height: 22) .contentShape(Rectangle()) // .background(Color.blue.opacity(0.5)) .onTapGesture { withAnimation(.interactiveSpring(response: 0.85, dampingFraction: 0.26, blendDuration: 0.45)) { // withAnimation(.spring()) { if isTapped { size = .init(width: 4, height: 4) } else { size = .init(width: 16, height: 16) } } isTapped.toggle() }} } Example project: https://app.box.com/s/q28upunrgkxyyd97ovslgud9yitqaxfk
1
0
415
Jul ’26
Tab bar animates from first tab to restored selection at launch with .tabBarMinimizeBehavior(.onScrollDown)
[Submitted as FB23998635] On iPhone, applying .tabBarMinimizeBehavior(.onScrollDown) to a SwiftUI TabView causes the native tab bar to briefly show the first declared tab before animating to the already-restored selection during app launch. The selected tab is persisted with @AppStorage and is already restored before the TabView is presented. The sample contains no explicit animations, transactions, navigation containers, loading states, asynchronous work, or post-launch selection changes. Removing .tabBarMinimizeBehavior(.onScrollDown) eliminates the launch animation entirely. Likewise, starting with .tabBarMinimizeBehavior(.never) and changing it to .onScrollDown after a one-second delay also eliminates the issue. The behavior reproduces with three simple tabs and a direct @AppStorage selection binding. ENVIRONMENT • iOS 26 & 27 REPRO STEPS Build and run the attached sample. Select the "Two" tab. Force-quit the app. Relaunch the app. Observe the tab bar during launch. ACTUAL The tab indicator initially appears on "One", the first declared tab, then animates to the correctly restored "Two" selection. EXPECTED The restored tab should be selected and stationary from the first visible frame, with no launch animation. SAMPLE CODE struct ContentView: View { private enum AppTab: String, Hashable { case one case two case three } @AppStorage("selectedGenericTab") private var selectedTab: AppTab = .three var body: some View { TabView(selection: $selectedTab) { Tab("One", systemImage: "1.circle", value: AppTab.one) { ReproTabContent(title: "One", color: .orange) } Tab("Two", systemImage: "2.circle", value: AppTab.two) { ReproTabContent(title: "Two", color: .blue) } Tab("Three", systemImage: "3.circle", value: AppTab.three) { ReproTabContent(title: "Three", color: .green) } } .tabBarMinimizeBehavior(.onScrollDown) } } private struct ReproTabContent: View { let title: String let color: Color var body: some View { ZStack { color.opacity(0.2) .ignoresSafeArea() Text(title) .font(.largeTitle) } } }
2
0
521
Jul ’26
SwiftUI template in Instruments 26.4.1 shows empty channels on iOS 26.4.2 device — even with a minimal TimelineView repro
Hi all, I've hit a reproducible issue where the presence of the SwiftUI instrument in a template prevents any data from being recorded, including from the other instruments in the same template. Removing the SwiftUI instrument immediately restores normal recording. Environment Host: macOS 26.4.1 (25E253), Mac mini Xcode / Instruments 26.4.1 (17E202) Device: iPhone 17, iOS 26.4.2 (23E261) (physical device, USB-attached) Symptom Recording the same app, same device, same session, only varying the template contents: SwiftUI template (as-is) => All lanes empty across the entire recording Same template with the SwiftUI instrument removed => Data collected normally (Time Profiler samples, Hangs, etc.) So it seems not an issue with the SwiftUI lanes specifically being empty — including the SwiftUI instrument appears to silence the entire recording. Steps to reproduce Open Instruments → pick the SwiftUI template (or build a custom template that includes the SwiftUI instrument alongside, e.g., Time Profiler). Target the device, attach to the running app. Record for ~10s, interact with the app. Stop. Result: every lane is empty. Edit the template, remove the SwiftUI instrument, re-record with no other changes. Result: normal data appears in the remaining instruments. Questions Is this a known regression in Instruments 26.4.1 on iOS 26.4.x? Is there a workaround to use the SwiftUI instrument on this OS combo (different Xcode build, runtime flag, entitlement)? Does it work for anyone on iOS 26.4.x + Xcode 26.4.1, or is everyone seeing this? I can file a Feedback if confirmed as a bug — wanted to check here first in case I'm missing a setup step. Thanks!
3
2
1.3k
Jul ’26
How to make a layout like Android's StaggeredGridLayoutManager in SwiftUI
Hi everyone In SwiftUI, are there any good solutions to achieve a layout similar to Android's StaggeredGridLayoutManager for displaying a large amount of data?
Replies
1
Boosts
0
Views
203
Activity
3w
ManipulationComponent + Warning messages in RealityView
Hi guys! I wanted to study this new ManipulationComponent(), but I keep getting a warning that I don’t understand, even in a very simple scenario. i don't have any collisions just binding the Manipulation the warning message is : ** Entity returned from EntityWrapper.makeEntity(context:) was already parented to another entity. This is not supported and may lead to unexpected behavior. SwiftUI adds entities to internally-managed entity hierarchies.** RealityView { content, attachments in if let loadedModel = try? await Entity(named: "cloud_glb", in: realityKitContentBundle) { content.add(loadedModel) loadedModel.components.set(ManipulationComponent()) } Thanks !
Replies
4
Boosts
0
Views
778
Activity
4w
[API] cannot add handler to 3 from 3 - dropping New XCode error!
while resizing screen on mac, start getting this weird error [API] cannot add handler to 3 from 3 - dropping All lazyVgrids fail to show afterwards then app crashes if you keep resizing. This error started with macOS Ventura. Any help much appreciated.
Replies
6
Boosts
5
Views
3.3k
Activity
4w
Pinch gesture not recognized on MTKView when attaching it to a RealityView using a ViewAttachmentComponent in a immersive space
Hello! We are seeing a problem with a SwiftUI view that wraps an MTKView and that MTKView uses gesture recognizers from UIKit. One of those gestures we are using is UIPinchGestureRecognizer. And that gesture isn’t recognized at all when the SwiftUI view is attached to a RealityView using the ViewAttachmentComponent AND the RealityView is being shown in an ImmersiveSpace. If the SwiftUI view is attached to the RealityView using the init that has an attachment closure then pinching works fine there. So this definitely seems like a bug. Here is some code to help you reproduce the problem. Run this on a Vision Pro device. A simple red square will be rendered and if a single tap or pinch gesture is recognized on the red square, it will print to the console. App Code: import SwiftUI @main struct VisionPinchProblemsApp: App { var body: some Scene { WindowGroup { MenuView() } ImmersiveSpace(id: "RedSquare") { RedSquareView() } } } View code: import MetalKit import RealityKit import SwiftUI import UIKit struct MenuView: View { @Environment(\.openImmersiveSpace) private var openImmersiveSpace @Environment(\.dismissImmersiveSpace) private var dismissImmersiveSpace @State private var showImmersiveSpace = false @State private var immersiveSpaceIsOpen = false var body: some View { Form { Toggle("Show red square", isOn: $showImmersiveSpace) .task(id: showImmersiveSpace) { if showImmersiveSpace { await openImmersiveSpace(id: "RedSquare") immersiveSpaceIsOpen = true } else { if immersiveSpaceIsOpen { await dismissImmersiveSpace() immersiveSpaceIsOpen = false } } } } .onDisappear { // Attempt to close the immersive space on the way out. Task { if immersiveSpaceIsOpen { await dismissImmersiveSpace() } } } } } struct RedSquareView: View { let metalViewAttachmentID = "metalID" var body: some View { // Adds SwiftUI view using attachments closure. // Pinching and single taps are recognized here! // RealityView { content, attachments in // if let metalViewEntity = attachments.entity(for: metalViewAttachmentID) { // metalViewEntity.position = [0, 1, -1.25] // content.add(metalViewEntity) // } // } placeholder: { // ProgressView() // } attachments: { // Attachment(id: metalViewAttachmentID) { // MetalView() // } // } // Add SwiftUI view using ViewAttachmentComponent. // Pinching is not recognized here! // Single tapping is recognized ! // Why doesn't the red square show up in the Vision Pro simulator? RealityView { content in let metalViewEntity = Entity() let metalView = MetalView() .frame(width: 500, height: 500) let component = ViewAttachmentComponent(rootView: metalView) metalViewEntity.components.set(component) metalViewEntity.position = [0, 1, -1.25] content.add(metalViewEntity) } placeholder: { ProgressView() } } } struct MetalView: UIViewRepresentable { var device: MTLDevice? init() { self.device = MTLCreateSystemDefaultDevice() } func makeUIView(context: Context) -> MTKView { let mtkView = MTKView() mtkView.device = device mtkView.clearColor = MTLClearColor(red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0) mtkView.delegate = context.coordinator let pinchGesture = UIPinchGestureRecognizer(target: context.coordinator, action: #selector(context.coordinator.handlePinch(_:))) mtkView.addGestureRecognizer(pinchGesture) let tapGesture = UITapGestureRecognizer(target: context.coordinator, action: #selector(context.coordinator.handleTap(_:))) mtkView.addGestureRecognizer(tapGesture) return mtkView } func updateUIView(_ uiView: MTKView, context: Context) { } func makeCoordinator() -> Coordinator { Coordinator(self) } class Coordinator: NSObject, MTKViewDelegate { var parent: MetalView init(_ parent: MetalView) { self.parent = parent } func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) { } func draw(in view: MTKView) { guard let drawable = view.currentDrawable else { return } guard let descriptor = view.currentRenderPassDescriptor else { return } let commandQueue = parent.device?.makeCommandQueue() let commandBuffer = commandQueue?.makeCommandBuffer() let renderEncoder = commandBuffer?.makeRenderCommandEncoder(descriptor: descriptor) renderEncoder?.endEncoding() commandBuffer?.present(drawable) commandBuffer?.commit() } @objc func handlePinch(_ sender: UIPinchGestureRecognizer) { print("Pinch detected") } @objc func handleTap(_ sender: UITapGestureRecognizer) { print("Tap detected") } } }
Replies
1
Boosts
0
Views
689
Activity
4w
ManipulationComponent causes makeUIView(context:) to get called twice
Here I have some demo code that is rendering a cylinder "platter" using RealityKit and there is a red circle rendered on top of it which uses Metal and SwiftUI. When the platter appears you will see in the console that makeUIView(context:) is called twice while it is documented that it will only be called once when the view appears for the first time. So this seems like a bug. If you remove ManipulationComponent from the platter's components you will see that this problem goes away so it seems like that is the cause of the problem. Any insight here would be appreciated! Thank you. Here is what is printed in the console: Entity returned from EntityWrapper.makeEntity(context:) was already parented to another entity. This is not supported and may lead to unexpected behavior. SwiftUI adds entities to internally-managed entity hierarchies. Make UI View! This should be called once. Make UI View! This should be called once. Here is the app code: import SwiftUI @main struct SomeApp: App { var body: some Scene { WindowGroup { ContentView() } ImmersiveSpace(id: "TableTop") { TableTopPlatterView() } } } Here is the view code: import MetalKit import RealityKit import SwiftUI struct ContentView: View { @Environment(\.openImmersiveSpace) private var openImmersiveSpace @Environment(\.dismissImmersiveSpace) private var dismissImmersiveSpace @State private var showImmersiveSpace = false @State private var immersiveSpaceIsOpen = false var body: some View { Form { Toggle("Show table top", isOn: $showImmersiveSpace) .task(id: showImmersiveSpace) { if showImmersiveSpace { await openImmersiveSpace(id: "TableTop") immersiveSpaceIsOpen = true } else { if immersiveSpaceIsOpen { await dismissImmersiveSpace() immersiveSpaceIsOpen = false } } } } .onDisappear { // Attempt to close the immersive space on the way out. Task { if immersiveSpaceIsOpen { await dismissImmersiveSpace() } } } } } struct TableTopPlatterView: View { private var attachmentID: String { "RedCircle" } var body: some View { RealityView { content, attachments in if let redCircleEntity = attachments.entity(for: attachmentID) { // Lays the red circle in the platter. let rotation = Rotation3D(redCircleEntity.orientation) .rotated(by: .init(angle: .degrees(-90), axis: .x)) redCircleEntity.setOrientation(.init(rotation), relativeTo: nil) redCircleEntity.position.y = 0.026 platterEntity.addChild(redCircleEntity) content.add(platterEntity) } } placeholder: { ProgressView() } attachments: { Attachment(id: attachmentID) { MetalView() .clipShape(.circle) } } } /// The platter entity that the red circle lays on top of. private let platterEntity: ModelEntity = { let anchor = AnchorEntity( .plane( .horizontal, classification: .table, minimumBounds: [0.01, 0.01] ) ) let material = SimpleMaterial( color: .lightGray, roughness: 0.5, isMetallic: false ) let platter = ModelEntity( mesh: .generateCylinder(height: 0.05, radius: 0.475), materials: [material] ) platter.generateCollisionShapes(recursive: false) let components: [any Component] = [ InputTargetComponent(), GroundingShadowComponent(castsShadow: true), ManipulationComponent() // MARK: This is causing makeUIView to get called twice! ] platter.components.set(components) // Placed closer to the user when booted up. platter.position = [0, 1, -1.25] anchor.addChild(platter) return platter }() } // Metal view that renders a red square. struct MetalView: UIViewRepresentable { var device: MTLDevice? init() { self.device = MTLCreateSystemDefaultDevice() } func makeUIView(context: Context) -> MTKView { print("Make UI View! This should be called once.") let mtkView = MTKView() mtkView.device = device mtkView.clearColor = MTLClearColor(red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0) mtkView.delegate = context.coordinator return mtkView } func updateUIView(_ uiView: MTKView, context: Context) { } func makeCoordinator() -> Coordinator { Coordinator(self) } class Coordinator: NSObject, MTKViewDelegate { var metalView: MetalView init(_ metalView: MetalView) { self.metalView = metalView } func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) { } func draw(in view: MTKView) { guard let drawable = view.currentDrawable else { return } guard let descriptor = view.currentRenderPassDescriptor else { return } let commandQueue = metalView.device?.makeCommandQueue() let commandBuffer = commandQueue?.makeCommandBuffer() let renderEncoder = commandBuffer?.makeRenderCommandEncoder(descriptor: descriptor) renderEncoder?.endEncoding() commandBuffer?.present(drawable) commandBuffer?.commit() } } }
Replies
3
Boosts
0
Views
1.5k
Activity
4w
Intermittent UIHostingController layout regression after app launch on iPadOS 27 beta 4
On iPadOS 27.0 beta 4, SwiftUI views hosted inside UIKit-managed containers/overlays can be laid out incorrectly. The same app and same code work correctly on iPadOS 26.x release versions. One visible example is the app’s About dialog. The dialog is implemented as a UIKit UIViewController presented with UIModalPresentationFormSheet. Inside that controller, a UIHostingController is added as a child view controller, and the hosting controller’s view is constrained to all four edges of the parent view. The SwiftUI root view is a VStack containing the app icon, app name, version, text, links, and buttons. Expected behavior: The SwiftUI content should be vertically centered inside the form sheet. The app icon should appear above the app title, followed by the version, text, links, and buttons. This is the behavior on iPadOS 26.x. Actual behavior on iPadOS 27.0 beta 4: When the About dialog is opened immediately after launching the app, this issue occurs intermittently. The form sheet itself appears, but the SwiftUI content is shifted upward. The app icon is missing or clipped, the title starts too close to the top edge of the sheet, and a large empty area appears at the bottom of the sheet. This is not limited to the About dialog. Similar layout issues can also appear in other SwiftUI-hosted UI surfaces in the app. The issue also appears to be affected by system-level UI changes: If I put the app into windowed mode and resize the whole app window, the layout inside the dialog recovers and becomes correct. The issue only occurs with some probability the first time this dialog is opened after launching the app. After the layout is restored by resizing the app window, the issue does not appear again as long as the app process is not terminated. This suggests the problem may be related to an incorrect initial layout pass, cached geometry, trait/safe-area propagation, or UIHostingController layout invalidation after the app/window scene is first created. Environment: Device: iPad OS with issue: iPadOS 27.0 beta 4 OS without issue: iPadOS 26.x release App: VoidLink - Extreme UI stack: UIKit containers/overlays hosting SwiftUI through UIHostingController Relevant code: AboutView.swift: https://github.com/The-Fried-Fish/VoidLink-previously-moonlight-zwm/blob/Integration/VoidLink/AboutView.swift AboutViewController.swift: https://github.com/The-Fried-Fish/VoidLink-previously-moonlight-zwm/blob/Integration/VoidLink/AboutViewController.swift Attachments: IMG_0291.PNG: correct layout on iPadOS 26.x IMG_0292.PNG: incorrect layout on iPadOS 27.0 beta 4
Replies
1
Boosts
0
Views
440
Activity
4w
Is it normal that mounted @Query cause every object of that type to re-render, even after unrelated saves?
I have a SwiftUI + SwiftData app where scrolling became very slow, and I've traced it to something about @Query I didn't expect. The app was running save for each lazy list item appearing in viewport (a separate bug, but it did highlight the problem), which made the whole list re-render on each save. After tracing why this happens I have discovered that it is because another model watched in the list parent is invalidated after every save, and the problem was that this another model has @Query in a separate view (sidebar), removing that @Query fixed the problem. Here is a minimal reproduction of this https://github.com/aytigra/QueryRefaultRepro I wonder if it is a bug or an expected behavior?
Replies
0
Boosts
0
Views
528
Activity
Aug ’26
How to achieve the UIEditMenuInteraction (?) for Link Preview used in iOS 27 Messages
I've been using the iOS 27 beta and noticed in Messages app that the link presentation is now customisable. Upon tapping on the link it opens (what I assume is) an edit menu interaction, which allows customising which metadata is shown. I've seen some links offer more customisation than others, presumably based on available metadata. There's also an option in the menu to convert to a text link, and when highlighting a link in text there's an option to "show link preview" which converts it to an LPLinkView. I've been wondering for a while now if it was possible to add a similar feature to my own app, allowing the user more control over the link previews. How can I achieve similar? Especially "Customise Link" sheet seen in the middle two screenshots?
Replies
0
Boosts
0
Views
666
Activity
Aug ’26
Adding a ManipulationComponent detaches the whole RealityView content hierarchy on visionOS 27
On visionOS 27 (27.0 Seed 4, 24M5326g), adding a ManipulationComponent to a single entity makes RealityKit remove the RealityView's ENTIRE content hierarchy from the scene and re-add it about 30ms later. Not just the manipulated entity — everything. Same app binary on visionOS 26: never happens. FB24092291. Bisected on the same build and device: two entities, neither with manipulation -> no detach add one entity with a ManipulationComponent -> detach, every time same entity, manipulation swapped for my own drag/rotate/ scale gesture handling -> no detach Everything else is identical between the last two cases — same entity, same collision shape, same InputTargetComponent, same ViewAttachmentComponent, same scene, same view hierarchy. The only variable is whether the component is installed. Setup: an immersive space containing a SwiftUI RealityView. One root entity added to the RealityViewContent in the make closure; all app content built as its descendants. The detach is brief but not harmless: every entity in the hierarchy gets SceneEvents.WillRemoveEntity and then DidAddEntity, so anything keyed to those events runs a full teardown and re-add for content that was never meant to go anywhere. In my app that cascade is what makes the scene visibly empty and rebuild. Nothing in my code removes it. Stack captured from a WillRemoveEntity subscription on the root — frame 0 is my callback, everything above it is framework: SwiftUI (AttributeGraph StatefulRule.withObservation ...) -> RealityFoundation -> CoreRE -> entity removal Ruled out before landing on the component: make runs exactly once, the root re-enters a scene with the same ObjectIdentifier (not a re-host), the hosting controller is never deallocated, there is exactly one RealityViewContent.add(_:) in the whole app, and with Self._printChanges() in the view's body the detach happens in an update pass where the body is not re-evaluated at all. Also ruled out: transient overlay UI (suppressed entirely, still detached) and entity count. Workaround if this is biting you: skip ManipulationComponent on 27 and handle drag/rotate/scale yourself. That is what I am doing for now. If you are debugging something similar: SceneEvents.WillRemoveEntity also fires when an entity merely leaves a scene, so a teardown-looking log is not proof anything was destroyed. Check whether make ran twice and whether the scene identity changed before suspecting your own code.
Replies
1
Boosts
2
Views
1.2k
Activity
Aug ’26
Extract Subview option missing in Xcode 26
Hi everyone, I recently updated to Xcode 26.0 and noticed that the “Extract Subview” refactoring option seems to be missing. Now, in Xcode 26, the only options I see under Editor -> Refactor -> are: Extract to Selection File Extract to Method Extract to Variable Extract All Occurrences But there’s no Extract Subview as there was before. Was Extract Subview intentionally removed in Xcode 26? Or is it hidden behind a new menu location or renamed?
Replies
6
Boosts
8
Views
1.1k
Activity
Aug ’26
SwiftUI: List cells flicker during scroll when .searchPresentationToolbarBehavior(.avoidHidingContent) is used with searchable modifier inside a sheet (iOS 26)
Description When a searchable List is presented inside a sheet and uses .searchPresentationToolbarBehavior(.avoidHidingContent) to keep the navigation bar visible during search, the list cells flicker/blink under the keyboard This reproduces with public SwiftUI API only — no UIKit, no appearance-proxy customization. Steps to Reproduce Create a new iOS App (SwiftUI) project (deployment target iOS 17.1+). Replace the generated App file with the sample code below. Run on iOS 26 (reproduces on both device and Simulator). Tap "Open transfer methods" to present the sheet. Tap the search field so the keyboard is shown (search becomes active). Scroll the list down, then up into the top bounce (overscroll) Observe the cells under the keyboard. Expected Cells scroll smoothly under the keyboard; no flicker. Actual Cells under the keyboard flicker/blink during top overscroll. See the attachments with shots of the process: 1. screen before a blink, 2. Screen in the moment of blinking Sample Code import SwiftUI @main struct FlickerReproApp: App { var body: some Scene { WindowGroup { RootView() } } } struct RootView: View { @State private var isSheetPresented = false var body: some View { Button("Open transfer methods") { isSheetPresented = true } .sheet(isPresented: $isSheetPresented) { NavigationStack { SimpleSearchScreen() } } } } struct SimpleSearchScreen: View { @State private var searchText = "" private let items = (0..<40).map { "Recipient \($0)" } private var filteredItems: [String] { searchText.isEmpty ? items : items.filter { $0.localizedCaseInsensitiveContains(searchText) } } var body: some View { List { ForEach(filteredItems, id: \.self) { item in Text(item) } } .listStyle(.plain) .navigationTitle("Transfer methods") .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .topBarLeading) { Button("Close", systemImage: "xmark") {} } } .searchable(text: $searchText, placement: .automatic, prompt: "Search") // Remove the line below -> flicker disappears, but the navigation bar // (title + toolbar items) then hides during search, which we need to keep. .searchPresentationToolbarBehavior(.avoidHidingContent) } } Notes / What was ruled out Removing .searchPresentationToolbarBehavior(.avoidHidingContent) eliminates the flicker — but then the navigation bar hides during search, which is exactly the behavior the modifier is meant to prevent. Presenting the same screen NOT inside a sheet does not flicker — the sheet presentation is required to reproduce. Independent of app-level customization: reproduces with no UINavigationBar/UITabBar/UISearchBar appearance proxies and no UIKit. Also tried, did NOT help: .scrollEdgeEffectStyle(.hard, for: .all), .toolbarBackground(.hidden, for: .navigationBar), keeping the bar visible via UISearchController.hidesNavigationBarDuringPresentation = false instead, .geometryGroup() on rows, .scrollDismissesKeyboard(.never), removing safe area insets.
Replies
0
Boosts
0
Views
210
Activity
Jul ’26
Animations become choppy in NSStatusItem when other window contains ScrollView
Feedback ID: FB23984230 This issue for some reason does not happen on my external 165 Hz display, but happens on my built-in MacBook Air display (60Hz). See attached example project: Contains two parts NSStatusItem with animation that triggers during ‘.onTapGesture()’ a window with ContentView that contains List and ScrollView while any ScrollView / List is in the view hierarchy in ContentView, triggering an animation in NSStatusItem (on a built-in MacBook display) is very choppy once removing ScrollView / List from view hierarchy from ContentView, animation in NSStatusItem is very smooth Project: Link macOS 26.5.2 (25F84)
Replies
1
Boosts
0
Views
519
Activity
Jul ’26
Looking on feedback on the UI Design
Hi everyone, I'm developing a simple iOS app that solves quadratic equations using SwiftUI. I've attached a screenshot of the current interface. I'd appreciate feedback on the UI and user experience, especially from the perspective of Apple's Human Interface Guidelines. Thanks!
Topic: Design SubTopic: General Tags:
Replies
1
Boosts
0
Views
887
Activity
Jul ’26
Unexpected behavior in the interaction between LazyVStack and GeometryReader
Hello! I'd like to share a problem and its potential solution. Steps to reproduce: The issue can be reproduced with the following minimal example: struct TestConditionalScrollView: View { var body: some View { ConditionalScrollView { LazyVStack(spacing: 16) { Text("Text 1") .frame(height: 20) Text("Text 2") .frame(height: 30) Text("Text 3") .frame(height: 40) Text("Text 4") .frame(height: 500) Text("Text 5") .frame(height: 400) } .padding() } } } struct ConditionalScrollView<Content: View>: View { let content: Content init(@ViewBuilder content: () -> Content) { self.content = content() } @State private var contentHeight: CGFloat = 0 var body: some View { GeometryReader { geo in _ = print("Height: \(contentHeight)") return Group { if contentHeight > geo.size.height { ScrollView { measuredContent } } else { measuredContent } } } } private var measuredContent: some View { content .background( GeometryReader { geo in Color.clear .preference( key: ContentHeightKey.self, value: geo.size.height ) } ) .onPreferenceChange(ContentHeightKey.self) { contentHeight = $0 } } } struct ContentHeightKey: PreferenceKey { static var defaultValue: CGFloat = 0 static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) { value = max(value, nextValue()) } } If we hit the breakpoint on the following line: _ = print("Height: (contentHeight)") the output looks like this: Problems observed Different values are reported, and it is unclear where those values originate from. The selected execution branch appears to change multiple times during the layout process. Possible reason As Rens Breur mentioned in the WWDC26 session "Dive into lazy stacks and scrolling with SwiftUI", LazyVStack relies on estimated layout information during certain phases of the layout process. I do not want to rely on or investigate SwiftUI's non-public implementation details, but I would like to explain what I believe is happening internally. To do that, let me show how the value reaches the GeometryReader closure: Step 1 In AttributeGraph, the GeometryReader<...> node and the LazyVStack node appear to be connected as shown below: Step 2 When the LazyVStack node is updated, the layout process appears to follow roughly this logic: `SwiftUICore 'SwiftUI.ForEachState.forEachItem:` n = number of cells to evaluate (initially n == 2) For first n cells: SwiftUICore`SwiftUI.ViewLayoutEngine.sizeThatFits(...) SwiftUI.EstimationCache.add(...) The total size of the lazy stack is then estimated: SwiftUI.LazyStack<...>.sizeThatFits(...): averageCellInfo = EstimationCache.average averageCellInfo.height = (firstCellHeight + secondCellHeight) / 2 totalHeight = firstCellHeight + secondCellHeight + averageCellInfo.height * remainingCells For the sample project, this produces an estimated height of 189. This value then appears to be cached inside a LazyLayoutComputer node. Step 3 When GeometryReader is updated and its closure executes, geo.size.height appears to be resolved from the cached value stored by LazyLayoutComputer. As a result, the reported height is: 189 + 32 (padding) = 221 Step 4 My assumption is that LazyVStack subsequently validates the estimated layout against the actual layout results. It seems to compare: The maximum Y position of the last list's cell The cached sizeThatFits value If those values differ sufficiently, the transaction is not committed and another layout pass is triggered. During a later pass, the real sizes become available and GeometryReader eventually reports the final correct value. If this interpretation is correct, the behavior shown in the logs would be expected: followed later by: Possible solution I could not find a public SwiftUI API that provides an accurate content size during the initial layout pass. I tried the following options: LazyVStack + GeometryReader LazyVStack + ViewThatFits LazyVStack + .scrollBounceBehavior(...) At the same time, SwiftUI itself appears to have information about layout validity. For example, the layout logs contain entries such as: placed(...) -> ... invalid: true This suggests that SwiftUI can determine when an estimated layout result is no longer valid and requires additional layout passes. If SwiftUI knows that the current layout is invalid, is there a way to access this information from within a GeometryReader closure or by some other means? Otherwise, clients may perform layout calculations based on invalid geometry, which can result in a broken dependent layout. Have a good day!
Replies
0
Boosts
2
Views
183
Activity
Jul ’26
iOS 26 Beta bug - keyboard toolbar with bottom safe area inset
Hello! I have experienced a weird bug in iOS 26 Beta (8) and previous beta versions. The safe area inset is not correctly aligned with the keyboard toolbar on real devices and simulators. When you focus a new textfield the bottom safe area is correctly placed aligned the keyboard toolbar. On real devices the safe area inset view is covered slightly by the keyboard toolbar, which is even worse than on the simulator. Here's a clip from a simulator: Here's the code that reproduced the bug I experienced in our app. #Preview { NavigationStack { ScrollView { TextField("", text: .constant("")) .padding() .background(Color.secondary) TextField("", text: .constant("")) .padding() .background(Color.green) } .padding() .safeAreaInset(edge: .bottom, content: { Color.red .frame(maxWidth: .infinity) .frame(height: 40) }) .toolbar { ToolbarItem(placement: .keyboard) { Button {} label: { Text("test") } } } } }
Replies
4
Boosts
12
Views
1.6k
Activity
Jul ’26
Accessing SwiftData document package?
I would very much like to store some additional data in my SwiftData document package, outside of SwiftData. Metadata about the document that doesn't lend itself well to the underlying RDBMS nature of SwiftData. Is that possible?
Replies
1
Boosts
1
Views
1.1k
Activity
Jul ’26
SwiftUI Button with Image view label has smaller hit target
[Also submitted as FB20213961] SwiftUI Button with a label: closure containing only an Image view has a smaller tap target than buttons created with a Label or the convenience initializer. The hit area shrinks to the image bounds instead of preserving the standard minimum tappable size. SCREEN RECORDING On a physical device, the difference is obvious—it’s easy to miss the button. Sometimes it even shows the button-tapped bounce animation but doesn’t trigger the action. SYSTEM INFO Xcode Version 26.0 (17A321) macOS 15.6.1 (24G90) iOS 26.0 (23A340) SAMPLE CODE The following snippet shows the difference in hit targets between the convenience initializer, a Label, and an Image (the latter two in a label: closure). // ✅ Hit target is entire button Button("Button 1", systemImage: "1.square.fill") { print("Button 1 tapped") } // ✅ Hit target is entire button Button { print("Button 2 tapped") } label: { Label("Button 2", systemImage: "2.square.fill") } // ❌ Hit target is smaller than button Button { print("Button 3 tapped") } label: { Image(systemName: "3.square.fill") }
Replies
7
Boosts
4
Views
966
Activity
Jul ’26
SwiftUI animation is laggy in NSStatusItem since macOS 26 Tahoe
My app is a bit of a special case and relies on a custom view in a NSStatusItem. I use a NSHostingView and add it as a subview to my NSStatusItem's .button property. Since macOS 26 Tahoe, even simple animations like a .frame change of a Circle won't animate smoothly even though the same SwiftUI animates normally in a WindowGroup. class AppDelegate: NSObject, NSApplicationDelegate { private let statusItem: NSStatusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength) func applicationDidFinishLaunching(_ aNotification: Notification) { let subview = NSHostingView(rootView: AnimationView()) let view = self.statusItem.button view?.addSubview(subview) subview.translatesAutoresizingMaskIntoConstraints = false guard let view = view else { return } NSLayoutConstraint.activate([ subview.centerXAnchor.constraint(equalTo: view.centerXAnchor), subview.centerYAnchor.constraint(equalTo: view.centerYAnchor), subview.widthAnchor.constraint(equalToConstant: 22), subview.heightAnchor.constraint(equalToConstant: 22) ]) } } struct AnimationView: View { @State private var isTapped = false @State private var size: CGSize = .init(width: 4, height: 4) var body: some View { Circle() .fill(.pink) .frame(width: size.width, height: size.height) .frame(width: 20, height: 20) // .frame(maxHeight: .infinity) // .padding(.horizontal, 9) // .frame(height: 22) .contentShape(Rectangle()) // .background(Color.blue.opacity(0.5)) .onTapGesture { withAnimation(.interactiveSpring(response: 0.85, dampingFraction: 0.26, blendDuration: 0.45)) { // withAnimation(.spring()) { if isTapped { size = .init(width: 4, height: 4) } else { size = .init(width: 16, height: 16) } } isTapped.toggle() }} } Example project: https://app.box.com/s/q28upunrgkxyyd97ovslgud9yitqaxfk
Replies
1
Boosts
0
Views
415
Activity
Jul ’26
Tab bar animates from first tab to restored selection at launch with .tabBarMinimizeBehavior(.onScrollDown)
[Submitted as FB23998635] On iPhone, applying .tabBarMinimizeBehavior(.onScrollDown) to a SwiftUI TabView causes the native tab bar to briefly show the first declared tab before animating to the already-restored selection during app launch. The selected tab is persisted with @AppStorage and is already restored before the TabView is presented. The sample contains no explicit animations, transactions, navigation containers, loading states, asynchronous work, or post-launch selection changes. Removing .tabBarMinimizeBehavior(.onScrollDown) eliminates the launch animation entirely. Likewise, starting with .tabBarMinimizeBehavior(.never) and changing it to .onScrollDown after a one-second delay also eliminates the issue. The behavior reproduces with three simple tabs and a direct @AppStorage selection binding. ENVIRONMENT • iOS 26 & 27 REPRO STEPS Build and run the attached sample. Select the "Two" tab. Force-quit the app. Relaunch the app. Observe the tab bar during launch. ACTUAL The tab indicator initially appears on "One", the first declared tab, then animates to the correctly restored "Two" selection. EXPECTED The restored tab should be selected and stationary from the first visible frame, with no launch animation. SAMPLE CODE struct ContentView: View { private enum AppTab: String, Hashable { case one case two case three } @AppStorage("selectedGenericTab") private var selectedTab: AppTab = .three var body: some View { TabView(selection: $selectedTab) { Tab("One", systemImage: "1.circle", value: AppTab.one) { ReproTabContent(title: "One", color: .orange) } Tab("Two", systemImage: "2.circle", value: AppTab.two) { ReproTabContent(title: "Two", color: .blue) } Tab("Three", systemImage: "3.circle", value: AppTab.three) { ReproTabContent(title: "Three", color: .green) } } .tabBarMinimizeBehavior(.onScrollDown) } } private struct ReproTabContent: View { let title: String let color: Color var body: some View { ZStack { color.opacity(0.2) .ignoresSafeArea() Text(title) .font(.largeTitle) } } }
Replies
2
Boosts
0
Views
521
Activity
Jul ’26
SwiftUI template in Instruments 26.4.1 shows empty channels on iOS 26.4.2 device — even with a minimal TimelineView repro
Hi all, I've hit a reproducible issue where the presence of the SwiftUI instrument in a template prevents any data from being recorded, including from the other instruments in the same template. Removing the SwiftUI instrument immediately restores normal recording. Environment Host: macOS 26.4.1 (25E253), Mac mini Xcode / Instruments 26.4.1 (17E202) Device: iPhone 17, iOS 26.4.2 (23E261) (physical device, USB-attached) Symptom Recording the same app, same device, same session, only varying the template contents: SwiftUI template (as-is) => All lanes empty across the entire recording Same template with the SwiftUI instrument removed => Data collected normally (Time Profiler samples, Hangs, etc.) So it seems not an issue with the SwiftUI lanes specifically being empty — including the SwiftUI instrument appears to silence the entire recording. Steps to reproduce Open Instruments → pick the SwiftUI template (or build a custom template that includes the SwiftUI instrument alongside, e.g., Time Profiler). Target the device, attach to the running app. Record for ~10s, interact with the app. Stop. Result: every lane is empty. Edit the template, remove the SwiftUI instrument, re-record with no other changes. Result: normal data appears in the remaining instruments. Questions Is this a known regression in Instruments 26.4.1 on iOS 26.4.x? Is there a workaround to use the SwiftUI instrument on this OS combo (different Xcode build, runtime flag, entitlement)? Does it work for anyone on iOS 26.4.x + Xcode 26.4.1, or is everyone seeing this? I can file a Feedback if confirmed as a bug — wanted to check here first in case I'm missing a setup step. Thanks!
Replies
3
Boosts
2
Views
1.3k
Activity
Jul ’26