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

Programatically adding to a TextField and moving the TextSelection point in SwiftUI
Hi! I am trying to create a simple SwiftUI TextField, with an external button to add text to the field at the current insertion point (the cursor in the TextField). When I add the text, the cursor (I-Beam) remains at the original insertion point, so I want it to move over to the end of what I added. The trouble is, it sometimes moves further forward or to the end (visibly) but works as if it is still at the point I moved it to. This seems to possibly be due to emojis in the TextField (because, I assume, they are composed of more bytes). Further, sometimes the addition of the text can cause an emoji to appear unexpectedly, I assume because it is combining the bytes in an odd way. So moving the cursor seems to sometimes introduce weird behaviour. This comes from a much larger project, but I have distilled this down to the smallest example project I could. And I have a video to show how it behaves. Here's the main part of the code, and I'll attach an Xcode project: import SwiftUI struct ContentView: View { @State private var text: String = "abcdef🧁🧁🧁🧁ghijkl" @State private var selectedText: TextSelection? var body: some View { VStack { TextField("", text: $text, selection: $selectedText) .font(.title) Button("Add Z at Insertion Point in TextField") { // Get indices of any selection in the text field let from: String.Index, to: String.Index if let selectedText = selectedText { let indices = selectedText.indices switch indices { case .selection(let range): from = range.lowerBound to = range.upperBound case .multiSelection(let rangeSet): from = rangeSet.ranges.first!.lowerBound to = rangeSet.ranges.first!.upperBound default: from = self.text.endIndex to = self.text.endIndex } } else { from = self.text.endIndex to = self.text.endIndex } guard from <= to && from <= self.text.endIndex else { return } // Insert and update the cursor position self.text.replaceSubrange(from..<to, with: "Z") // Move cursor after the inserted character let newIndex = self.text.index(after: from) selectedText = TextSelection(insertionPoint: newIndex) } } .padding() } } STEPS TO REPRODUCE Run the app. Also view the video as it shows the steps. Put insertion point between c and d. Press the "Add Z" button. Note that Z is placed between c and d. This is great. Put insertion point between h and i. Press the "Add Z" button. Note that Z is placed between h and i. BUT, the insertion point I-beam moves to the end of the string. Press the "Add Z" button again. Z is added where you would have expected based on where the TextSelection insertion point was put, but the flashing I-Beam is still at the end. Press the "Add Z" button again. Same issue. insertion point is being shown at end, but to the button it is between Z and i. OF NOTE, if you use the keyboard and press delete, it deletes from end (where the I-beam is). Now put insertion point between the 4 cupcakes. Press "Add Z" two times. It behaves correctly. Press "Add Z" a third time. It adds a fairy emoji. So, any idea what I am doing wrong? I thought it might be an issue requiring me to update in a background thread, but I tried that, even delaying the update in the thread, but the issue remains. Thanks in advance. Here's a video: https://curmi.name/temp/SimpleTextField%20showing%20issues.mp4 And if it helps, here is the Xcode project: https://curmi.name/temp/SimpleTextfield.zip
1
0
221
13m
SwiftUI retain cycle with .searchable
I'm trying to see what I am doing wrong ... So I created this simple app where the stateobject Retainer won't get deallocated when I pop the view off the stack: import SwiftUI struct ContentView: View { var body: some View { NavigationView { List { NavigationLink("To Retain Cycle") { RetainCycleView() } } .navigationTitle("Retain Cycle Demo") } .navigationViewStyle(.stack) } } struct RetainCycleView: View { @StateObject var model = Retainer() // @State var enteredText: String = "" var body: some View { VStack(alignment: .leading, spacing: 4) { Text("Navigate back to the previous view.") Text("You will see that 'Retainer' was NOT deallocated.") Text("(it's deinit function prints deallocing Retainer)") .font(.callout) } .padding() .searchable(text: $model.enteredText) // ^---- retain cycle // .searchable(text: $enteredText) // ^---- no retain cycle when using the @State var } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } } class Retainer: ObservableObject { @Published var enteredText: String = "" init() { print("instantiated Retainer") } deinit { print("deallocing Retainer") } } I filed feedback but I am not entirely sure that this isn't me making some mistake ... Please help me
4
1
1.1k
51m
Glass effect interactive effect issue when used with concentric shapes
Using .glassEffect(.clear.interactive(), in: shape), where shape is some concentric shape that adapts to corner radius of the device, results in appearing of highlighted capsule shape. Code to reproduce this behavior import SwiftUI struct HelloLiquidGlass: View { var body: some View { if #available(iOS 26.0, *) { Text("Hello, World!") .frame(maxWidth: .infinity, maxHeight: .infinity) // .glassEffect(.clear.interactive(), in: .rect(corners: .concentric)) // .glassEffect(.clear.interactive(), in: ConcentricRectangle(uniformTopCorners: .fixed(80))) .padding(36) .ignoresSafeArea() .preferredColorScheme(.dark) } } } #Preview { HelloLiquidGlass() } Either of both commented-out modifiers produces the same result when user interacts with Liquid Glass pane (revealing of capsule shape that is not relative to actual shape of liquid glass effect modifier) iOS 26.5 (23F73) SDK + iOS 26.5 (23F77) Simulator
1
0
123
20h
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!
0
0
15
1d
Overlay window above all windows, even when moving spaces
Hi! I would like to overlay a macOS application window above all windows, even when moving spaces or moving to a fullscreen app, similar in function to the DropOver app for macOS. What I have tried: Overwriting the default app delegate in SwiftUI and creating the NSWindow myself. Then setting the window.collectionBehavior to [.canJoinAllApplications, .canJoinAllSpaces, .fullScreenAuxiliary] and the window.level to .floating or .statusBar. This works for moving between Spaces, but still does not display above apps in fullscreen mode. I also registered a notification observer for NSWorkspace.activeSpaceDidChangeNotification, where I call window.orderFrontRegardless() to always have my window frontmost. Still not displaying above fullscreen apps. What am I missing to make this work? Best regards
2
0
89
1d
Bottom sheet does not adapt to screen rotation after presenting NFC Reader session
Hello I am implementing an NFC Reader session in my app, and presenting the bottom sheet works completely fine. However, I am facing an issue with screen rotation. If I rotate the device while the NFC bottom sheet is active, the sheet does not adapt or resize according to the new screen orientation. Is there a way to force the NFC bottom sheet to update its layout or fix this rotation issue?Any help or workarounds would be greatly appreciated. Thanks!
1
0
38
1d
UI layout overlaps in bottom sheet upon screen rotation during NFC Reader session
Hello I am experiencing a layout issue where UI components overlap in a bottom sheet during screen rotation while an NFC Reader session is active. The NFC Reader session initializes and displays the bottom sheet properly. However, if the device is rotated while this sheet is on screen, the layout breaks, and elements appear overlapped. I have attached a sample image demonstrating this rendering issue. Is there a known workaround to fix this layout distortion or force a proper layout update during an active NFC session?Any insights or suggestions would be greatly appreciated. Thanks!
1
0
24
2d
tvOS SwiftUI - Siri "On-Screen Actions" breaks lazy lists
Hey team, This is a weird one, when Siri + "On-Screen Actions" are enabled in the Apple TV settings (General Settings), LazyVStack experiences severe main-thread hangs and very high CPU usage. The issue only occurs when Siri + On-Screen Actions are enabled in Apple TV device settings (General Settings). Disabling On-Screen Actions completely resolves the issue. The issue has been verified on multiple Apple TV devices, running tvOS 26.3, 26.4 and 26.5. The behavior was not observed in Simulators, only on an actual device. This seems to be caused by some type of accessibility overlap with SwiftUI. Running the below code with "On-Screen Actions" enabled in the Siri settings will demonstrate a complete hang of the CPU at 100% struct TestData { let id = UUID() var title: String } struct TestView: View { @State private var test: [TestData] = [] var body: some View { ScrollView { LazyVStack { ForEach(self.test, id: \.id) { data in Button { } label: { Text(data.title) } } } } .task { var arr: [TestData] = [] for _ in 0..<30000 { let data = TestData(title: "Title") arr.append(data) } self.test = arr } } } Already submited a bug report FB22758928
0
0
112
3d
SF Symbols .replace animation is partially missing
In SF Symbols 7, I'm observing a discrepancy between the .replace animation previewed in the SF Symbols app and the result when using the code template the app provides. Expected behavior: When previewing the .replace animation in the SF Symbols app (with Magic Replace preferred), the transition looks like: Actual behavior: When I implement the animation using the exact code template generated by the SF Symbols app, the result looks like: My code: struct PlaygroundSwiftUIView: View { @State var name = "folder.circle" var body: some View { Image(systemName: name) .font(.system(size: 60)) .contentTransition(.symbolEffect(.replace)) .onTapGesture { name = "tree" } } } #Preview { PlaygroundSwiftUIView() } The animation rendered in my app does not match the preview shown in the SF Symbols app. The drawOff animation is partially missing. Is there something missing from the code template, or is there an additional configuration step required to achieve the correct Replace effect? or is this a bug?
2
0
175
4d
SwiftUI + AppKit context menu wedges window-level mouse events when the menu's anchor view is deleted by the menu's own action
I'm on macOS 26.4 (SwiftUI + SwiftData + AppKit bridging). I have a SwiftUI canvas where each child view (a card) carries a per-card AppKit context menu via an NSViewRepresentable overlay (a small NSView subclass that overrides menu(for:) and returns an NSMenu whose items invoke a Swift closure). The closure for the "Delete" item removes the underlying model object. SwiftData mutates → SwiftUI re-emits the canvas → the card containing the menu's anchor NSView unmounts → that anchor NSView is removed from the AppKit view hierarchy. After this happens, the entire window's SwiftUI gestures stop receiving mouse events: pan, zoom, taps on a separate background Color view all go silent. The window's first responder ends up at the NSWindow itself. The wedge persists across switching to a sibling SwiftUI view in the same window (different SwiftData root), so the bad state is at the window/event-routing level, not in any one SwiftUI subtree. The wedge is reliably cleared by another right-click that successfully shows + dismisses any menu — strongly suggesting AppKit's menu/event-tracking cleanup is left in a half-finished state because the anchor view went away during the cleanup window. I've already verified: Switching from explicit NSMenu.popUpContextMenu(_:with:for:) to overriding menu(for:) does not change the behavior. Deferring the model deletion via DispatchQueue.main.async, asyncAfter (30 ms), or RunLoop.main.perform(inModes: [.default]) either doesn't help or only avoids the wedge if the delay is long enough to be perceptible (seconds), implying the cleanup window is event-driven, not time-driven. Triggering the same deletion from a SwiftUI Button (no AppKit menu involved) never wedges. My current understanding is that the standard AppKit pattern (e.g., NSTableView, NSOutlineView) attaches the context menu to a stable parent view, never to the per-row view itself, precisely so the menu's anchor outlives any single row's deletion. Restructuring my code so a single canvas-level AppKitContextMenuRegion returns the appropriate menu via hit-testing in menu(for:) should avoid the issue, but I'd like to confirm: Is the assumption that an NSMenu's anchor view must outlive the menu's tracking-end cleanup documented anywhere, or is it implicit? Is there a supported way to safely use a per-row anchor view that gets deleted by the menu's own action, or is the canvas-level / parent-level menu attachment the only correct pattern? 3. Are there any post-tracking notifications or APIs that guarantee AppKit's cleanup is complete before the anchor view is allowed to be torn down (so a per-row anchor could be made safe with the right ordering)? Thanks!
0
0
277
5d
Why doesn't .glassEffect tint render on a Menu in an iOS 26 toolbar?
This is what I am trying to achieve (from the Phone app, similar one is also in Photos) I have a standard SwiftUI Menu in a toolbar with a glass tint applied to indicate the filter is active: Menu { // …filter options } label: { Label("Filter", systemImage: "line.3.horizontal.decrease") } .glassEffect(.regular.tint(.accentColor).interactive()) The glass effect doesn't render at all, no tint. The button looks completely unstyled. If I switch the label from Label to Image, the glass renders, but as a stretched oblong pill. I have tried several other combinations too: Also in the Apple's version during hover (iPad) the highlight aligns with the tint itself (see second image above) rather than outside it like in example 3 from the list above: Is there a way to get a Menu's trigger inset tint to look as in the Phone app example?
0
0
52
1w
'NSKeyedUnarchiveFromData' should not be used to for un-archiving and will be removed in a future release
Hi, Overview: I get the following error when trying to save / read from SwiftData It happens when I try to save color to SwiftData (code below) Error 'NSKeyedUnarchiveFromData' should not be used to for un-archiving and will be removed in a future release Questions How can I resolve the error? I am not directly using data, I am using just Float values, swift types. Why am I getting this error? Is there a way to add a breakpoint to stop at the exact type causing the error? (Symbolic breakpoint doesn't seem to help) Or is the below code ok and not responsible for the error? Code import SwiftUI nonisolated struct ColorRepresentation: Codable { let red: Float let green: Float let blue: Float let opacity: Float init(colorResolved: Color.Resolved) { red = colorResolved.red green = colorResolved.green blue = colorResolved.blue opacity = colorResolved.opacity } func color() throws -> Color { Color( red: Double(red), green: Double(green), blue: Double(blue), opacity: Double(opacity) ) } } extension ColorRepresentation: Equatable {}
9
0
359
1w
Navigation Title and UIAlertViewController actions truncated/cut on iPhone 13/14/15 Pro Max with iOS 26.4/5 using Cyrillic localisation
Since iOS 26.4, we are observing an issue on iPhone 13 Pro Max, iPhone 14 Pro Max, and iPhone 15 Pro Max where text is truncated on first presentation when using Bulgarian (Cyrillic) localization. The issue affects: UINavigationBar title (both inline and large titles) UIAlertController action titles Behavior: On first presentation, the text is truncated/cut off. On subsequent presentations, the layout appears correct. Adding a zero-width space (\u{200B}) before the last character of the string prevents truncation. This appears to slightly increase the layout width calculation and avoids the issue. Has anyone else encountered this behavior or found a more appropriate workaround?
1
0
110
1w
SwiftUI NavigationSplitView sidebar toolbar has excessive top inset when embedded in TabView since iPadOS 26.4
I’m seeing a layout regression in SwiftUI on iPadOS 26.4 involving NavigationSplitView inside a TabView. When a NavigationSplitView is embedded in a TabView, the sidebar toolbar appears to reserve too much vertical space. There is a large vertical gap between the top edge of the sidebar and the sidebar collapse/toggle icon. It looks as if the sidebar toolbar itself has become much taller than expected. The same NavigationSplitView layout is rendered correctly when it is shown directly without being embedded in a TabView. Environment: iPadOS 26.4 or later SwiftUI iPad TabView NavigationSplitView inside one tab Expected behavior The sidebar toolbar should use its normal height, as it does when the same NavigationSplitView is shown without a surrounding TabView. The sidebar collapse/toggle icon should appear close to the top of the sidebar, without a large empty gap above it. Actual behavior When the NavigationSplitView is hosted inside a TabView, the sidebar toolbar area becomes excessively tall. A large empty space appears above the sidebar collapse/toggle icon. This only happens in the TabView setup. Rendering the same NavigationSplitView directly does not show the issue. Feedback I also filed this as Feedback Assistant report: FB22645938 Has anyone else seen this behavior since iPadOS 26.4? Is this an intentional layout change, or is there a supported way to avoid this additional top inset when using NavigationSplitView inside TabView? Reproduction import SwiftUI struct ContentView: View { enum AppTab { case first case second } @State private var selectedTab: AppTab = .first var body: some View { TabView(selection: $selectedTab) { Tab("First", systemImage: "sidebar.leading", value: .first) { NavigationSplitView { List { Section("Sidebar Content") { ForEach(1...20, id: \.self) { index in Text("Item \(index)") } } } .navigationTitle("Sidebar") .toolbar { ToolbarItem(placement: .topBarLeading) { Button { // action } label: { Image(systemName: "plus") } } } } detail: { Text("Detail") } } Tab("Second", systemImage: "doc", value: .second) { Text("Second tab") } } } }
0
0
131
1w
ImageRenderer fails to render Text views that use @AccessibilityFocusState (.accessibilityFocused)
Environment: iOS 16.0+ SwiftUI Problem Description: I am using ImageRenderer to convert a SwiftUI view into a UIImage for sharing purposes. The view renders perfectly fine on-screen. However, in the generated UIImage, specific Text elements completely disappear. After debugging, I found that the issue is caused by the @AccessibilityFocusState property wrapper. Any Text view that has the .accessibilityFocused(_:) modifier applied to it will be completely missing from the ImageRenderer output. Other views (like Text without the modifier, or Image views) in the exact same hierarchy render perfectly. It seems that because ImageRenderer renders the view off-screen without a live accessibility environment/tree, the accessibility focus binding silently breaks the layout or rendering of that specific element. Minimal Reproducible Example: Here is a generic, drop-in example that demonstrates the bug. When you tap "Capture with ImageRenderer", the resulting image will only contain the subtitle, while the title text vanishes. import SwiftUI // 1. The View we want to render struct ComponentView: View { // The accessibility focus state causing the issue @AccessibilityFocusState private var isTitleFocused: Bool var body: some View { VStack(spacing: 12) { // BUG: This text will NOT appear in the rendered image Text("Title (with accessibility focus)") .font(.headline) .accessibilityFocused($isTitleFocused) // This text WILL appear normally Text("Subtitle (no accessibility focus)") .font(.subheadline) } .padding() .background(Color.blue.opacity(0.1)) .cornerRadius(12) } } // 2. The Container to test the rendering struct ContentView: View { @State private var renderedImage: UIImage? var body: some View { VStack(spacing: 40) { // On-screen: Both Title and Subtitle appear perfectly VStack { Text("Live On-Screen View:") .font(.caption) ComponentView() } Button("Capture with ImageRenderer") { renderImage() } .buttonStyle(.borderedProminent) // Off-screen render: Title is missing! if let image = renderedImage { VStack { Text("Rendered UIImage Result:") .font(.caption) Image(uiImage: image) .overlay( Rectangle().stroke(Color.red, style: StrokeStyle(lineWidth: 1, dash: [5])) ) } } } .padding() } @MainActor private func renderImage() { let renderer = ImageRenderer(content: ComponentView()) renderer.scale = UIScreen.main.scale if let uiImage = renderer.uiImage { self.renderedImage = uiImage } } } Questions: Is this a known limitation of ImageRenderer not supporting the accessibility environment? Is there a way to inject an accessibility environment into ImageRenderer so these modifiers don't break the render? Are there any cleaner workarounds other than manually stripping accessibility modifiers before rendering?
2
0
732
1w
How can I intercept Shift+Tab in SwiftUI on macOS?
Hi everyone, I'm building a macOS SwiftUI app and I'm trying to intercept both: Tab Shift + Tab to perform custom actions (similar to how text editors indent/outdent items). Right now, plain Tab works fine, but Shift + Tab never reaches my .onKeyPress(.tab) handler. Here's what I'm currently trying: import SwiftUI struct ShiftTabNotIntercepted: View { @State private var shiftKeyPressed = false var body: some View { Text("Hello") .focusable() .onKeyPress(.tab) { print("tab pressed with shift: \(shiftKeyPressed)") return .handled } .onModifierKeysChanged(mask: .shift) { old, new in shiftKeyPressed = new.contains(.shift) } } } Behavior: Pressing Tab prints: tab pressed with shift: false Pressing Shift + Tab does nothing — .onKeyPress(.tab) never fires. I also noticed: if a sidebar is visible, Shift + Tab moves focus to the sidebar if no sidebar is visible, it still doesn't trigger the handler So it seems macOS is intercepting Shift + Tab for focus navigation before SwiftUI sees it. My goal is to fully own this keyboard behavior for a custom outline/tree editor UI. Questions: Is there a SwiftUI-native way to intercept Shift + Tab? Is .onKeyPress fundamentally unable to capture this combination? Do I need to drop down to AppKit (NSViewRepresentable, keyDown, etc.) to reliably handle it? Thanks!
1
0
113
1w
Right Way of Positioning an Always Visible Overlay on Top of Navigation Bar That Aligns With Other Navigation Items
Starting with Liquid Glass, the navigation items seem not to be vertically centered within the navigation bar. This makes it very challenging to position an overlay on top of the navigation bar so that it aligns naturally with other elements such as the back button, dismiss button, and others. We want to achieve this for a progress bar so that it remains visible regardless of which views are pushed underneath. Therefore, we cannot add it as a navigation item on each screen, as doing so would cause the progress bar to animate repeatedly as new screens are pushed onto the stack. This used to be easier pre liquid glass since navigation items were centered vertically within the navigation bar. The approach I tried to center the progress bar in the navigation bar: Get access to the top safe area insets through GeometryReader Get access to the the status bar height through UIWindowScene's statusBarManager Subtract status bar height from top safe area inset to calculate the navigation bar height Update the padding of the progress bar accordingly to make sure it is centered within the navigation bar This works, but as I mentioned, now the navigation items are not centered, and the amount of vertical offset they have seems to differ from one screen to another, making it impossible to come up with an additional padding value to align across devices. See how the navigation item looks like within the navigation bar in the view debugger (doesn't matter if it is UINavigationController or NavigationStack the behaviour is the same, also please note that the positioning is the same for a view without an explicit leading toolbar item, where the default back button is provided by the system when a view is pushed): Existing code (without any hacky solutions) to add a progress bar on top as overlay: struct ContentView: View { @State var shouldShowOverlay = false var body: some View { NavigationStack { NavigationLink("Go to View2") { View2() } .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .topBarLeading) { Button { } label: { Image(systemName: "chevron.left") } } } } .overlay(alignment: .top) { ProgressView(value: 0.5) .frame(width: 200) // what to add as padding here // .padding(.top, 16.0) } } How it looks: Some additional observations for the navigation bar item here: There seems to be 4 _UINavigationBarPlatterAnimationViews in the view stack, prior to the bar button item:
 The first two seems to be fine, they both have (0, 0, 44, 44) for both frame and bounds
 The third one’s frame has height and width of 48.2, and x, y values of -2.1. The last one’s frame has 40,17 for height and width and 1.92 for x,y values. Both views have the following bounds: (0, 0, 44, 44). I also tried to access to the origin of the back bar button item so that I could calculate where the position the overlay, but that also didn't yield to something useful, not to mention it would also be a super hacky solution. So my ultimate question is, is there a clean way to position an overlay on top of the navigation bar that vertically aligns with other navigation bar items, or should we just position it elsewhere and do not mess with navigation bar anymore? Any input would be greatly appreciated.
0
1
149
1w
ToolbarItemGroup With Palette Style Cannot Present a View Controller While the Context Menu Is Visible
When I set up a toolbar item group with multiple options and set the controlGroupStyle as .palette, and when one of the options are supposed to present a view controller, I get the following error Attempt to present <UINavigationController: 0x101813200> on <ProjectName.HomeTabBarViewController: 0x10701bc00> (from <UINavigationController: 0x107821000>) which is already presenting <_UIContextMenuActionsOnlyViewController: 0x1035c19d0>. So basically the context menu we see is under the hood a view controller that is being presented. Is there a right way of fixing it, or is it maybe something that will be fixed by Apple? This is how I set up the ToolbarItemGroup on SwiftUI and the view model ultimately presents another UINavigationController that has a UIHostingController as its view controller: .toolbar { ToolbarItemGroup(placement: .topBarTrailing) { Button("ft_commons_edit".localised, systemImage: "pencil") { viewModel.didTapEditAction() } Button("ft_commons_delete".localised, systemImage: "trash") { viewModel.didTapDeleteAction() } } label: { Image("edit-icon") .resizable() .frame(width: 24.0, height: 24.0) } } .controlGroupStyle(.palette) This is how the view looks like when presentation fails: As a workaround, I found two options that work well. I’d like to share them and ask for recommendations, just to make sure they won’t cause any unexpected issues later on: Option 1: Access the top most visible view controller and attempt presenting on it This requires the following extension: extension UIViewController { func topMostViewController() -> UIViewController { if let presentedViewController = self.presentedViewController { return presentedViewController.topMostViewController() } else if let navigationController = self as? UINavigationController, let topViewController = navigationController.topViewController { return topViewController.topMostViewController() } else if let tabBarController = self as? UITabBarController, let selectedViewController = tabBarController.selectedViewController { return selectedViewController.topMostViewController() } else { return self } } } Then called as: navigationController.topMostViewController().present(exerciseEditingNavController, animated: true) Option 2: Call dismiss before attempting to present anything This also works fine, even without any delay, the menu is first dismissed and presentation works fine afterwards, code example: navigationController.dismiss(animated: true) navigationController.present(exerciseEditingNavController, animated: true) I feel like Option 1 would be the better choice, because if this context menu is ever no longer treated as a view controller and direct presentation starts working, Option 1 would still behave correctly by presenting from the topmost visible view controller. Option 2, on the other hand, could introduce a bug by dismissing an unrelated view controller if the context menu is no longer represented as a view controller at that point. I would appreciate any advice from anyone who has experienced this, or from Apple developers. Thanks
6
0
339
1w
Programatically adding to a TextField and moving the TextSelection point in SwiftUI
Hi! I am trying to create a simple SwiftUI TextField, with an external button to add text to the field at the current insertion point (the cursor in the TextField). When I add the text, the cursor (I-Beam) remains at the original insertion point, so I want it to move over to the end of what I added. The trouble is, it sometimes moves further forward or to the end (visibly) but works as if it is still at the point I moved it to. This seems to possibly be due to emojis in the TextField (because, I assume, they are composed of more bytes). Further, sometimes the addition of the text can cause an emoji to appear unexpectedly, I assume because it is combining the bytes in an odd way. So moving the cursor seems to sometimes introduce weird behaviour. This comes from a much larger project, but I have distilled this down to the smallest example project I could. And I have a video to show how it behaves. Here's the main part of the code, and I'll attach an Xcode project: import SwiftUI struct ContentView: View { @State private var text: String = "abcdef🧁🧁🧁🧁ghijkl" @State private var selectedText: TextSelection? var body: some View { VStack { TextField("", text: $text, selection: $selectedText) .font(.title) Button("Add Z at Insertion Point in TextField") { // Get indices of any selection in the text field let from: String.Index, to: String.Index if let selectedText = selectedText { let indices = selectedText.indices switch indices { case .selection(let range): from = range.lowerBound to = range.upperBound case .multiSelection(let rangeSet): from = rangeSet.ranges.first!.lowerBound to = rangeSet.ranges.first!.upperBound default: from = self.text.endIndex to = self.text.endIndex } } else { from = self.text.endIndex to = self.text.endIndex } guard from <= to && from <= self.text.endIndex else { return } // Insert and update the cursor position self.text.replaceSubrange(from..<to, with: "Z") // Move cursor after the inserted character let newIndex = self.text.index(after: from) selectedText = TextSelection(insertionPoint: newIndex) } } .padding() } } STEPS TO REPRODUCE Run the app. Also view the video as it shows the steps. Put insertion point between c and d. Press the "Add Z" button. Note that Z is placed between c and d. This is great. Put insertion point between h and i. Press the "Add Z" button. Note that Z is placed between h and i. BUT, the insertion point I-beam moves to the end of the string. Press the "Add Z" button again. Z is added where you would have expected based on where the TextSelection insertion point was put, but the flashing I-Beam is still at the end. Press the "Add Z" button again. Same issue. insertion point is being shown at end, but to the button it is between Z and i. OF NOTE, if you use the keyboard and press delete, it deletes from end (where the I-beam is). Now put insertion point between the 4 cupcakes. Press "Add Z" two times. It behaves correctly. Press "Add Z" a third time. It adds a fairy emoji. So, any idea what I am doing wrong? I thought it might be an issue requiring me to update in a background thread, but I tried that, even delaying the update in the thread, but the issue remains. Thanks in advance. Here's a video: https://curmi.name/temp/SimpleTextField%20showing%20issues.mp4 And if it helps, here is the Xcode project: https://curmi.name/temp/SimpleTextfield.zip
Replies
1
Boosts
0
Views
221
Activity
13m
SwiftUI retain cycle with .searchable
I'm trying to see what I am doing wrong ... So I created this simple app where the stateobject Retainer won't get deallocated when I pop the view off the stack: import SwiftUI struct ContentView: View { var body: some View { NavigationView { List { NavigationLink("To Retain Cycle") { RetainCycleView() } } .navigationTitle("Retain Cycle Demo") } .navigationViewStyle(.stack) } } struct RetainCycleView: View { @StateObject var model = Retainer() // @State var enteredText: String = "" var body: some View { VStack(alignment: .leading, spacing: 4) { Text("Navigate back to the previous view.") Text("You will see that 'Retainer' was NOT deallocated.") Text("(it's deinit function prints deallocing Retainer)") .font(.callout) } .padding() .searchable(text: $model.enteredText) // ^---- retain cycle // .searchable(text: $enteredText) // ^---- no retain cycle when using the @State var } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } } class Retainer: ObservableObject { @Published var enteredText: String = "" init() { print("instantiated Retainer") } deinit { print("deallocing Retainer") } } I filed feedback but I am not entirely sure that this isn't me making some mistake ... Please help me
Replies
4
Boosts
1
Views
1.1k
Activity
51m
Glass effect interactive effect issue when used with concentric shapes
Using .glassEffect(.clear.interactive(), in: shape), where shape is some concentric shape that adapts to corner radius of the device, results in appearing of highlighted capsule shape. Code to reproduce this behavior import SwiftUI struct HelloLiquidGlass: View { var body: some View { if #available(iOS 26.0, *) { Text("Hello, World!") .frame(maxWidth: .infinity, maxHeight: .infinity) // .glassEffect(.clear.interactive(), in: .rect(corners: .concentric)) // .glassEffect(.clear.interactive(), in: ConcentricRectangle(uniformTopCorners: .fixed(80))) .padding(36) .ignoresSafeArea() .preferredColorScheme(.dark) } } } #Preview { HelloLiquidGlass() } Either of both commented-out modifiers produces the same result when user interacts with Liquid Glass pane (revealing of capsule shape that is not relative to actual shape of liquid glass effect modifier) iOS 26.5 (23F73) SDK + iOS 26.5 (23F77) Simulator
Replies
1
Boosts
0
Views
123
Activity
20h
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
0
Boosts
0
Views
15
Activity
1d
Overlay window above all windows, even when moving spaces
Hi! I would like to overlay a macOS application window above all windows, even when moving spaces or moving to a fullscreen app, similar in function to the DropOver app for macOS. What I have tried: Overwriting the default app delegate in SwiftUI and creating the NSWindow myself. Then setting the window.collectionBehavior to [.canJoinAllApplications, .canJoinAllSpaces, .fullScreenAuxiliary] and the window.level to .floating or .statusBar. This works for moving between Spaces, but still does not display above apps in fullscreen mode. I also registered a notification observer for NSWorkspace.activeSpaceDidChangeNotification, where I call window.orderFrontRegardless() to always have my window frontmost. Still not displaying above fullscreen apps. What am I missing to make this work? Best regards
Replies
2
Boosts
0
Views
89
Activity
1d
Bottom sheet does not adapt to screen rotation after presenting NFC Reader session
Hello I am implementing an NFC Reader session in my app, and presenting the bottom sheet works completely fine. However, I am facing an issue with screen rotation. If I rotate the device while the NFC bottom sheet is active, the sheet does not adapt or resize according to the new screen orientation. Is there a way to force the NFC bottom sheet to update its layout or fix this rotation issue?Any help or workarounds would be greatly appreciated. Thanks!
Replies
1
Boosts
0
Views
38
Activity
1d
UI layout overlaps in bottom sheet upon screen rotation during NFC Reader session
Hello I am experiencing a layout issue where UI components overlap in a bottom sheet during screen rotation while an NFC Reader session is active. The NFC Reader session initializes and displays the bottom sheet properly. However, if the device is rotated while this sheet is on screen, the layout breaks, and elements appear overlapped. I have attached a sample image demonstrating this rendering issue. Is there a known workaround to fix this layout distortion or force a proper layout update during an active NFC session?Any insights or suggestions would be greatly appreciated. Thanks!
Replies
1
Boosts
0
Views
24
Activity
2d
How to optimize SwiftData performance in large lists?
I’m building a SwiftUI application using SwiftData for local storage. When loading large lists with images, scrolling performance becomes slower. What are the best practices to optimize SwiftData fetch performance in SwiftUI apps?
Replies
1
Boosts
0
Views
41
Activity
2d
tvOS SwiftUI - Siri "On-Screen Actions" breaks lazy lists
Hey team, This is a weird one, when Siri + "On-Screen Actions" are enabled in the Apple TV settings (General Settings), LazyVStack experiences severe main-thread hangs and very high CPU usage. The issue only occurs when Siri + On-Screen Actions are enabled in Apple TV device settings (General Settings). Disabling On-Screen Actions completely resolves the issue. The issue has been verified on multiple Apple TV devices, running tvOS 26.3, 26.4 and 26.5. The behavior was not observed in Simulators, only on an actual device. This seems to be caused by some type of accessibility overlap with SwiftUI. Running the below code with "On-Screen Actions" enabled in the Siri settings will demonstrate a complete hang of the CPU at 100% struct TestData { let id = UUID() var title: String } struct TestView: View { @State private var test: [TestData] = [] var body: some View { ScrollView { LazyVStack { ForEach(self.test, id: \.id) { data in Button { } label: { Text(data.title) } } } } .task { var arr: [TestData] = [] for _ in 0..<30000 { let data = TestData(title: "Title") arr.append(data) } self.test = arr } } } Already submited a bug report FB22758928
Replies
0
Boosts
0
Views
112
Activity
3d
SF Symbols .replace animation is partially missing
In SF Symbols 7, I'm observing a discrepancy between the .replace animation previewed in the SF Symbols app and the result when using the code template the app provides. Expected behavior: When previewing the .replace animation in the SF Symbols app (with Magic Replace preferred), the transition looks like: Actual behavior: When I implement the animation using the exact code template generated by the SF Symbols app, the result looks like: My code: struct PlaygroundSwiftUIView: View { @State var name = "folder.circle" var body: some View { Image(systemName: name) .font(.system(size: 60)) .contentTransition(.symbolEffect(.replace)) .onTapGesture { name = "tree" } } } #Preview { PlaygroundSwiftUIView() } The animation rendered in my app does not match the preview shown in the SF Symbols app. The drawOff animation is partially missing. Is there something missing from the code template, or is there an additional configuration step required to achieve the correct Replace effect? or is this a bug?
Replies
2
Boosts
0
Views
175
Activity
4d
SwiftUI + AppKit context menu wedges window-level mouse events when the menu's anchor view is deleted by the menu's own action
I'm on macOS 26.4 (SwiftUI + SwiftData + AppKit bridging). I have a SwiftUI canvas where each child view (a card) carries a per-card AppKit context menu via an NSViewRepresentable overlay (a small NSView subclass that overrides menu(for:) and returns an NSMenu whose items invoke a Swift closure). The closure for the "Delete" item removes the underlying model object. SwiftData mutates → SwiftUI re-emits the canvas → the card containing the menu's anchor NSView unmounts → that anchor NSView is removed from the AppKit view hierarchy. After this happens, the entire window's SwiftUI gestures stop receiving mouse events: pan, zoom, taps on a separate background Color view all go silent. The window's first responder ends up at the NSWindow itself. The wedge persists across switching to a sibling SwiftUI view in the same window (different SwiftData root), so the bad state is at the window/event-routing level, not in any one SwiftUI subtree. The wedge is reliably cleared by another right-click that successfully shows + dismisses any menu — strongly suggesting AppKit's menu/event-tracking cleanup is left in a half-finished state because the anchor view went away during the cleanup window. I've already verified: Switching from explicit NSMenu.popUpContextMenu(_:with:for:) to overriding menu(for:) does not change the behavior. Deferring the model deletion via DispatchQueue.main.async, asyncAfter (30 ms), or RunLoop.main.perform(inModes: [.default]) either doesn't help or only avoids the wedge if the delay is long enough to be perceptible (seconds), implying the cleanup window is event-driven, not time-driven. Triggering the same deletion from a SwiftUI Button (no AppKit menu involved) never wedges. My current understanding is that the standard AppKit pattern (e.g., NSTableView, NSOutlineView) attaches the context menu to a stable parent view, never to the per-row view itself, precisely so the menu's anchor outlives any single row's deletion. Restructuring my code so a single canvas-level AppKitContextMenuRegion returns the appropriate menu via hit-testing in menu(for:) should avoid the issue, but I'd like to confirm: Is the assumption that an NSMenu's anchor view must outlive the menu's tracking-end cleanup documented anywhere, or is it implicit? Is there a supported way to safely use a per-row anchor view that gets deleted by the menu's own action, or is the canvas-level / parent-level menu attachment the only correct pattern? 3. Are there any post-tracking notifications or APIs that guarantee AppKit's cleanup is complete before the anchor view is allowed to be torn down (so a per-row anchor could be made safe with the right ordering)? Thanks!
Replies
0
Boosts
0
Views
277
Activity
5d
Why doesn't .glassEffect tint render on a Menu in an iOS 26 toolbar?
This is what I am trying to achieve (from the Phone app, similar one is also in Photos) I have a standard SwiftUI Menu in a toolbar with a glass tint applied to indicate the filter is active: Menu { // …filter options } label: { Label("Filter", systemImage: "line.3.horizontal.decrease") } .glassEffect(.regular.tint(.accentColor).interactive()) The glass effect doesn't render at all, no tint. The button looks completely unstyled. If I switch the label from Label to Image, the glass renders, but as a stretched oblong pill. I have tried several other combinations too: Also in the Apple's version during hover (iPad) the highlight aligns with the tint itself (see second image above) rather than outside it like in example 3 from the list above: Is there a way to get a Menu's trigger inset tint to look as in the Phone app example?
Replies
0
Boosts
0
Views
52
Activity
1w
'NSKeyedUnarchiveFromData' should not be used to for un-archiving and will be removed in a future release
Hi, Overview: I get the following error when trying to save / read from SwiftData It happens when I try to save color to SwiftData (code below) Error 'NSKeyedUnarchiveFromData' should not be used to for un-archiving and will be removed in a future release Questions How can I resolve the error? I am not directly using data, I am using just Float values, swift types. Why am I getting this error? Is there a way to add a breakpoint to stop at the exact type causing the error? (Symbolic breakpoint doesn't seem to help) Or is the below code ok and not responsible for the error? Code import SwiftUI nonisolated struct ColorRepresentation: Codable { let red: Float let green: Float let blue: Float let opacity: Float init(colorResolved: Color.Resolved) { red = colorResolved.red green = colorResolved.green blue = colorResolved.blue opacity = colorResolved.opacity } func color() throws -> Color { Color( red: Double(red), green: Double(green), blue: Double(blue), opacity: Double(opacity) ) } } extension ColorRepresentation: Equatable {}
Replies
9
Boosts
0
Views
359
Activity
1w
Navigation Title and UIAlertViewController actions truncated/cut on iPhone 13/14/15 Pro Max with iOS 26.4/5 using Cyrillic localisation
Since iOS 26.4, we are observing an issue on iPhone 13 Pro Max, iPhone 14 Pro Max, and iPhone 15 Pro Max where text is truncated on first presentation when using Bulgarian (Cyrillic) localization. The issue affects: UINavigationBar title (both inline and large titles) UIAlertController action titles Behavior: On first presentation, the text is truncated/cut off. On subsequent presentations, the layout appears correct. Adding a zero-width space (\u{200B}) before the last character of the string prevents truncation. This appears to slightly increase the layout width calculation and avoids the issue. Has anyone else encountered this behavior or found a more appropriate workaround?
Replies
1
Boosts
0
Views
110
Activity
1w
SwiftUI NavigationSplitView sidebar toolbar has excessive top inset when embedded in TabView since iPadOS 26.4
I’m seeing a layout regression in SwiftUI on iPadOS 26.4 involving NavigationSplitView inside a TabView. When a NavigationSplitView is embedded in a TabView, the sidebar toolbar appears to reserve too much vertical space. There is a large vertical gap between the top edge of the sidebar and the sidebar collapse/toggle icon. It looks as if the sidebar toolbar itself has become much taller than expected. The same NavigationSplitView layout is rendered correctly when it is shown directly without being embedded in a TabView. Environment: iPadOS 26.4 or later SwiftUI iPad TabView NavigationSplitView inside one tab Expected behavior The sidebar toolbar should use its normal height, as it does when the same NavigationSplitView is shown without a surrounding TabView. The sidebar collapse/toggle icon should appear close to the top of the sidebar, without a large empty gap above it. Actual behavior When the NavigationSplitView is hosted inside a TabView, the sidebar toolbar area becomes excessively tall. A large empty space appears above the sidebar collapse/toggle icon. This only happens in the TabView setup. Rendering the same NavigationSplitView directly does not show the issue. Feedback I also filed this as Feedback Assistant report: FB22645938 Has anyone else seen this behavior since iPadOS 26.4? Is this an intentional layout change, or is there a supported way to avoid this additional top inset when using NavigationSplitView inside TabView? Reproduction import SwiftUI struct ContentView: View { enum AppTab { case first case second } @State private var selectedTab: AppTab = .first var body: some View { TabView(selection: $selectedTab) { Tab("First", systemImage: "sidebar.leading", value: .first) { NavigationSplitView { List { Section("Sidebar Content") { ForEach(1...20, id: \.self) { index in Text("Item \(index)") } } } .navigationTitle("Sidebar") .toolbar { ToolbarItem(placement: .topBarLeading) { Button { // action } label: { Image(systemName: "plus") } } } } detail: { Text("Detail") } } Tab("Second", systemImage: "doc", value: .second) { Text("Second tab") } } } }
Replies
0
Boosts
0
Views
131
Activity
1w
ImageRenderer fails to render Text views that use @AccessibilityFocusState (.accessibilityFocused)
Environment: iOS 16.0+ SwiftUI Problem Description: I am using ImageRenderer to convert a SwiftUI view into a UIImage for sharing purposes. The view renders perfectly fine on-screen. However, in the generated UIImage, specific Text elements completely disappear. After debugging, I found that the issue is caused by the @AccessibilityFocusState property wrapper. Any Text view that has the .accessibilityFocused(_:) modifier applied to it will be completely missing from the ImageRenderer output. Other views (like Text without the modifier, or Image views) in the exact same hierarchy render perfectly. It seems that because ImageRenderer renders the view off-screen without a live accessibility environment/tree, the accessibility focus binding silently breaks the layout or rendering of that specific element. Minimal Reproducible Example: Here is a generic, drop-in example that demonstrates the bug. When you tap "Capture with ImageRenderer", the resulting image will only contain the subtitle, while the title text vanishes. import SwiftUI // 1. The View we want to render struct ComponentView: View { // The accessibility focus state causing the issue @AccessibilityFocusState private var isTitleFocused: Bool var body: some View { VStack(spacing: 12) { // BUG: This text will NOT appear in the rendered image Text("Title (with accessibility focus)") .font(.headline) .accessibilityFocused($isTitleFocused) // This text WILL appear normally Text("Subtitle (no accessibility focus)") .font(.subheadline) } .padding() .background(Color.blue.opacity(0.1)) .cornerRadius(12) } } // 2. The Container to test the rendering struct ContentView: View { @State private var renderedImage: UIImage? var body: some View { VStack(spacing: 40) { // On-screen: Both Title and Subtitle appear perfectly VStack { Text("Live On-Screen View:") .font(.caption) ComponentView() } Button("Capture with ImageRenderer") { renderImage() } .buttonStyle(.borderedProminent) // Off-screen render: Title is missing! if let image = renderedImage { VStack { Text("Rendered UIImage Result:") .font(.caption) Image(uiImage: image) .overlay( Rectangle().stroke(Color.red, style: StrokeStyle(lineWidth: 1, dash: [5])) ) } } } .padding() } @MainActor private func renderImage() { let renderer = ImageRenderer(content: ComponentView()) renderer.scale = UIScreen.main.scale if let uiImage = renderer.uiImage { self.renderedImage = uiImage } } } Questions: Is this a known limitation of ImageRenderer not supporting the accessibility environment? Is there a way to inject an accessibility environment into ImageRenderer so these modifiers don't break the render? Are there any cleaner workarounds other than manually stripping accessibility modifiers before rendering?
Replies
2
Boosts
0
Views
732
Activity
1w
How can I intercept Shift+Tab in SwiftUI on macOS?
Hi everyone, I'm building a macOS SwiftUI app and I'm trying to intercept both: Tab Shift + Tab to perform custom actions (similar to how text editors indent/outdent items). Right now, plain Tab works fine, but Shift + Tab never reaches my .onKeyPress(.tab) handler. Here's what I'm currently trying: import SwiftUI struct ShiftTabNotIntercepted: View { @State private var shiftKeyPressed = false var body: some View { Text("Hello") .focusable() .onKeyPress(.tab) { print("tab pressed with shift: \(shiftKeyPressed)") return .handled } .onModifierKeysChanged(mask: .shift) { old, new in shiftKeyPressed = new.contains(.shift) } } } Behavior: Pressing Tab prints: tab pressed with shift: false Pressing Shift + Tab does nothing — .onKeyPress(.tab) never fires. I also noticed: if a sidebar is visible, Shift + Tab moves focus to the sidebar if no sidebar is visible, it still doesn't trigger the handler So it seems macOS is intercepting Shift + Tab for focus navigation before SwiftUI sees it. My goal is to fully own this keyboard behavior for a custom outline/tree editor UI. Questions: Is there a SwiftUI-native way to intercept Shift + Tab? Is .onKeyPress fundamentally unable to capture this combination? Do I need to drop down to AppKit (NSViewRepresentable, keyDown, etc.) to reliably handle it? Thanks!
Replies
1
Boosts
0
Views
113
Activity
1w
Prominent glass button in SwiftUI incorrect text style
How do you create a prominent glass button in SwiftUI? In UIKit it’s let button = UIButton(configuration: .prominentGlass()) button.configuration?.title = "Agree" I tried Button("Agree") {} .buttonStyle(.glassProminent) but the title text is white not glassified 🤨
Replies
0
Boosts
0
Views
60
Activity
1w
Right Way of Positioning an Always Visible Overlay on Top of Navigation Bar That Aligns With Other Navigation Items
Starting with Liquid Glass, the navigation items seem not to be vertically centered within the navigation bar. This makes it very challenging to position an overlay on top of the navigation bar so that it aligns naturally with other elements such as the back button, dismiss button, and others. We want to achieve this for a progress bar so that it remains visible regardless of which views are pushed underneath. Therefore, we cannot add it as a navigation item on each screen, as doing so would cause the progress bar to animate repeatedly as new screens are pushed onto the stack. This used to be easier pre liquid glass since navigation items were centered vertically within the navigation bar. The approach I tried to center the progress bar in the navigation bar: Get access to the top safe area insets through GeometryReader Get access to the the status bar height through UIWindowScene's statusBarManager Subtract status bar height from top safe area inset to calculate the navigation bar height Update the padding of the progress bar accordingly to make sure it is centered within the navigation bar This works, but as I mentioned, now the navigation items are not centered, and the amount of vertical offset they have seems to differ from one screen to another, making it impossible to come up with an additional padding value to align across devices. See how the navigation item looks like within the navigation bar in the view debugger (doesn't matter if it is UINavigationController or NavigationStack the behaviour is the same, also please note that the positioning is the same for a view without an explicit leading toolbar item, where the default back button is provided by the system when a view is pushed): Existing code (without any hacky solutions) to add a progress bar on top as overlay: struct ContentView: View { @State var shouldShowOverlay = false var body: some View { NavigationStack { NavigationLink("Go to View2") { View2() } .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .topBarLeading) { Button { } label: { Image(systemName: "chevron.left") } } } } .overlay(alignment: .top) { ProgressView(value: 0.5) .frame(width: 200) // what to add as padding here // .padding(.top, 16.0) } } How it looks: Some additional observations for the navigation bar item here: There seems to be 4 _UINavigationBarPlatterAnimationViews in the view stack, prior to the bar button item:
 The first two seems to be fine, they both have (0, 0, 44, 44) for both frame and bounds
 The third one’s frame has height and width of 48.2, and x, y values of -2.1. The last one’s frame has 40,17 for height and width and 1.92 for x,y values. Both views have the following bounds: (0, 0, 44, 44). I also tried to access to the origin of the back bar button item so that I could calculate where the position the overlay, but that also didn't yield to something useful, not to mention it would also be a super hacky solution. So my ultimate question is, is there a clean way to position an overlay on top of the navigation bar that vertically aligns with other navigation bar items, or should we just position it elsewhere and do not mess with navigation bar anymore? Any input would be greatly appreciated.
Replies
0
Boosts
1
Views
149
Activity
1w
ToolbarItemGroup With Palette Style Cannot Present a View Controller While the Context Menu Is Visible
When I set up a toolbar item group with multiple options and set the controlGroupStyle as .palette, and when one of the options are supposed to present a view controller, I get the following error Attempt to present <UINavigationController: 0x101813200> on <ProjectName.HomeTabBarViewController: 0x10701bc00> (from <UINavigationController: 0x107821000>) which is already presenting <_UIContextMenuActionsOnlyViewController: 0x1035c19d0>. So basically the context menu we see is under the hood a view controller that is being presented. Is there a right way of fixing it, or is it maybe something that will be fixed by Apple? This is how I set up the ToolbarItemGroup on SwiftUI and the view model ultimately presents another UINavigationController that has a UIHostingController as its view controller: .toolbar { ToolbarItemGroup(placement: .topBarTrailing) { Button("ft_commons_edit".localised, systemImage: "pencil") { viewModel.didTapEditAction() } Button("ft_commons_delete".localised, systemImage: "trash") { viewModel.didTapDeleteAction() } } label: { Image("edit-icon") .resizable() .frame(width: 24.0, height: 24.0) } } .controlGroupStyle(.palette) This is how the view looks like when presentation fails: As a workaround, I found two options that work well. I’d like to share them and ask for recommendations, just to make sure they won’t cause any unexpected issues later on: Option 1: Access the top most visible view controller and attempt presenting on it This requires the following extension: extension UIViewController { func topMostViewController() -> UIViewController { if let presentedViewController = self.presentedViewController { return presentedViewController.topMostViewController() } else if let navigationController = self as? UINavigationController, let topViewController = navigationController.topViewController { return topViewController.topMostViewController() } else if let tabBarController = self as? UITabBarController, let selectedViewController = tabBarController.selectedViewController { return selectedViewController.topMostViewController() } else { return self } } } Then called as: navigationController.topMostViewController().present(exerciseEditingNavController, animated: true) Option 2: Call dismiss before attempting to present anything This also works fine, even without any delay, the menu is first dismissed and presentation works fine afterwards, code example: navigationController.dismiss(animated: true) navigationController.present(exerciseEditingNavController, animated: true) I feel like Option 1 would be the better choice, because if this context menu is ever no longer treated as a view controller and direct presentation starts working, Option 1 would still behave correctly by presenting from the topmost visible view controller. Option 2, on the other hand, could introduce a bug by dismissing an unrelated view controller if the context menu is no longer represented as a view controller at that point. I would appreciate any advice from anyone who has experienced this, or from Apple developers. Thanks
Replies
6
Boosts
0
Views
339
Activity
1w