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 I use containerRelative on a grid in my widget?
I want to display a grid of items in my widget similar to the systemLarge Shortcuts app widget. I use clipShape(.containerRelative) to get the widget corner radius, but items that do not touch a corner in any way do not get this treatment. This is even worse with the extra large widget. How can I apply the corner radius of the widget minus the padding across all items? It does not seem like the radius is exposed outside of that special shape.
1
0
64
4w
Unable to get external display working
Trying to get my iPad app to display a different view on connected external display. Info.plist is setup as follows: <dict> <key>UIApplicationSceneManifest</key> <dict> <key>UIApplicationSupportsMultipleScenes</key> <true/> <key>UISceneConfigurations</key> <dict> <key>UIWindowSceneSessionRoleExternalDisplayNonInteractive</key> <array> <dict> <key>UISceneConfigurationName</key> <string>External Display</string> <key>UISceneDelegateClassName</key> <string>$(PRODUCT_MODULE_NAME).SceneDelegate</string> </dict> </array> </dict> </dict> </dict> I have my SceneDelegate setup as the follows: import SwiftUI class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { guard let windowScene = scene as? UIWindowScene else { return } if session.role == .windowExternalDisplayNonInteractive { let window = UIWindow(windowScene: windowScene) window.rootViewController = UIHostingController(rootView: ExternalDisplayView()) self.window = window window.makeKeyAndVisible() } } } I've tested running on a physical iPad with usb-c to hdmi connected and it only mirror's the screen. Same when connecting to AirPlay. Also tested running a simulator with external display enabled. Is there something I'm missing?
Topic: UI Frameworks SubTopic: SwiftUI
3
0
79
4w
iOS 18 Subviews and Environment values
Hello 👋 I played with the iOS 18 Group(subviews:) APIs these days and I guess I'm missing a point. Environment values seems to not being passed to subviews when set within the Group(subviews:) API. See the following code: Is it intended to be that way ? How to propagate different values to different subviews in this case ? I heard of ContainerValues API but it seems to be a way to provide value at root level to access it during subview iteration. What I'm looking for is "insert"/"propagate" values to each subview during subview iteration. PS: This works but I have lost subview context this way (I'm out of the group). Thanks in advance for anyone answering this!
3
0
73
4w
Preventing a custom menu to close when the main window SwiftUI View is updating - (macOS App)
I reposted this issue from my previous post since I have a partial solution to it, and now have a more precise question. The previous post title was too broad. My app is not Document-based, the UI is written with SwiftUI and I had to implement the 'Open Recent' menu myself. The class RecentDiscsModel has a refresh() function that forces the new opened file to be seen in the menu. Without that, the app needed to be restarted to see them. (The files were appearing in the Dock menu, however.) My problem is, that when we open this 'Open Recent' menu, it closes when the UI is updating. My app being a Disc Player, the main and unique window UI is updating every second or more through many @Published properties from my AudioDiscPlayer class. I have no idea how to prevent this issue. @main struct MyApp: App @main struct MyApp: App { @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate @StateObject private var recentDiscsModel = RecentDiscsModel.shared var body: some Scene { Window("Player", id: "main-window") { PlayerView() } .commands { CommandGroup(replacing: .newItem) { } CommandGroup(after: .newItem) { Button("Open...") { NotificationCenter.default.post(name: .openDocumentRequested, object: nil) } .keyboardShortcut("O", modifiers: .command) if recentDiscsModel.recentDocs.isEmpty == false { Menu("Open Recent") { ForEach(NSDocumentController.shared.recentDocumentURLs, id: \.self) { url in Button(url.lastPathComponent) { global.insertDisc(at: url) } } Divider() Button("Clear Menu") { NSDocumentController.shared.clearRecentDocuments(nil) recentDiscsModel.refresh() } } } } PlayBackMenu() } } } class RecentDiscsModel: ObservableObject | The class that handles the live refresh of the 'Open Recent' menu class RecentDiscsModel: ObservableObject { static let shared = RecentDiscsModel() @Published private(set) var recentFiles: [URL] = [] private init() { refresh() } func refresh() { let newDocs = NSDocumentController.shared.recentDocumentURLs if newDocs != recentDocs { recentDocs = newDocs } } } class Globals: ObservableObject | This is the class that handling opening cueSheet file: class Globals: ObservableObject { static let shared = MCGlobals() init() {} @Published var errorMessage = "" @Published var errorDetails = "" @Published var showingErrorAlert = false func handleFileImport(result: Result<URL, Error>) { switch result { case .success(let url): guard url.startAccessingSecurityScopedResource() else { errorMessage = "Unable to access file" errorDetails = "Permission denied" showingErrorAlert = true return } defer { url.stopAccessingSecurityScopedResource() } insertDisc(at: url) case .failure(let error): errorMessage = "Failed to import file" errorDetails = error.localizedDescription showingErrorAlert = true } } func insertDisc(at url: URL, startPlayback: Bool = false) { do { try AudioDiscPlayer.shared.insertDisc(at: url, startPlayback: startPlayback) NSDocumentController.shared.noteNewRecentDocumentURL(url) } catch { let nsError = error as NSError errorMessage = nsError.localizedDescription let reason = nsError.localizedFailureReason ?? "" let recovery = nsError.localizedRecoverySuggestion ?? "" errorDetails = "\n\n\(reason)\n\n\(recovery)".trimmingCharacters(in: .whitespacesAndNewlines) showingErrorAlert = true } RecentDiscsModel.shared.refresh() } }
1
0
73
4w
Seeking Feedback on iOS 18.5/18.6 Behavior
Hi all, We’re developing hybrid apps using the Ionic Framework and testing them on Xcode. Recently, we tested our app on iOS 18.5 and 18.6 and noticed a strange issue: when trying to change elements like the range bar, the control only responds if we tap slightly above it. It seems like there’s a misalignment. This problem didn’t occur on earlier iOS versions. Is anyone else experiencing similar issues?
Topic: UI Frameworks SubTopic: SwiftUI
1
0
45
4w
SwiftUI's List backed by CoreData using @FetchRequest fails to update on iOS 26 when compiled with Xcode 26
Hey there! I've been tracking a really weird behavior with a List backed by @FetchRequest from CoreData. When I toggle a bool on the CoreData model, the first time it updates correctly, but if I do it a second time, the UI doesn't re-render as expected. This does not happen if I compile the app using Xcode 16 (targeting both iOS 18 and iOS 26), nor it happens when using Xcode 26 and targeting iOS 18. It only happens when building the app using Xcode 26 and running it on iOS 26. Here are two demos: the first one works as expected, when I toggle the state twice, both times updates. The second one, only on iOS 26, the second toggle fails to re-render. Demo (running from Xcode 16): Demo (running from Xcode 26): The code: import SwiftUI import CoreData @main struct CoreDataTestApp: App { let persistenceController = PersistenceController.shared var body: some Scene { WindowGroup { ContentView() .environment(\.managedObjectContext, persistenceController.container.viewContext) } } } struct ContentView: View { @Environment(\.managedObjectContext) private var viewContext @FetchRequest(sortDescriptors: [NSSortDescriptor(keyPath: \Item.timestamp, ascending: true)]) private var items: FetchedResults<Item> var body: some View { NavigationView { List { ForEach(items) { item in HStack { Text(item.timestamp!.formatted()) Image(systemName: item.isFavorite ? "heart.fill" : "heart").foregroundStyle(.red) }.swipeActions(edge: .leading, allowsFullSwipe: true) { Button(item.isFavorite ? "Unfavorite" : "Favorite", systemImage: item.isFavorite ? "heart" : "heart.fill") { toggleFavoriteStatus(item: item) } } } } .toolbar { ToolbarItem { Button(action: addItem) { Label("Add Item", systemImage: "plus") } } } } } private func addItem() { withAnimation { let newItem = Item(context: viewContext) newItem.timestamp = Date() newItem.isFavorite = Bool.random() try! viewContext.save() } } private func toggleFavoriteStatus(item: Item) { withAnimation { item.isFavorite.toggle() try! viewContext.save() } } } struct PersistenceController { static let shared = PersistenceController() let container: NSPersistentContainer init() { container = NSPersistentContainer(name: "CoreDataTest") container.loadPersistentStores(completionHandler: { _, _ in }) container.viewContext.automaticallyMergesChangesFromParent = true } }
5
0
173
4w
Source view disappearing when interrupting a zoom navigation transition
When I use the .zoom transition in a navigation stack, I get a glitch when interrupting the animation by swiping back before it completes. When doing this, the source view disappears. I can still tap it to trigger the navigation again, but its not visible on screen. This seems to be a regression in iOS 26, as it works as expected when testing on iOS 18. Has someone else seen this issue and found a workaround? Is it possible to disable interrupting the transition? Filed a feedback on the issue FB19601591 Screen recording: https://share.icloud.com/photos/04cio3fEcbR6u64PAgxuS2CLQ Example code @State var showDetail = false @Namespace var namespace var body: some View { NavigationStack { ScrollView { showDetailButton } .navigationTitle("Title") .navigationBarTitleDisplayMode(.inline) .navigationDestination(isPresented: $showDetail) { Text("Detail") .navigationTransition(.zoom(sourceID: "zoom", in: namespace)) } } } var showDetailButton: some View { Button { showDetail = true } label: { Text("Show detail") .padding() .background(.green) .matchedTransitionSource(id: "zoom", in: namespace) } } }
Topic: UI Frameworks SubTopic: SwiftUI
5
3
90
4w
Display .icon files in SwiftUI
Is there a way to display a .icon file in SwiftUI? I want to show the app icon in the app itself but exporting and including the app icon as a PNG feels redundant. This would consume a lot of unnecessary storage especially when including a lot of alternative app icons. There has to be a better way Otherwise I would file a feedback for that Thank you
0
6
74
Aug ’25
Properly Implementing “Open Recent” Menu for a SwiftUI non Document-Based macOS Music Player
I've already searched extensively on Apple Developer Forums and Stack Overflow, and didn't really find what I need or I missed it. I'm developing a macOS music player app that uses cuesheet files paired with audio files. The core functionality is working, opening and playing files works without issues, but I'm struggling with implementing proper file handling features due to my limited experience with UI frameworks. The current state of my app is intentionally simple: Single window interface representing a music player with track list Opening cuesheet files changes the “disc” and updates the window Built with SwiftUI (not AppKit) Not created as a Document-Based app since the user doesn't need to edit, save, or work with multiple documents simultaneously What I Need to Implement: Open Recent menu that actually works Recent files accessible from Dock menu Opening cuesheet files from Finder Drag-and-drop cuesheet files onto app window (lower priority) Problems I've Encountered: I've tried multiple approaches but never achieved full functionality. The closest I got was an “Open Recent” menu that: Only updated after app relaunch Its drop-down kept closing while music was playing My Questions Is it possible purely in SwiftUI? Is there documentation I'm missing? I feel like I might be overcomplicating this. I'm open to alternative approaches if my current direction isn't ideal: Should I redesign as Document-Based? Since I apparently need NSDocumentController, would it be better to start with a Document-Based app template and disable unwanted features, and how? Should I mix AppKit with SwiftUI? While SwiftUI has been wonderful for my main window, it's becoming frustrating for other UI parts, especially menus. Would using AppKit for menus and keeping SwiftUI for the main interface be a reasonable approach? I thought this would be straightforward: Customize the .fileImporter with the proper logic of what to do with the files Call NSDocumentController.shared.noteNewRecentDocumentURL(url) so the Open Recent menu is created a populated with opened files. Maybe it really is that simple and I've gotten lost down a rabbit hole? UI programming is quite new to me, so I might be missing something obvious. Any guidance, code examples, or pointing me toward the right documentation would be greatly appreciated!
2
0
77
Aug ’25
SwiftUI iOS 16 TabView PageTabViewStyle index behavior is wrong for right to left layoutDirection
TabView page control element has a bug on iOS 16 if tabview is configured as RTL with PageTabViewStyle. Found iOS 16 Issues: Page indicators display dots in reverse order (appears to treat layout as LTR while showing RTL) Index selection is reversed - tapping indicators selects wrong pages Using the page control directly to navigate eventually breaks the index binding The underlying index counting logic conflicts with the visual presentation iOS 18 Behavior: Works as expected with correct dot order and index selection. Xcode version: Version 16.3 (16E140) Conclusion: Confirmed broken on iOS 16 Confirmed working on iOS 18 iOS 17 and earlier versions not yet tested I've opened a feedback assistant ticket quite a while ago but there is no answer. There's a code example and a video there. Anyone else had experience with this particular bug? Here's the code: public struct PagingView<Content: View>: View { //MARK: - Public Properties let pages: (Int) -> Content let numberOfPages: Int let pageMargin: CGFloat @Binding var currentPage: Int //MARK: - Object's Lifecycle public init(currentPage: Binding<Int>, pageMargin: CGFloat = 20, numberOfPages: Int, @ViewBuilder pages: @escaping (Int) -> Content) { self.pages = pages self.numberOfPages = numberOfPages self.pageMargin = pageMargin _currentPage = currentPage } //MARK: - View's Layout public var body: some View { TabView(selection: $currentPage) { ForEach(0..<numberOfPages, id: \.self) { index in pages(index) .padding(.horizontal, pageMargin) } } .tabViewStyle(PageTabViewStyle(indexDisplayMode: .always)) .ignoresSafeArea() } } //MARK: - Previews struct ContentView: View { @State var currentIndex: Int = 0 var body: some View { ZStack { Rectangle() .frame(height: 300) .foregroundStyle(Color.gray.opacity(0.2)) PagingView( currentPage: $currentIndex.onChange({ index in print("currentIndex: ", index) }), pageMargin: 20, numberOfPages: 10) { index in ZStack { Rectangle() .frame(width: 200, height: 200) .foregroundStyle(Color.gray.opacity(0.2)) Text("\(index)") .foregroundStyle(.brown) .background(Color.yellow) } }.frame(height: 200) } } } #Preview("ContentView") { ContentView() } extension Binding { @MainActor func onChange(_ handler: @escaping (Value) -> Void) -> Binding<Value> { Binding( get: { self.wrappedValue }, set: { newValue in self.wrappedValue = newValue handler(newValue) } ) } }
0
0
79
Aug ’25
SwiftUI preview for drag/drop APIs
The new drag & drop APIs for macOS 26 are terrific, but there's an important missing piece. If I use draggable(containerItemID:), there's no way to provide a custom view for the drag image. Meanwhile the older API lets you provide a preview, but you miss out on things like multi-item drag and custom drag sessions. Is there some mechanism for supplying a preview that I'm not seeing? Without it, the drag interface in my apps is going to look terrible.
0
0
72
Aug ’25
ScrollView ScrollPosition scrollTo Anchor ignored/broken
ScrollPosition does not obey anchor when calling scrollTo. The anchor is ignored and it does not matter what value you provide. If the view is below the visible content, it will pin the new scrolled to view at the bottom. If the view is already in the visible content, it will not scroll to the anchor. If the view is above the visible content, it will pin to top. All three cases ignore the anchor. This just another example of how painful SwiftUI is to work with. It just doesn't work as expected or intended, despite years of prioritizing ScrollView APIs. Link to Gist // This should scroll to item 50 with the requested anchor, but SCROLLPOSITION IGNORES THE ANCHOR. if shouldBeTopAnchor { // Expected: Scroll item 50 to top, but actually scrolls to bottom scrollPosition.scrollTo(id: 50, anchor: .top) } else { // Expected: Scroll item 50 to center, but actually scrolls to top scrollPosition.scrollTo(id: 50, anchor: .center) }
Topic: UI Frameworks SubTopic: SwiftUI
0
0
81
Aug ’25
Background Modes, ScreenTime API
I’m attempting to run Apple’s ScreenTime API while my app is in the background When debugging on a device my background restrictions functionality works, but on TestFlight it does not. This is the error message when analyzing the device in XCodes Console BackgroundTaskSuspended. Background entitlement: NO When the proper entitlements are added this error doesn't allow for the build to be uploaded to TestFlight. Provisioning profile "iOS Team Provisioning Profile: com.xxx.xxx" doesn't include the UIBackgroundModes entitlement. I have the proper entries in the “Signing and Capabilities” of the project, but my functionality will not run in the background when the app is not launched. I just need a function to run that calls Apple's ScreenTime API while my app is in the background. Other apps have achieved the functionality I’m looking for so I know it’s possible, but this is the roadblock I’m running into.
2
0
127
Aug ’25
iOS 26 navigationTransition .zoom issue
When I dismiss a view presented with .navigationTransition(.zoom), the source view gets a weird background (black or white depending on the appearance) for a couple of seconds, and then it disappears. Here’s a simple code example. import SwiftUI struct NavigationTransition: View { @Namespace private var namespace @State private var isSecondViewPresented = false var body: some View { NavigationStack { ZStack { DetailView(namespace: namespace) .onTapGesture { isSecondViewPresented = true } } .fullScreenCover(isPresented: $isSecondViewPresented) { SecondView() .navigationTransition(.zoom(sourceID: "world", in: namespace)) } } } } struct DetailView: View { var namespace: Namespace.ID var body: some View { ZStack { Color.blue Text("Hello World!") .foregroundStyle(.white) .matchedTransitionSource(id: "world", in: namespace) } .ignoresSafeArea() } } struct SecondView: View { var body: some View { ZStack { Color.green Image(systemName: "globe") .foregroundStyle(Color.red) } .ignoresSafeArea() } } #Preview { NavigationTransition() }
2
4
114
Aug ’25
Question about the Scope and "Inheritance" Behavior of SwiftUI Modifiers
I am confused about the "inheritance" behavior of modifiers in SwiftUI. Some modifiers (such as .background, .clipShape, etc.) seem to affect both parent and child views inconsistently. Here are some specific examples I encountered in Xcode 16.4 with the iOS 18.5 iPhone 16 Pro simulator: struct ContentView: View { var body: some View { VStack { // RedVStack Text("Hello world!") VStack { // OrangeVStack Text("Hello") Text("Hello") } .background(.orange) } .background(.red, in: RoundedRectangle(cornerRadius: 5)) // RedVStack has rounded corners, OrangeVStack also has rounded corners } } struct ContentView: View { var body: some View { VStack { // RedVStack Text("Hello world!") VStack { // OrangeVStack Text("Hello") Text("Hello") } .background(.orange) Text("Hello world!") } .background(.red, in: RoundedRectangle(cornerRadius: 5)) // RedVStack has rounded corners, OrangeVStack does not have rounded corners } } struct ContentView: View { var body: some View { VStack { // RedVStack Text("Hello world!") VStack { // OrangeVStack Text("Hello") Text("Hello") } .background(.orange) } .background(.red) .clipShape(RoundedRectangle(cornerRadius: 5)) // RedVStack has rounded corners, OrangeVStack does not have rounded corners } } I find it difficult to understand which modifiers affect child views and which do not. Is there any official documentation or authoritative explanation that can help me understand the scope and "inheritance" mechanism of SwiftUI modifiers? Thank you!
0
0
57
Aug ’25
Popover in Toolbar Causes Crash in Catalyst App on macOS 26
Hi everyone, I’ve encountered an issue where using a popover inside the toolbar of a Catalyst app causes a crash on macOS 26 beta 5 with Xcode 26 beta 5. Here’s a simplified code snippet: import SwiftUI struct ContentView: View { @State private var isPresentingPopover = false var body: some View { NavigationStack { VStack { } .padding() .toolbar { ToolbarItem { Button(action: { isPresentingPopover.toggle() }) { Image(systemName: "bubble") } .popover(isPresented: $isPresentingPopover) { Text("Hello") .font(.largeTitle) .padding() } } } } } } Steps to reproduce: Create a new iOS app using Xcode 26 beta 5. Enable Mac Catalyst (Match iPad). Add the above code to show a Popover from a toolbar button. Run the app on macOS 26, then click the toolbar button. The app crashes immediately upon clicking the toolbar button. Has anyone else run into this? Any workarounds or suggestions would be appreciated! Thanks!
1
0
62
Aug ’25
watchOS 26 system clock collision
Our app has an infrequently used but useful calculator screen similar to Apple's calculator app where the current value ticker squats in the top toolbar trailing area, extending into the system clock area. In 26 beta, that view is now broken: the system clock not longer hides when a layout collision is detected. Can the prior avoidance behavior (or an API to hide the clock) be triggered somehow?
2
0
51
Aug ’25
Dividers not appearing in menu bar on iPadOS 26
On macOS 26 I can see the dividers when I open my Help menu: However, on iPadOS 26 the dividers don't appear: I am simply using Divider() to separate my menu bar items in my CommandGroup. iPadOS does support dividers as I can see them for the system generated Edit menu but for some reason it's not working here. Does anyone know if I am doing something wrong with the iPadOS implementation?
2
1
74
Aug ’25
`.task(id:_:)` starting in the `Task.isCancelled` state
I'm running into a weird SwiftUI concurrency issue that only seems to happen on a real device (iOS 18.5 in my case), and not in simulator. I have a NavigationSplitView where the Detail view uses .task(id:_:) to perform some async work upon appearance (in my real-world case, a map snapshot). When running this on a real device, the first execution of the task works as expected. However, any task subsequent executions, even ones for the same id, always start in the Task.isCancelled state. Is there something about .task(id:_:) that I'm misunderstanding, or have I stumbled upon a serious bug here? import SwiftUI struct ContentView: View { var body: some View { TaskBugSplitView() } } struct TestItem: Identifiable, Hashable { var id: Int var name: String } struct TaskBugSplitView: View { @State private var selectedItemIndex: [TestItem].Index? @State private var testItems: [TestItem] = [ TestItem(id: 1, name: "First Item"), TestItem(id: 2, name: "Second Item"), TestItem(id: 3, name: "Third Item"), TestItem(id: 4, name: "Fourth Item"), TestItem(id: 5, name: "Fifth Item") ] var body: some View { NavigationSplitView { List(testItems.indices, id: \.self, selection: $selectedItemIndex) { item in Text(testItems[item].name) } } detail: { if let selectedItemIndex { TaskBugDetailView(item: testItems[selectedItemIndex]) } else { Text("Select an item") .foregroundStyle(.secondary) } } } } struct TaskBugDetailView: View { @State var item: TestItem @State private var taskResult = "Not started" var body: some View { VStack(spacing: 20) { Text("Item: \(item.name)") .font(.title2) Text("Task Result:") .font(.headline) Text(taskResult) .foregroundStyle(taskResult.contains("CANCELLED") ? .red : .primary) Spacer() } .padding() .navigationTitle(item.name) .task(id: item.id) { // BUG: On real devices, Task.isCancelled is true immediately for all tasks // after the first successful one, even though the ID has changed if Task.isCancelled { taskResult = "CANCELLED at start" print("Task cancelled at start for item \(item.id)") return } taskResult = "SUCCESS" print("Task completed successfully for item \(item.id)") } } }
2
0
63
Aug ’25