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

SwiftUI Documentation

Posts under SwiftUI subtopic

Post

Replies

Boosts

Views

Activity

How do you correctly use a SwiftUI View inside an NSToolbarItem?
I've been struggling to get consistent UI and UX behaviour of SwiftUI Views inside NSToolbarItems and was wondering if there is an official way to use them. I've now revisited this issue in macOS 27 and continue to see some idiosyncrasies. In the attached screenshot, you can see that the highlight area on mouse down between to the two buttons is different. This is the easiest example I've come up with that shows SwiftUI Views exhibiting different behaviour than AppKit Views. Two questions: Is an NSHostingView a valid and supported view type for NSToolbarItem.view? If so, are there any rules that govern how the SwiftUI view should be configured? (ex: frame, sizing options, supported SwiftUI Views, preferred "root view" types, etc?) Sample code that created the two NSToolbarItem buttons in the screenshot. macOS 27 ZY21R0CMGL (Public Beta 1) Xcode 27.0 beta Minimum Deployment target: 27.0 // Left-Top SwiftUI Button (Clipped Highlighting) let item = NSToolbarItem(itemIdentifier: itemIdentifier) let rootView = Button { } label: { Image(systemName: "sidebar.trailing") } item.view = NSHostingView(rootView: rootView) // ... snip .. // Right-Bottom AppKit Button (Correct Highlighting) let item = NSToolbarItem(itemIdentifier: itemIdentifier) item.image = NSImage(systemSymbolName: "sidebar.trailing", accessibilityDescription: nil) Both screenshots are taken on mouse down.
1
0
25
1h
How do you make a resizable segmented control in SwiftUI for macOS?
In SwiftUI for macOS, how do I configure a Picker as a segmented control to have a flexible width? This design pattern is present in Xcode 26 at the top of the sidebar and inspector panel. I can't figure out the combination of view modifiers to achieve a similar look. import SwiftUI struct ContentView: View { @State private var selection = 0 var body: some View { VStack { Picker("", selection: $selection) { Image(systemName: "doc") Image(systemName: "folder") Image(systemName: "gear") Image(systemName: "globe") .frame(maxWidth: .infinity) // Doesn't do anything. } .labelsHidden() .pickerStyle(.segmented) .frame(maxWidth: .infinity) // Doesn't affect segment sizes. Spacer() } } } I want the entire Picker to fill the width and for each segment to be of equal widths. How? In AppKit I would use AutoLayout for the flexible width and NSSegmentedControl.segmentDistribution for the segment widths. Is there a SwiftUI equivalent? macOS 26 / Xcode 26.3
Topic: UI Frameworks SubTopic: SwiftUI Tags:
1
0
137
7h
Push new views to sidebar when using NavigationPath
I have an existing app that uses NavigationSplitView with a detail view that is never changed and just updates to show changes in data. All navigation changes the sidebar only using NavigationLinks with .isDetailLink(false). Now I'm wanting to use NavigationPath with a simple router and enums. import SwiftUI @main struct NavRouterApp: App { @State private var router = Router() var body: some Scene { WindowGroup { NavigationStack(path: $router.navPath) { ContentView() .navigationDestination(for: AppRoute.self) { route in switch route { case .citizens: ContentView() .environment(router) case .citizen: EmptyView() .environment(router) case .tasform2: TASForm2View() .environment(router) case .start: StartView() .environment(router) case .editcharacteristics: EmptyView() .environment(router) case .basicdata(let citizen): BasicDataView(citizen: citizen) .environment(router) .navigationBarBackButtonHidden(true) case .characteristics: EmptyView() .environment(router) } } } .environment(router) } } } The problem is that pushing a subview now replaces the entire content instead of just the sidebar. Pushing a subview with NavigationSplitView would update the sidebar as desired but I would have to replace the detail view, which is not a good idea. I haven't been able to find any way to accomplish what I want. Suggestions?
Topic: UI Frameworks SubTopic: SwiftUI
4
0
59
1d
Multiline TextField in a List does not keep the caret visible on iPad
On iPad, a multiline TextField inside a List does not scroll when the text wraps to a new line. The caret and newly entered text can move behind the keyboard until the user scrolls manually. The same code works correctly on iPhone. struct ContentView: View { @State var text = "" var body: some View { List { Color.red.frame(width: 200, height: 500) // When the caret goes to the next line during the text input the scroll doesn't follow it. // So the new text is being overlapped by the keyboard until you scroll manually. TextField( "Run me on iPad and start entering multiline text with keyboard up", text: $text, axis: .vertical ) .lineLimit(1...) Color.red.frame(width: 200, height: 500) } } }
Topic: UI Frameworks SubTopic: SwiftUI
0
0
17
2d
Avoiding crashes in iOS 27 when selected tab is hidden from TabView
I read through the iOS 27 release notes and the following caught my attention: In apps built with the iOS 27.0 and iPadOS 27.0 SDKs, a TabView enforces that its selection is set to a visible tab. TabView might crash when its selection is set to a hidden or otherwise unavailable tab. (164516837) My app has a partially configurable tab bar. If the user currently has tab X selected (in the TabView) and then chooses to hide tab X (by toggling an option in my app's settings), then I guess this could cause a crash. My app already has logic in place so that if the user hides the tab that is currently the selected tab, then the currently selected tab is changed to another one that is always visible. This is handled by an .onChange view modifier (basically: on change of setting, if selected tab is now hidden, change selected tab to something else). However, I'm concerned about the potential for race conditions with this set up. For example, if the TabView re-renders before the selected tab is changed to an available tab, then this could cause a crash. My questions for Apple: Are programmatically configurable TabViews officially supported, or are you recommending against this practice? If they are, what defensive steps are recommended to avoid crashes (e.g. adding a small delay to make sure that the selected tab is changed first before removing a tab from the TabView).
Topic: UI Frameworks SubTopic: SwiftUI
2
0
61
2d
Different keyboards show up for KeyboardType .decimalPad
Environment: iOS 26; iPad Mini/Air/Pro Problem: In a TextField, I am using a keyboard with the type .decimalPad. When I initially tap into the TextField, the "popover" keyboard (i.e. the decimalPad) shows up and focusses the TextField. However, when I click outside the TextField (to dismiss the keyboard), the TextField is still focussed (the keyboard was dismissed though). When reentering in the TextField, another keyboard (from the bottom of the screen) appears (most likely .numeric). Does anybody know how to solve this? What I already tried: I tried listening to the dismissal of the keyboard to manually set the FocusState to nil. However, the dismissal of the "popover/decimal" keyboard is not recognized as such a dismissal. I also tried to build a custom component out of that, but then I lose the TextField behavior, conflicting with HIG.
2
0
75
2d
Zoom transition source tile lags after back navigation when LazyVGrid is scrolled immediately
[Submitted as FB21961572] When navigating from a tile in a scrolling LazyVGrid to a child view using .navigationTransition(.zoom) and then returning, the source tile can lag behind the rest of the grid if scrolling starts immediately after returning. The lag becomes more pronounced as tile content gets more complex; in this simplified sample, it can seem subtle, but in production-style tiles (as used in both of my apps), it is clearly visible and noticeable. This may be related to another issue I recently filed: Source item disappears after swipe-back with .navigationTransition(.zoom) CONFIGURATION Platform: iOS Simulator and physical device Navigation APIs: matchedTransitionSource + navigationTransition(.zoom) Container: ScrollView + LazyVGrid Sample project: ZoomTransition (DisappearingTile).zip REPRO STEPS Create a new iOS project and replace ContentView with the code below. Run the app in sim or physical device Tap any tile in the scrolling grid to navigate to the child view. Return to the grid (back button or edge swipe). Immediately scroll the grid. Watch the tile that was just opened. EXPECTED All tiles should move together as one coherent scrolling grid, with no per-item lag or desynchronization. ACTUAL The tile that was just opened appears to trail behind neighboring tiles for a short time during immediate scrolling after returning. MINIMAL CODE SAMPLE import SwiftUI struct ContentView: View { @Namespace private var namespace private let tileCount = 40 private let columns = [GridItem(.adaptive(minimum: 110), spacing: 12)] var body: some View { NavigationStack { ScrollView { LazyVGrid(columns: columns, spacing: 12) { ForEach(0..<tileCount, id: \.self) { index in NavigationLink(value: index) { RoundedRectangle(cornerRadius: 16) .fill(color(for: index)) .frame(height: 110) .overlay(alignment: .bottomLeading) { Text("\(index + 1)") .font(.headline) .foregroundStyle(.white) .padding(10) } .matchedTransitionSource(id: index, in: namespace) } .buttonStyle(.plain) } } .padding(16) } .navigationTitle("Zoom Transition Grid") .navigationSubtitle("Open tile, go back, then scroll immediately") .navigationDestination(for: Int.self) { index in Rectangle() .fill(color(for: index)) .ignoresSafeArea() .navigationTransition(.zoom(sourceID: index, in: namespace)) } } } private func color(for index: Int) -> Color { let hue = Double(index % 20) / 20.0 return Color(hue: hue, saturation: 0.8, brightness: 0.9) } } SCREEN RECORDING
Topic: UI Frameworks SubTopic: SwiftUI
6
3
463
2d
iOS 27 / SwiftUI — Search tab .prominent doesn't morph on activation (field goes to top, tabs don't collapse)
I'm building an app with Xcode 27. I had trouble getting the tab bar to detach the search button onto the trailing side. I found that the search behaviour now works through the new .prominent treatment on iOS 27 (with Tab(role: .search)), and the detached button now displays correctly. My remaining problem is the activation behaviour. Expected (as in the system Phone / News / App Store apps): tapping the search button morphs the tab bar — the other tabs collapse into a single leading button and the search field expands at the bottom, centered. Actual: tapping the search tab pushes the search field to the top of the screen, attached to the navigation bar, with no morph animation and no tab collapse. Setup: Xcode 27.0 beta, iOS 27.0 on a physical iPhone Air Same with the Simulator (by the way... i also have some terrible lag with my app on Simulator + my iPhone even with a release version, but not throught TestFlight... strange...) 3 standard tabs + 1 search tab Single .searchable(text:) applied directly on the TabView (not on the inner NavigationStack) No .introspect or third-party modifiers on the search NavigationStack Is the morph + tab-collapse behaviour automatic for the search tab on iPhone, or does it require an additional modifier/configuration I'm missing? Can anyone confirm whether this morph works on a stable iOS 26 build with equivalent code? I suspect a regression in the 27.0 beta SDK, since the detached button works but the activation morph does not. I'm not yet an expert... so maybe i'm doing something wrong. I'll file Feedback if this is confirmed as a beta bug. Thanks.
Topic: UI Frameworks SubTopic: SwiftUI
0
0
37
4d
OS27 LazyVGrid hops like crazy on scroll up.
I’m not sure if this is a ”care later in the summer” situation, but on beta 1, with an .adaptive Grid Item, a scrolling LazyVGrid will hop and “bounce” when scrolling back up from the bottom of the grid. I can see the scrollbar visibly hopping as item views are re-created. Anyone else seeing this?
2
1
73
4d
iOS 26, SwiftUI .sheet Background Color has Gray/Green Tint on iPad
On iPadOS 26 (up to beta 7), .sheet backgrounds have a dark green tint on Dark Mode and a gray tint on Light Mode. This is clearly noticeable on both the Canvas/Simulator and a physical device. Here's a sample View that shows the issue: import SwiftUI struct ContentView: View { @State private var isPresenting: Bool = false var body: some View { VStack { Image(systemName: "globe") .imageScale(.large) .foregroundStyle(.tint) Text("Hello, world!") Button("Show Sheet") { isPresenting.toggle() } } .sheet(isPresented: $isPresenting) { VStack { HStack { Spacer() Button("Cancel", systemImage: "xmark.circle.fill") { } .foregroundStyle(.secondary) .labelStyle(.iconOnly) .buttonStyle(.plain) .contentShape(.circle) } TabView { Tab("Tab 1", systemImage: "cart") { Text("Hello, tab 1") } Tab("Tab 2", systemImage: "cart") { Text("Hello, tab 2") } } } .scenePadding() } .padding() .preferredColorScheme(.dark) } } #Preview { ContentView() } Is this the expected behavior with the new OS? Anyone else seeing this?
Topic: UI Frameworks SubTopic: SwiftUI
3
1
507
5d
NSViewRepresentable updates triggered by .onChange ignore SwiftUI Transactions on macOS
I am encountering a systemic issue on macOS where NSViewRepresentable (and some native container views like Table) completely discard explicit SwiftUI animations when the state change is handled via an .onChange modifier. While the exact same reactive architecture produces fluid animations on iOS, the AppKit bridge on macOS snaps the frame updates instantly. I have filed a formal bug report for this behavior, but I want to open this up to the community to see if anyone has found a cleaner architectural workaround. The Problem When observing a state change (e.g., via @AppStorage, @SceneStorage, or local state) using .onChange, applying a withAnimation block fails to animate the underlying layer changes in an AppKit representable view. // The Reactive Pattern that breaks on macOS .onChange(of: toggle) { newValue in withAnimation(.easeInOut(duration: 0.5)) { self.targetColor = newValue ? .systemBlue : .systemRed } } The Diagnostic Anomaly If you inspect context.transaction inside the updateNSView(_:context:) method during this lifecycle pass, SwiftUI reports that the transaction is animated: func updateNSView(_ nsView: NSView, context: Context) { // Prints 'true', indicating SwiftUI thinks it's animating print("Is Animated: \(context.transaction.animation != nil)") // Result: Snaps instantly. No animation occurs. nsView.layer?.backgroundColor = targetColor.cgColor } Why It Happens (The Double-Commit) It appears that on macOS, .onChange flushes a static layout transaction to the window layer immediately upon the state mutating. By the time the withAnimation block evaluates inside the closure, the AppKit backing layer has already processed a implicit setDisableActions(true) directive. The GPU pipeline for that transaction frame is effectively closed, despite what the context.transaction metadata claims. The Low-Level Workaround To force the AppKit bridge to respect the animation intent, I have to manually drop into Core Animation inside updateNSView and explicitly veto SwiftUI's action-disabling behavior: func updateNSView(_ nsView: NSView, context: Context) { CATransaction.begin() if context.transaction.animation != nil { // Explicitly override SwiftUI's implicit frame lock CATransaction.setDisableActions(false) CATransaction.setAnimationDuration(0.5) // Hardcoded fallback match } else { CATransaction.setDisableActions(true) } nsView.layer?.backgroundColor = targetColor.cgColor CATransaction.commit() } My Questions: Is this intentional behavior due to how AppKit's layer-backed architectures handle frame integrity vs. iOS's fluid layout engine? Has anyone found a way to bridge SwiftUI's Animation type curves (like .spring()) cleanly down into the CATransaction or NSAnimationContext layer without hardcoding durations inside updateNSView? Is there a purely "Reactive" paradigm that avoids mutating state at the primary action source (e.g., forcing a Button to own the animation logic) while maintaining fluid transitions on macOS?
Topic: UI Frameworks SubTopic: SwiftUI
1
0
88
5d
Liquid Glass tab bar doesn't update its background on tab switch until I scroll — expected? Workaround?
Hi, On iOS 26 I'm seeing a behavior with the new Liquid Glass tab bar in SwiftUI and I can't tell if it's expected or if there's a supported workaround. When I switch tabs in a TabView, the floating Liquid Glass tab bar keeps the glass appearance it derived from the previous tab's content. It only refreshes to match the newly selected tab once I scroll the new tab's scroll view — even by a single point. In Apple's own apps (e.g. Home) the bar updates immediately on tab change, which makes me think this isn't intended. I reduced it to a minimal example — no images, no glassEffect, no NavigationStack, just gradients: struct ContentView: View { enum TabID: Hashable { case blue, green, dark } @State private var selection: TabID = .blue var body: some View { TabView(selection: $selection) { Tab("Blue", systemImage: "drop.fill", value: .blue) { Page(colors: [.blue, .indigo]) } Tab("Green", systemImage: "leaf.fill", value: .green) { Page(colors: [.green, .teal]) } Tab("Dark", systemImage: "moon.fill", value: .dark) { Page(colors: [.gray, .black]) } } } } struct Page: View { let colors: [Color] var body: some View { ZStack { LinearGradient(colors: colors, startPoint: .top, endPoint: .bottom) .ignoresSafeArea() ScrollView { ForEach(0..<40, id: \.self) { Text("Row \($0)") .foregroundStyle(.white) .frame(maxWidth: .infinity, alignment: .leading) .padding() } } } } } Steps to reproduce: Run on a real device (iPhone 17 Pro Max, iOS 26.5.1, Xcode 26). Switch between the three tabs without scrolling. Expected: the tab bar's glass tint adapts to the destination tab's gradient immediately on switch. Actual: the bar keeps the previous tab's tint until I scroll the new view ~1pt. What I've already tried (no luck): Both the modern Tab API and the legacy .tabItem / .tag style — same behavior. Faking a scroll with setContentOffset (animated and non-animated) — doesn't reliably trigger the refresh; only a genuine user scroll does. toolbarBackgroundVisibility(...) and forcing a color scheme — no effect. Questions: Is this a known issue / expected behavior in iOS 26? Is there a supported way to make the tab bar re-sample its Liquid Glass backdrop when the selected tab changes (without requiring a user scroll)? I've also filed it via Feedback Assistant. Thanks! Configuration: iPhone 17 Pro Max, iOS 26.5.1, Xcode 26.
0
0
65
5d
Menu in the bottom bar flies to the top of the screen
I have a Menu in a Toolbar (specifically, the .bottomBar). If I open the menu quickly after it appears (within a few seconds), it flies to the top of the screen. I've created a minimum woking example below. This appears to be a pretty glaring iOS 26 bug that has been present since the early betas, but I can't seem to find much discussion about it (apart from this post from 8 months ago), so I'm wondering if I might be doing something wrong. Or maybe someone managed to figure out a workaround. If the Menu is very simple (just Text items), it seems to be okay. But if the Menu is even slightly complex (e.g. includes icons), then it exhibits the flying behavior. I've also been able to reproduce this bug under different types of navigation component (e.g. NavigationSplitView). I'm seeing this behavior in the current version of iOS (26.2.1), both on device and in the simulator. MWE struct ContentView: View { var body: some View { NavigationStack { VStack { NavigationLink("Go to Detail") { DetailView() } } .navigationTitle("Root") } } } struct DetailView: View { var body: some View { VStack { Text("Detail View") } .navigationTitle("Detail") .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .bottomBar) { Menu { Button { } label: { Label("Delete", systemImage: "trash") } } label: { Image(systemName: "ellipsis.circle") } } } } }
Topic: UI Frameworks SubTopic: SwiftUI
3
1
268
6d
NavigationSplitView no longer pops back to the root view when selection = nil in iOS 26.4 (with a nested TabView)
In iOS 26.4 (iPhone, not iPad), when a NavigationSplitView is combined with a nested TabView, it no longer pops back to the root sidebar view when the List selection is set to nil. This has been working fine for at least a few years, but has just stopped working in iOS 26.4. Here's a minimal working example: import SwiftUI struct ContentView: View { @State var articles: [Article] = [Article(articleTitle: "Dog"), Article(articleTitle: "Cat"), Article(articleTitle: "Mouse")] @State private var selectedArticle: Article? = nil var body: some View { NavigationSplitView { TabView { Tab { List(articles, selection: $selectedArticle) { article in Button { selectedArticle = article } label: { Text(article.title) } } } label: { Label("Explore", systemImage: "binoculars") } } } detail: { Group { if let selectedArticle { Text(selectedArticle.title) } else { Text("No selected article") } } .navigationBarBackButtonHidden(true) .toolbar { ToolbarItem(placement: .topBarTrailing) { Button("Close", systemImage: "xmark") { selectedArticle = nil } } } } } } struct Article: Identifiable, Hashable { let id: String let title: String init(articleTitle: String) { self.id = articleTitle self.title = articleTitle } } First, I'm aware that nesting a TabView inside a NavigationSplitView is frowned upon: Apple seems to prefer NavigationSplitView nested inside a Tab. However, for my app, that leads to a very confusing user experience. Users quickly get lost because they end up with different articles open in different tabs and it doesn't align well with my core distinction between two "modes": article selection mode and article reading mode. When the user is in article selection mode (sidebar view), they can pick between different ways of selecting an article (Explore, Bookmarks, History, Search), which are implemented as "tabs". When they pick an article from any tab they jump into article reading mode (the detail view). Second, I'm using .navigationBarBackButtonHidden(true) to remove the auto back button that pops back to the sidebar view. This button does still work in iOS 26.4, even with the nested TabView. However, I can't use the auto back button because my detail view is actually a WebView with its own back/forward logic and UI. Therefore, I need a separate close button to exit from the detail view. My close button sets selectedArticle to nil, which (pre-iOS 26.4) would trigger the NavigationSplitView to pop back to the sidebar view. For some reason, in iOS 26.4 the NavigationSplitView doesn't seem to bind correctly to the List's selection parameter, specifically when there's a TabView nested between them. Or, rather, it binds, but fails to pop back when selection becomes nil. One option is to replace NavigationSplitView with NavigationStack (on iPhone). NavigationStack still works with a nested TabView, but it creates other downstream issues for me (as well as forcing me to branch for iPhone and iPad), so I'd prefer to continue using NavigationSplitView. Does anyone have any ideas about how to work around this problem? Is there some way of explicitly telling NavigationSplitView to pop back to the sidebar view on iPhone? (I've tried setting the column visibility but nothing seems to work). Thanks for any help!
2
1
242
6d
MapKit MapCamera
SwiftUI Map with MapCamera jerks on every GPS update instead of animating smoothly I'm trying to make camera to follow the user smoothly during navigation using MapCamera with heading and pitch, similar to Apple Maps or Google Maps. The camera updates on every GPS tick but instead of animating smoothly between positions it jerks , it snaps to the new position, pauses, snaps again, pauses...terrible UX. The blue user location (UserAnnotation) puck moves completely smoothly. Only the camera jerks I have tried all sort of animations and interpolation you may think of. Something is just not right, must be something missing from the puzzle. I have prepared a minimal reproducible example so you can copy and paste the only thing needed is to add the Privacy - Location When In Use Usage Description Run in Simulator, go to Features > Location > Freeway Drive and tap on Track then you'll notice how camera is following then stop then following and stops again Don't bother using AI, he has no clue what's this all about. I also went through docs to find anything useful like a magic modifier, but no joy Here is a video hosted online as well: [https://streamable.com/ear9cv] And a code snippet copy paste import MapKit import CoreLocation // You'll only need to add Privacy - Location When In Use Usage Description to the Info tab struct ContentView: View { @State private var locationManager = LocationManagerDelegate() @State private var cameraPosition: MapCameraPosition = .userLocation(followsHeading: false, fallback: .automatic) @State private var isTracking: Bool = false @State private var lastKnownHeading: Double = 0 var body: some View { Map(position: $cameraPosition) { UserAnnotation() } .onChange(of: locationManager.location) { _, location in guard isTracking, let location else { return } withAnimation(.linear(duration: 0.5)) { cameraPosition = .camera(MapCamera( centerCoordinate: location.coordinate, distance: 1000, heading: location.course, pitch: 60 )) } } .safeAreaInset(edge: .bottom) { // Added to the safeAreaInset to keep the Apple Logo visible Button("Track") { isTracking.toggle() locationManager.requestPermission() locationManager.startNavigating() } .buttonStyle(.glassProminent) .buttonSizing(.flexible) .controlSize(.extraLarge) .padding(.horizontal) } } } @MainActor @Observable final class LocationManagerDelegate: NSObject, CLLocationManagerDelegate { var location: CLLocation? var authorizationStatus: CLAuthorizationStatus = .notDetermined let manager = CLLocationManager() private var liveUpdateTask: Task<Void, Never>? override init() { super.init() manager.delegate = self manager.desiredAccuracy = kCLLocationAccuracyBestForNavigation authorizationStatus = manager.authorizationStatus } func requestPermission() { manager.requestWhenInUseAuthorization() } func startNavigating() { liveUpdateTask = Task { do { for try await update in CLLocationUpdate.liveUpdates(.automotiveNavigation) { guard let newLocation = update.location else { continue } self.location = newLocation } } catch { print("Live updates error: \(error)") } } } func stopNavigating() { liveUpdateTask?.cancel() liveUpdateTask = nil manager.requestLocation() } func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { location = locations.last } func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) { authorizationStatus = manager.authorizationStatus } }
0
0
39
6d
How to build a picker wheel similar as the Calendar App?
How to build the below UI using SwiftUI? I tried to use Picker with wheel style, but it is not the same as the screenshot. The screenshot came from the iOS built-in calendar app. Add a new calendar event Click "Repeat" Choose "Custom" Click "Every day" The required picker wheel will be displayed Picker("Every", selection: $interval) { ForEach(1..<366) { interval in Text("\(interval)").tag(interval) } } .pickerStyle(.wheel)
0
0
57
6d
How do I get SwiftUI to let me determine a custom frame size for NSTextField
I have a NSViewRepresentable that wraps an NSTextField subclass which is displayed as larger than your typical text field. SwiftUI doesn't seem to allow me to set the size of the view when the underlying is an NSTextField. It forces it as a single line field. I've tried both setting the frame on creation as well as using SwiftUI .frame(width:height:) on the represented view. I always end up with a single line field. struct BigTextField: NSViewRepresentable { @Binding var text: String class Coordinator: NSObject, NSTextFieldDelegate { var parent: BigTextField init(_ parent: BigTextField) { self.parent = parent } func controlTextDidChange(_ obj: Notification) { if let textField = obj.object as? NSTextField { parent.text = textField.stringValue } } } func makeCoordinator() -> Coordinator { Coordinator(self) } func makeNSView(context: Context) -> NSTextField { //let frame = NSRect(x: 0, y: 0, width: 350, height: 140) //let textField = NSTextField(frame: frame) let textField = NSTextField() textField.isEditable = true textField.isBordered = true textField.isBezeled = true textField.delegate = context.coordinator // Assign the coordinator return textField } func updateNSView(_ nsView: NSTextField, context: Context) { if nsView.stringValue != text { nsView.stringValue = text } } } I've also included the SwiftUI declaration which demonstrates the problem. struct ContentView: View { @State var text : String = "Test string" var body: some View { VStack { BigTextField(text: $text) .frame(width: 350, height: 140) } .padding() } } NSTextField can be any arbitrary frame size. I already do this from AppKit but am trying to adapt this custom field to work within SwiftUI. SwiftUI seems to override the sizing of this NSViewRepresentable that I give it. Am I missing something here? Is there some way to override SwiftUI's sizing behavior? Thank you.
0
0
56
1w
Back gesture not disabled with navigationBarBackButtonHidden(true) when using .zoom transition
[Submitted as FB22226720] For a NavigationStack destination, applying .navigationBarBackButtonHidden(true) hides the back button and also disables the interactive left-edge back gesture when using the standard push navigation transition. However, when the destination uses .navigationTransition(.zoom), the back button is hidden but the left-edge back gesture is still available—it can still be dismissed even though back is intentionally suppressed. This creates inconsistent behavior between navigation transition styles. navigationBarBackButtonHidden(_:) works with a standard push transition, but not with .navigationTransition(.zoom). In the code below, .interactiveDismissDisabled(true) is also applied as another attempt to suppress the back-swipe gesture, but it has no effect. As a result, there’s currently no clean way to prevent back navigation when using the zoom transition. REPRO STEPS Create an iOS project then replace ContentView with code below, build and run. Leave nav type set to List Push. Open an item. Verify there is no back button, then try the left-edge back gesture. Return to the root view. Change nav type to Grid Zoom. Open an item. Verify there is no back button, then try the left-edge back gesture. ACTUAL In List Push mode, the left-edge back gesture is prevented. In Grid Zoom mode, the back button is hidden, but the left-edge back gesture still works and returns to the previous view. EXPECTED Behavior should be consistent across navigation transition styles. If this configuration is meant to suppress interactive backward navigation for a destination, it should also suppress the left-edge back gesture when using .navigationTransition(.zoom). SCREEN RECORDING SAMPLE CODE struct ContentView: View { private enum NavigationMode: String, CaseIterable { case listPush = "List Push" case gridZoom = "Grid Zoom" } @Namespace private var namespace @State private var navigationMode: NavigationMode = .listPush private let colors: [Color] = [.red, .blue] var body: some View { NavigationStack { VStack(spacing: 16) { Picker("Navigation Type", selection: $navigationMode) { ForEach(NavigationMode.allCases, id: \.self) { mode in Text(mode.rawValue).tag(mode) } } .pickerStyle(.segmented) if navigationMode == .gridZoom { HStack { ForEach(colors.indices, id: \.self) { index in NavigationLink(value: index) { VStack { RoundedRectangle(cornerRadius: 14) .fill(colors[index]) .frame(height: 120) Text("Grid Item \(index + 1)") .font(.subheadline.weight(.medium)) } .padding(12) .frame(maxWidth: .infinity) .background(.quaternary.opacity(0.25), in: RoundedRectangle(cornerRadius: 16)) .matchedTransitionSource(id: index, in: namespace) } .buttonStyle(.plain) } } } else { ForEach(colors.indices, id: \.self) { index in NavigationLink(value: index) { HStack { Circle() .fill(colors[index]) .frame(width: 24, height: 24) Text("List Item \(index + 1)") Spacer() Image(systemName: "chevron.right") .foregroundStyle(.secondary) } .padding() .background(.quaternary.opacity(0.25), in: RoundedRectangle(cornerRadius: 12)) } .buttonStyle(.plain) } } Spacer() } .padding(20) .navigationTitle("Prevent Back Swipe") .navigationSubtitle("Compare Grid Zoom vs List Push") .navigationDestination(for: Int.self) { index in if navigationMode == .gridZoom { DetailView(color: colors[index]) .navigationTransition(.zoom(sourceID: index, in: namespace)) } else { DetailView(color: colors[index]) } } } } } private struct DetailView: View { @Environment(\.dismiss) private var dismiss let color: Color var body: some View { ZStack { color.ignoresSafeArea() Text("Try left-edge swipe back") .font(.title.bold()) .multilineTextAlignment(.center) .padding(.horizontal, 24) } .navigationBarBackButtonHidden(true) .interactiveDismissDisabled(true) .toolbar { ToolbarItem(placement: .topBarTrailing) { Button("Close", action: dismiss.callAsFunction) } } } }
3
0
738
1w
TextKit 2 + SwiftUI (NSViewRepresentable): NSTextLayoutManager rendering attributes don’t reliably draw/update
I’m embedding an NSTextView (TextKit 2) inside a SwiftUI app using NSViewRepresentable. I’m trying to highlight dynamic subranges (changing as the user types) by providing per-range rendering attributes via NSTextLayoutManager’s rendering-attributes mechanism. The issue: the highlight is unreliable. Often, the highlight doesn’t appear at all even though the delegate/data source is returning attributes for the expected range. Sometimes it appears once, but then it stops updating even when the underlying “highlight range” changes. This feels related to SwiftUI - AppKit layout issue when using NSViewRepresentable (as said in https://developer.apple.com/documentation/swiftui/nsviewrepresentable). What I’ve tried Updating the state that drives the highlight range and invalidating layout fragments / asking for relayout Ensuring all updates happen on the main thread. Calling setNeedsDisplay(_:) on the NSViewRepresentable’s underlying view. Toggling the SwiftUI view identity (e.g. .id(...)) to force reconstruction (works, but too expensive / loses state). Question In a SwiftUI + NSViewRepresentable setup with TextKit 2, what is the correct way to make NSTextLayoutManager re-query and redraw rendering attributes when my highlight ranges change? Is there a recommended invalidation call for TextKit 2 to trigger re-rendering of rendering attributes? Or is this a known limitation when hosting NSTextView inside SwiftUI, where rendering attributes aren’t reliably invalidated? If this approach is fragile, is there a better pattern for dynamic highlights that avoids mutating the attributed string (to prevent layout/scroll jitter)?
4
0
428
1w
How do you correctly use a SwiftUI View inside an NSToolbarItem?
I've been struggling to get consistent UI and UX behaviour of SwiftUI Views inside NSToolbarItems and was wondering if there is an official way to use them. I've now revisited this issue in macOS 27 and continue to see some idiosyncrasies. In the attached screenshot, you can see that the highlight area on mouse down between to the two buttons is different. This is the easiest example I've come up with that shows SwiftUI Views exhibiting different behaviour than AppKit Views. Two questions: Is an NSHostingView a valid and supported view type for NSToolbarItem.view? If so, are there any rules that govern how the SwiftUI view should be configured? (ex: frame, sizing options, supported SwiftUI Views, preferred "root view" types, etc?) Sample code that created the two NSToolbarItem buttons in the screenshot. macOS 27 ZY21R0CMGL (Public Beta 1) Xcode 27.0 beta Minimum Deployment target: 27.0 // Left-Top SwiftUI Button (Clipped Highlighting) let item = NSToolbarItem(itemIdentifier: itemIdentifier) let rootView = Button { } label: { Image(systemName: "sidebar.trailing") } item.view = NSHostingView(rootView: rootView) // ... snip .. // Right-Bottom AppKit Button (Correct Highlighting) let item = NSToolbarItem(itemIdentifier: itemIdentifier) item.image = NSImage(systemSymbolName: "sidebar.trailing", accessibilityDescription: nil) Both screenshots are taken on mouse down.
Replies
1
Boosts
0
Views
25
Activity
1h
How do you make a resizable segmented control in SwiftUI for macOS?
In SwiftUI for macOS, how do I configure a Picker as a segmented control to have a flexible width? This design pattern is present in Xcode 26 at the top of the sidebar and inspector panel. I can't figure out the combination of view modifiers to achieve a similar look. import SwiftUI struct ContentView: View { @State private var selection = 0 var body: some View { VStack { Picker("", selection: $selection) { Image(systemName: "doc") Image(systemName: "folder") Image(systemName: "gear") Image(systemName: "globe") .frame(maxWidth: .infinity) // Doesn't do anything. } .labelsHidden() .pickerStyle(.segmented) .frame(maxWidth: .infinity) // Doesn't affect segment sizes. Spacer() } } } I want the entire Picker to fill the width and for each segment to be of equal widths. How? In AppKit I would use AutoLayout for the flexible width and NSSegmentedControl.segmentDistribution for the segment widths. Is there a SwiftUI equivalent? macOS 26 / Xcode 26.3
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Replies
1
Boosts
0
Views
137
Activity
7h
Push new views to sidebar when using NavigationPath
I have an existing app that uses NavigationSplitView with a detail view that is never changed and just updates to show changes in data. All navigation changes the sidebar only using NavigationLinks with .isDetailLink(false). Now I'm wanting to use NavigationPath with a simple router and enums. import SwiftUI @main struct NavRouterApp: App { @State private var router = Router() var body: some Scene { WindowGroup { NavigationStack(path: $router.navPath) { ContentView() .navigationDestination(for: AppRoute.self) { route in switch route { case .citizens: ContentView() .environment(router) case .citizen: EmptyView() .environment(router) case .tasform2: TASForm2View() .environment(router) case .start: StartView() .environment(router) case .editcharacteristics: EmptyView() .environment(router) case .basicdata(let citizen): BasicDataView(citizen: citizen) .environment(router) .navigationBarBackButtonHidden(true) case .characteristics: EmptyView() .environment(router) } } } .environment(router) } } } The problem is that pushing a subview now replaces the entire content instead of just the sidebar. Pushing a subview with NavigationSplitView would update the sidebar as desired but I would have to replace the detail view, which is not a good idea. I haven't been able to find any way to accomplish what I want. Suggestions?
Topic: UI Frameworks SubTopic: SwiftUI
Replies
4
Boosts
0
Views
59
Activity
1d
Multiline TextField in a List does not keep the caret visible on iPad
On iPad, a multiline TextField inside a List does not scroll when the text wraps to a new line. The caret and newly entered text can move behind the keyboard until the user scrolls manually. The same code works correctly on iPhone. struct ContentView: View { @State var text = "" var body: some View { List { Color.red.frame(width: 200, height: 500) // When the caret goes to the next line during the text input the scroll doesn't follow it. // So the new text is being overlapped by the keyboard until you scroll manually. TextField( "Run me on iPad and start entering multiline text with keyboard up", text: $text, axis: .vertical ) .lineLimit(1...) Color.red.frame(width: 200, height: 500) } } }
Topic: UI Frameworks SubTopic: SwiftUI
Replies
0
Boosts
0
Views
17
Activity
2d
Avoiding crashes in iOS 27 when selected tab is hidden from TabView
I read through the iOS 27 release notes and the following caught my attention: In apps built with the iOS 27.0 and iPadOS 27.0 SDKs, a TabView enforces that its selection is set to a visible tab. TabView might crash when its selection is set to a hidden or otherwise unavailable tab. (164516837) My app has a partially configurable tab bar. If the user currently has tab X selected (in the TabView) and then chooses to hide tab X (by toggling an option in my app's settings), then I guess this could cause a crash. My app already has logic in place so that if the user hides the tab that is currently the selected tab, then the currently selected tab is changed to another one that is always visible. This is handled by an .onChange view modifier (basically: on change of setting, if selected tab is now hidden, change selected tab to something else). However, I'm concerned about the potential for race conditions with this set up. For example, if the TabView re-renders before the selected tab is changed to an available tab, then this could cause a crash. My questions for Apple: Are programmatically configurable TabViews officially supported, or are you recommending against this practice? If they are, what defensive steps are recommended to avoid crashes (e.g. adding a small delay to make sure that the selected tab is changed first before removing a tab from the TabView).
Topic: UI Frameworks SubTopic: SwiftUI
Replies
2
Boosts
0
Views
61
Activity
2d
Different keyboards show up for KeyboardType .decimalPad
Environment: iOS 26; iPad Mini/Air/Pro Problem: In a TextField, I am using a keyboard with the type .decimalPad. When I initially tap into the TextField, the "popover" keyboard (i.e. the decimalPad) shows up and focusses the TextField. However, when I click outside the TextField (to dismiss the keyboard), the TextField is still focussed (the keyboard was dismissed though). When reentering in the TextField, another keyboard (from the bottom of the screen) appears (most likely .numeric). Does anybody know how to solve this? What I already tried: I tried listening to the dismissal of the keyboard to manually set the FocusState to nil. However, the dismissal of the "popover/decimal" keyboard is not recognized as such a dismissal. I also tried to build a custom component out of that, but then I lose the TextField behavior, conflicting with HIG.
Replies
2
Boosts
0
Views
75
Activity
2d
Zoom transition source tile lags after back navigation when LazyVGrid is scrolled immediately
[Submitted as FB21961572] When navigating from a tile in a scrolling LazyVGrid to a child view using .navigationTransition(.zoom) and then returning, the source tile can lag behind the rest of the grid if scrolling starts immediately after returning. The lag becomes more pronounced as tile content gets more complex; in this simplified sample, it can seem subtle, but in production-style tiles (as used in both of my apps), it is clearly visible and noticeable. This may be related to another issue I recently filed: Source item disappears after swipe-back with .navigationTransition(.zoom) CONFIGURATION Platform: iOS Simulator and physical device Navigation APIs: matchedTransitionSource + navigationTransition(.zoom) Container: ScrollView + LazyVGrid Sample project: ZoomTransition (DisappearingTile).zip REPRO STEPS Create a new iOS project and replace ContentView with the code below. Run the app in sim or physical device Tap any tile in the scrolling grid to navigate to the child view. Return to the grid (back button or edge swipe). Immediately scroll the grid. Watch the tile that was just opened. EXPECTED All tiles should move together as one coherent scrolling grid, with no per-item lag or desynchronization. ACTUAL The tile that was just opened appears to trail behind neighboring tiles for a short time during immediate scrolling after returning. MINIMAL CODE SAMPLE import SwiftUI struct ContentView: View { @Namespace private var namespace private let tileCount = 40 private let columns = [GridItem(.adaptive(minimum: 110), spacing: 12)] var body: some View { NavigationStack { ScrollView { LazyVGrid(columns: columns, spacing: 12) { ForEach(0..<tileCount, id: \.self) { index in NavigationLink(value: index) { RoundedRectangle(cornerRadius: 16) .fill(color(for: index)) .frame(height: 110) .overlay(alignment: .bottomLeading) { Text("\(index + 1)") .font(.headline) .foregroundStyle(.white) .padding(10) } .matchedTransitionSource(id: index, in: namespace) } .buttonStyle(.plain) } } .padding(16) } .navigationTitle("Zoom Transition Grid") .navigationSubtitle("Open tile, go back, then scroll immediately") .navigationDestination(for: Int.self) { index in Rectangle() .fill(color(for: index)) .ignoresSafeArea() .navigationTransition(.zoom(sourceID: index, in: namespace)) } } } private func color(for index: Int) -> Color { let hue = Double(index % 20) / 20.0 return Color(hue: hue, saturation: 0.8, brightness: 0.9) } } SCREEN RECORDING
Topic: UI Frameworks SubTopic: SwiftUI
Replies
6
Boosts
3
Views
463
Activity
2d
iOS 27 / SwiftUI — Search tab .prominent doesn't morph on activation (field goes to top, tabs don't collapse)
I'm building an app with Xcode 27. I had trouble getting the tab bar to detach the search button onto the trailing side. I found that the search behaviour now works through the new .prominent treatment on iOS 27 (with Tab(role: .search)), and the detached button now displays correctly. My remaining problem is the activation behaviour. Expected (as in the system Phone / News / App Store apps): tapping the search button morphs the tab bar — the other tabs collapse into a single leading button and the search field expands at the bottom, centered. Actual: tapping the search tab pushes the search field to the top of the screen, attached to the navigation bar, with no morph animation and no tab collapse. Setup: Xcode 27.0 beta, iOS 27.0 on a physical iPhone Air Same with the Simulator (by the way... i also have some terrible lag with my app on Simulator + my iPhone even with a release version, but not throught TestFlight... strange...) 3 standard tabs + 1 search tab Single .searchable(text:) applied directly on the TabView (not on the inner NavigationStack) No .introspect or third-party modifiers on the search NavigationStack Is the morph + tab-collapse behaviour automatic for the search tab on iPhone, or does it require an additional modifier/configuration I'm missing? Can anyone confirm whether this morph works on a stable iOS 26 build with equivalent code? I suspect a regression in the 27.0 beta SDK, since the detached button works but the activation morph does not. I'm not yet an expert... so maybe i'm doing something wrong. I'll file Feedback if this is confirmed as a beta bug. Thanks.
Topic: UI Frameworks SubTopic: SwiftUI
Replies
0
Boosts
0
Views
37
Activity
4d
OS27 LazyVGrid hops like crazy on scroll up.
I’m not sure if this is a ”care later in the summer” situation, but on beta 1, with an .adaptive Grid Item, a scrolling LazyVGrid will hop and “bounce” when scrolling back up from the bottom of the grid. I can see the scrollbar visibly hopping as item views are re-created. Anyone else seeing this?
Replies
2
Boosts
1
Views
73
Activity
4d
iOS 26, SwiftUI .sheet Background Color has Gray/Green Tint on iPad
On iPadOS 26 (up to beta 7), .sheet backgrounds have a dark green tint on Dark Mode and a gray tint on Light Mode. This is clearly noticeable on both the Canvas/Simulator and a physical device. Here's a sample View that shows the issue: import SwiftUI struct ContentView: View { @State private var isPresenting: Bool = false var body: some View { VStack { Image(systemName: "globe") .imageScale(.large) .foregroundStyle(.tint) Text("Hello, world!") Button("Show Sheet") { isPresenting.toggle() } } .sheet(isPresented: $isPresenting) { VStack { HStack { Spacer() Button("Cancel", systemImage: "xmark.circle.fill") { } .foregroundStyle(.secondary) .labelStyle(.iconOnly) .buttonStyle(.plain) .contentShape(.circle) } TabView { Tab("Tab 1", systemImage: "cart") { Text("Hello, tab 1") } Tab("Tab 2", systemImage: "cart") { Text("Hello, tab 2") } } } .scenePadding() } .padding() .preferredColorScheme(.dark) } } #Preview { ContentView() } Is this the expected behavior with the new OS? Anyone else seeing this?
Topic: UI Frameworks SubTopic: SwiftUI
Replies
3
Boosts
1
Views
507
Activity
5d
NSViewRepresentable updates triggered by .onChange ignore SwiftUI Transactions on macOS
I am encountering a systemic issue on macOS where NSViewRepresentable (and some native container views like Table) completely discard explicit SwiftUI animations when the state change is handled via an .onChange modifier. While the exact same reactive architecture produces fluid animations on iOS, the AppKit bridge on macOS snaps the frame updates instantly. I have filed a formal bug report for this behavior, but I want to open this up to the community to see if anyone has found a cleaner architectural workaround. The Problem When observing a state change (e.g., via @AppStorage, @SceneStorage, or local state) using .onChange, applying a withAnimation block fails to animate the underlying layer changes in an AppKit representable view. // The Reactive Pattern that breaks on macOS .onChange(of: toggle) { newValue in withAnimation(.easeInOut(duration: 0.5)) { self.targetColor = newValue ? .systemBlue : .systemRed } } The Diagnostic Anomaly If you inspect context.transaction inside the updateNSView(_:context:) method during this lifecycle pass, SwiftUI reports that the transaction is animated: func updateNSView(_ nsView: NSView, context: Context) { // Prints 'true', indicating SwiftUI thinks it's animating print("Is Animated: \(context.transaction.animation != nil)") // Result: Snaps instantly. No animation occurs. nsView.layer?.backgroundColor = targetColor.cgColor } Why It Happens (The Double-Commit) It appears that on macOS, .onChange flushes a static layout transaction to the window layer immediately upon the state mutating. By the time the withAnimation block evaluates inside the closure, the AppKit backing layer has already processed a implicit setDisableActions(true) directive. The GPU pipeline for that transaction frame is effectively closed, despite what the context.transaction metadata claims. The Low-Level Workaround To force the AppKit bridge to respect the animation intent, I have to manually drop into Core Animation inside updateNSView and explicitly veto SwiftUI's action-disabling behavior: func updateNSView(_ nsView: NSView, context: Context) { CATransaction.begin() if context.transaction.animation != nil { // Explicitly override SwiftUI's implicit frame lock CATransaction.setDisableActions(false) CATransaction.setAnimationDuration(0.5) // Hardcoded fallback match } else { CATransaction.setDisableActions(true) } nsView.layer?.backgroundColor = targetColor.cgColor CATransaction.commit() } My Questions: Is this intentional behavior due to how AppKit's layer-backed architectures handle frame integrity vs. iOS's fluid layout engine? Has anyone found a way to bridge SwiftUI's Animation type curves (like .spring()) cleanly down into the CATransaction or NSAnimationContext layer without hardcoding durations inside updateNSView? Is there a purely "Reactive" paradigm that avoids mutating state at the primary action source (e.g., forcing a Button to own the animation logic) while maintaining fluid transitions on macOS?
Topic: UI Frameworks SubTopic: SwiftUI
Replies
1
Boosts
0
Views
88
Activity
5d
Liquid Glass tab bar doesn't update its background on tab switch until I scroll — expected? Workaround?
Hi, On iOS 26 I'm seeing a behavior with the new Liquid Glass tab bar in SwiftUI and I can't tell if it's expected or if there's a supported workaround. When I switch tabs in a TabView, the floating Liquid Glass tab bar keeps the glass appearance it derived from the previous tab's content. It only refreshes to match the newly selected tab once I scroll the new tab's scroll view — even by a single point. In Apple's own apps (e.g. Home) the bar updates immediately on tab change, which makes me think this isn't intended. I reduced it to a minimal example — no images, no glassEffect, no NavigationStack, just gradients: struct ContentView: View { enum TabID: Hashable { case blue, green, dark } @State private var selection: TabID = .blue var body: some View { TabView(selection: $selection) { Tab("Blue", systemImage: "drop.fill", value: .blue) { Page(colors: [.blue, .indigo]) } Tab("Green", systemImage: "leaf.fill", value: .green) { Page(colors: [.green, .teal]) } Tab("Dark", systemImage: "moon.fill", value: .dark) { Page(colors: [.gray, .black]) } } } } struct Page: View { let colors: [Color] var body: some View { ZStack { LinearGradient(colors: colors, startPoint: .top, endPoint: .bottom) .ignoresSafeArea() ScrollView { ForEach(0..<40, id: \.self) { Text("Row \($0)") .foregroundStyle(.white) .frame(maxWidth: .infinity, alignment: .leading) .padding() } } } } } Steps to reproduce: Run on a real device (iPhone 17 Pro Max, iOS 26.5.1, Xcode 26). Switch between the three tabs without scrolling. Expected: the tab bar's glass tint adapts to the destination tab's gradient immediately on switch. Actual: the bar keeps the previous tab's tint until I scroll the new view ~1pt. What I've already tried (no luck): Both the modern Tab API and the legacy .tabItem / .tag style — same behavior. Faking a scroll with setContentOffset (animated and non-animated) — doesn't reliably trigger the refresh; only a genuine user scroll does. toolbarBackgroundVisibility(...) and forcing a color scheme — no effect. Questions: Is this a known issue / expected behavior in iOS 26? Is there a supported way to make the tab bar re-sample its Liquid Glass backdrop when the selected tab changes (without requiring a user scroll)? I've also filed it via Feedback Assistant. Thanks! Configuration: iPhone 17 Pro Max, iOS 26.5.1, Xcode 26.
Replies
0
Boosts
0
Views
65
Activity
5d
Menu in the bottom bar flies to the top of the screen
I have a Menu in a Toolbar (specifically, the .bottomBar). If I open the menu quickly after it appears (within a few seconds), it flies to the top of the screen. I've created a minimum woking example below. This appears to be a pretty glaring iOS 26 bug that has been present since the early betas, but I can't seem to find much discussion about it (apart from this post from 8 months ago), so I'm wondering if I might be doing something wrong. Or maybe someone managed to figure out a workaround. If the Menu is very simple (just Text items), it seems to be okay. But if the Menu is even slightly complex (e.g. includes icons), then it exhibits the flying behavior. I've also been able to reproduce this bug under different types of navigation component (e.g. NavigationSplitView). I'm seeing this behavior in the current version of iOS (26.2.1), both on device and in the simulator. MWE struct ContentView: View { var body: some View { NavigationStack { VStack { NavigationLink("Go to Detail") { DetailView() } } .navigationTitle("Root") } } } struct DetailView: View { var body: some View { VStack { Text("Detail View") } .navigationTitle("Detail") .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .bottomBar) { Menu { Button { } label: { Label("Delete", systemImage: "trash") } } label: { Image(systemName: "ellipsis.circle") } } } } }
Topic: UI Frameworks SubTopic: SwiftUI
Replies
3
Boosts
1
Views
268
Activity
6d
NavigationSplitView no longer pops back to the root view when selection = nil in iOS 26.4 (with a nested TabView)
In iOS 26.4 (iPhone, not iPad), when a NavigationSplitView is combined with a nested TabView, it no longer pops back to the root sidebar view when the List selection is set to nil. This has been working fine for at least a few years, but has just stopped working in iOS 26.4. Here's a minimal working example: import SwiftUI struct ContentView: View { @State var articles: [Article] = [Article(articleTitle: "Dog"), Article(articleTitle: "Cat"), Article(articleTitle: "Mouse")] @State private var selectedArticle: Article? = nil var body: some View { NavigationSplitView { TabView { Tab { List(articles, selection: $selectedArticle) { article in Button { selectedArticle = article } label: { Text(article.title) } } } label: { Label("Explore", systemImage: "binoculars") } } } detail: { Group { if let selectedArticle { Text(selectedArticle.title) } else { Text("No selected article") } } .navigationBarBackButtonHidden(true) .toolbar { ToolbarItem(placement: .topBarTrailing) { Button("Close", systemImage: "xmark") { selectedArticle = nil } } } } } } struct Article: Identifiable, Hashable { let id: String let title: String init(articleTitle: String) { self.id = articleTitle self.title = articleTitle } } First, I'm aware that nesting a TabView inside a NavigationSplitView is frowned upon: Apple seems to prefer NavigationSplitView nested inside a Tab. However, for my app, that leads to a very confusing user experience. Users quickly get lost because they end up with different articles open in different tabs and it doesn't align well with my core distinction between two "modes": article selection mode and article reading mode. When the user is in article selection mode (sidebar view), they can pick between different ways of selecting an article (Explore, Bookmarks, History, Search), which are implemented as "tabs". When they pick an article from any tab they jump into article reading mode (the detail view). Second, I'm using .navigationBarBackButtonHidden(true) to remove the auto back button that pops back to the sidebar view. This button does still work in iOS 26.4, even with the nested TabView. However, I can't use the auto back button because my detail view is actually a WebView with its own back/forward logic and UI. Therefore, I need a separate close button to exit from the detail view. My close button sets selectedArticle to nil, which (pre-iOS 26.4) would trigger the NavigationSplitView to pop back to the sidebar view. For some reason, in iOS 26.4 the NavigationSplitView doesn't seem to bind correctly to the List's selection parameter, specifically when there's a TabView nested between them. Or, rather, it binds, but fails to pop back when selection becomes nil. One option is to replace NavigationSplitView with NavigationStack (on iPhone). NavigationStack still works with a nested TabView, but it creates other downstream issues for me (as well as forcing me to branch for iPhone and iPad), so I'd prefer to continue using NavigationSplitView. Does anyone have any ideas about how to work around this problem? Is there some way of explicitly telling NavigationSplitView to pop back to the sidebar view on iPhone? (I've tried setting the column visibility but nothing seems to work). Thanks for any help!
Replies
2
Boosts
1
Views
242
Activity
6d
MapKit MapCamera
SwiftUI Map with MapCamera jerks on every GPS update instead of animating smoothly I'm trying to make camera to follow the user smoothly during navigation using MapCamera with heading and pitch, similar to Apple Maps or Google Maps. The camera updates on every GPS tick but instead of animating smoothly between positions it jerks , it snaps to the new position, pauses, snaps again, pauses...terrible UX. The blue user location (UserAnnotation) puck moves completely smoothly. Only the camera jerks I have tried all sort of animations and interpolation you may think of. Something is just not right, must be something missing from the puzzle. I have prepared a minimal reproducible example so you can copy and paste the only thing needed is to add the Privacy - Location When In Use Usage Description Run in Simulator, go to Features > Location > Freeway Drive and tap on Track then you'll notice how camera is following then stop then following and stops again Don't bother using AI, he has no clue what's this all about. I also went through docs to find anything useful like a magic modifier, but no joy Here is a video hosted online as well: [https://streamable.com/ear9cv] And a code snippet copy paste import MapKit import CoreLocation // You'll only need to add Privacy - Location When In Use Usage Description to the Info tab struct ContentView: View { @State private var locationManager = LocationManagerDelegate() @State private var cameraPosition: MapCameraPosition = .userLocation(followsHeading: false, fallback: .automatic) @State private var isTracking: Bool = false @State private var lastKnownHeading: Double = 0 var body: some View { Map(position: $cameraPosition) { UserAnnotation() } .onChange(of: locationManager.location) { _, location in guard isTracking, let location else { return } withAnimation(.linear(duration: 0.5)) { cameraPosition = .camera(MapCamera( centerCoordinate: location.coordinate, distance: 1000, heading: location.course, pitch: 60 )) } } .safeAreaInset(edge: .bottom) { // Added to the safeAreaInset to keep the Apple Logo visible Button("Track") { isTracking.toggle() locationManager.requestPermission() locationManager.startNavigating() } .buttonStyle(.glassProminent) .buttonSizing(.flexible) .controlSize(.extraLarge) .padding(.horizontal) } } } @MainActor @Observable final class LocationManagerDelegate: NSObject, CLLocationManagerDelegate { var location: CLLocation? var authorizationStatus: CLAuthorizationStatus = .notDetermined let manager = CLLocationManager() private var liveUpdateTask: Task<Void, Never>? override init() { super.init() manager.delegate = self manager.desiredAccuracy = kCLLocationAccuracyBestForNavigation authorizationStatus = manager.authorizationStatus } func requestPermission() { manager.requestWhenInUseAuthorization() } func startNavigating() { liveUpdateTask = Task { do { for try await update in CLLocationUpdate.liveUpdates(.automotiveNavigation) { guard let newLocation = update.location else { continue } self.location = newLocation } } catch { print("Live updates error: \(error)") } } } func stopNavigating() { liveUpdateTask?.cancel() liveUpdateTask = nil manager.requestLocation() } func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { location = locations.last } func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) { authorizationStatus = manager.authorizationStatus } }
Replies
0
Boosts
0
Views
39
Activity
6d
Xcode video rendering performance
video-probe test [object Object]
Topic: UI Frameworks SubTopic: SwiftUI
Replies
0
Boosts
0
Views
22
Activity
6d
How to build a picker wheel similar as the Calendar App?
How to build the below UI using SwiftUI? I tried to use Picker with wheel style, but it is not the same as the screenshot. The screenshot came from the iOS built-in calendar app. Add a new calendar event Click "Repeat" Choose "Custom" Click "Every day" The required picker wheel will be displayed Picker("Every", selection: $interval) { ForEach(1..<366) { interval in Text("\(interval)").tag(interval) } } .pickerStyle(.wheel)
Replies
0
Boosts
0
Views
57
Activity
6d
How do I get SwiftUI to let me determine a custom frame size for NSTextField
I have a NSViewRepresentable that wraps an NSTextField subclass which is displayed as larger than your typical text field. SwiftUI doesn't seem to allow me to set the size of the view when the underlying is an NSTextField. It forces it as a single line field. I've tried both setting the frame on creation as well as using SwiftUI .frame(width:height:) on the represented view. I always end up with a single line field. struct BigTextField: NSViewRepresentable { @Binding var text: String class Coordinator: NSObject, NSTextFieldDelegate { var parent: BigTextField init(_ parent: BigTextField) { self.parent = parent } func controlTextDidChange(_ obj: Notification) { if let textField = obj.object as? NSTextField { parent.text = textField.stringValue } } } func makeCoordinator() -> Coordinator { Coordinator(self) } func makeNSView(context: Context) -> NSTextField { //let frame = NSRect(x: 0, y: 0, width: 350, height: 140) //let textField = NSTextField(frame: frame) let textField = NSTextField() textField.isEditable = true textField.isBordered = true textField.isBezeled = true textField.delegate = context.coordinator // Assign the coordinator return textField } func updateNSView(_ nsView: NSTextField, context: Context) { if nsView.stringValue != text { nsView.stringValue = text } } } I've also included the SwiftUI declaration which demonstrates the problem. struct ContentView: View { @State var text : String = "Test string" var body: some View { VStack { BigTextField(text: $text) .frame(width: 350, height: 140) } .padding() } } NSTextField can be any arbitrary frame size. I already do this from AppKit but am trying to adapt this custom field to work within SwiftUI. SwiftUI seems to override the sizing of this NSViewRepresentable that I give it. Am I missing something here? Is there some way to override SwiftUI's sizing behavior? Thank you.
Replies
0
Boosts
0
Views
56
Activity
1w
Back gesture not disabled with navigationBarBackButtonHidden(true) when using .zoom transition
[Submitted as FB22226720] For a NavigationStack destination, applying .navigationBarBackButtonHidden(true) hides the back button and also disables the interactive left-edge back gesture when using the standard push navigation transition. However, when the destination uses .navigationTransition(.zoom), the back button is hidden but the left-edge back gesture is still available—it can still be dismissed even though back is intentionally suppressed. This creates inconsistent behavior between navigation transition styles. navigationBarBackButtonHidden(_:) works with a standard push transition, but not with .navigationTransition(.zoom). In the code below, .interactiveDismissDisabled(true) is also applied as another attempt to suppress the back-swipe gesture, but it has no effect. As a result, there’s currently no clean way to prevent back navigation when using the zoom transition. REPRO STEPS Create an iOS project then replace ContentView with code below, build and run. Leave nav type set to List Push. Open an item. Verify there is no back button, then try the left-edge back gesture. Return to the root view. Change nav type to Grid Zoom. Open an item. Verify there is no back button, then try the left-edge back gesture. ACTUAL In List Push mode, the left-edge back gesture is prevented. In Grid Zoom mode, the back button is hidden, but the left-edge back gesture still works and returns to the previous view. EXPECTED Behavior should be consistent across navigation transition styles. If this configuration is meant to suppress interactive backward navigation for a destination, it should also suppress the left-edge back gesture when using .navigationTransition(.zoom). SCREEN RECORDING SAMPLE CODE struct ContentView: View { private enum NavigationMode: String, CaseIterable { case listPush = "List Push" case gridZoom = "Grid Zoom" } @Namespace private var namespace @State private var navigationMode: NavigationMode = .listPush private let colors: [Color] = [.red, .blue] var body: some View { NavigationStack { VStack(spacing: 16) { Picker("Navigation Type", selection: $navigationMode) { ForEach(NavigationMode.allCases, id: \.self) { mode in Text(mode.rawValue).tag(mode) } } .pickerStyle(.segmented) if navigationMode == .gridZoom { HStack { ForEach(colors.indices, id: \.self) { index in NavigationLink(value: index) { VStack { RoundedRectangle(cornerRadius: 14) .fill(colors[index]) .frame(height: 120) Text("Grid Item \(index + 1)") .font(.subheadline.weight(.medium)) } .padding(12) .frame(maxWidth: .infinity) .background(.quaternary.opacity(0.25), in: RoundedRectangle(cornerRadius: 16)) .matchedTransitionSource(id: index, in: namespace) } .buttonStyle(.plain) } } } else { ForEach(colors.indices, id: \.self) { index in NavigationLink(value: index) { HStack { Circle() .fill(colors[index]) .frame(width: 24, height: 24) Text("List Item \(index + 1)") Spacer() Image(systemName: "chevron.right") .foregroundStyle(.secondary) } .padding() .background(.quaternary.opacity(0.25), in: RoundedRectangle(cornerRadius: 12)) } .buttonStyle(.plain) } } Spacer() } .padding(20) .navigationTitle("Prevent Back Swipe") .navigationSubtitle("Compare Grid Zoom vs List Push") .navigationDestination(for: Int.self) { index in if navigationMode == .gridZoom { DetailView(color: colors[index]) .navigationTransition(.zoom(sourceID: index, in: namespace)) } else { DetailView(color: colors[index]) } } } } } private struct DetailView: View { @Environment(\.dismiss) private var dismiss let color: Color var body: some View { ZStack { color.ignoresSafeArea() Text("Try left-edge swipe back") .font(.title.bold()) .multilineTextAlignment(.center) .padding(.horizontal, 24) } .navigationBarBackButtonHidden(true) .interactiveDismissDisabled(true) .toolbar { ToolbarItem(placement: .topBarTrailing) { Button("Close", action: dismiss.callAsFunction) } } } }
Replies
3
Boosts
0
Views
738
Activity
1w
TextKit 2 + SwiftUI (NSViewRepresentable): NSTextLayoutManager rendering attributes don’t reliably draw/update
I’m embedding an NSTextView (TextKit 2) inside a SwiftUI app using NSViewRepresentable. I’m trying to highlight dynamic subranges (changing as the user types) by providing per-range rendering attributes via NSTextLayoutManager’s rendering-attributes mechanism. The issue: the highlight is unreliable. Often, the highlight doesn’t appear at all even though the delegate/data source is returning attributes for the expected range. Sometimes it appears once, but then it stops updating even when the underlying “highlight range” changes. This feels related to SwiftUI - AppKit layout issue when using NSViewRepresentable (as said in https://developer.apple.com/documentation/swiftui/nsviewrepresentable). What I’ve tried Updating the state that drives the highlight range and invalidating layout fragments / asking for relayout Ensuring all updates happen on the main thread. Calling setNeedsDisplay(_:) on the NSViewRepresentable’s underlying view. Toggling the SwiftUI view identity (e.g. .id(...)) to force reconstruction (works, but too expensive / loses state). Question In a SwiftUI + NSViewRepresentable setup with TextKit 2, what is the correct way to make NSTextLayoutManager re-query and redraw rendering attributes when my highlight ranges change? Is there a recommended invalidation call for TextKit 2 to trigger re-rendering of rendering attributes? Or is this a known limitation when hosting NSTextView inside SwiftUI, where rendering attributes aren’t reliably invalidated? If this approach is fragile, is there a better pattern for dynamic highlights that avoids mutating the attributed string (to prevent layout/scroll jitter)?
Replies
4
Boosts
0
Views
428
Activity
1w