Explore the various UI frameworks available for building app interfaces. Discuss the use cases for different frameworks, share best practices, and get help with specific framework-related questions.

All subtopics
Posts under UI Frameworks topic

Post

Replies

Boosts

Views

Created

DiffableDataSource hangs on apply
About a year ago, I developed and released an app on the App Store (I believe it was running on the Sequoia SDK at the time), and everything was working fine. I’m now revisiting the project using the newer Tahoe SDK, and I’m running into an issue with DiffableDataSource. Specifically, the app hangs and CPU usage spikes to 100% when applying snapshots. Has anyone experienced similar issues after upgrading to newer SDKs? Are there any recent changes or pitfalls with DiffableDataSource (e.g., threading, Hashable requirements, or snapshot handling) that I should be aware of? Any insights or suggestions would be greatly appreciated. extension Section { enum Identifier: Int, CaseIterable { case main } enum Item: Hashable { case file(FileViewData) } } struct FileViewData: Equatable, Hashable, Identifiable { let id: String let name: String var accessoryViewData: KTFDownloadAccessoryViewData init( id: String, name: String, accessoryViewData: KTFDownloadAccessoryViewData = .nothing ) { self.id = id self.name = name self.accessoryViewData = accessoryViewData } } public enum KTFDownloadAccessoryViewData: Equatable, Hashable { case nothing case selected(SelectedState) case completed public enum SelectedState: Equatable, Hashable { case nothing case waiting case downloading(Double) } } When I changed FileViewData as below, no hangs but item appearance doesn't change of course. struct FileViewData: Equatable, Hashable, Identifiable { let id: String let name: String var accessoryViewData: KTFDownloadAccessoryViewData init( id: String, name: String, accessoryViewData: KTFDownloadAccessoryViewData = .nothing ) { self.id = id self.name = name self.accessoryViewData = accessoryViewData } func hash(into hasher: inout Hasher) { hasher.combine(id) } static func == (lhs: FileViewData, rhs: FileViewData) -> Bool { return lhs.id == rhs.id } }
Topic: UI Frameworks SubTopic: UIKit Tags:
1
0
136
Apr ’26
NSDocument "saveToURL:ofType:..." is using outdated file type
These days I've observed a strange behavior in my document-based app on macOS: Its NSDocument class implementation is overwriting "saveToURL:ofType:forSaveOperation:completionHandler:", performing some additional checks and calling super by passing the original parameters. As my app is supporting various file formats for writing (and exporting those UTIs) the user can open a file in one format and save it to another. NSDocument is calling the mentioned methods implicitly after completing the "Save as..." dialog. If this happens, the passed-on fileType is still the one of format #1, although the file is saved with the file name extension of format #2. This hick-up is not directly obvious to the user. But if the file is re-saved (e.g. after modifications), Cocoa is trying to extend the sandbox for the URL of type #1, and fails with the following error message at the Xcode console: -[STBDocument saveToURL:ofType:forSaveOperation:completionHandler:] [Line 521] typeName: com.janome.jef -[STBDocument saveToURL:ofType:forSaveOperation:completionHandler:] [Line 523] targetTypeUTI: com.tajima.dst NSFileSandboxingRequestRelatedItemExtension: Failed to issue extension for /Users/matthias/Desktop/Ohne Titel.jef because: Error Domain=NSPOSIXErrorDomain Code=3 "No such process" -[NSFileCoordinator itemAtURL:willMoveToURL:] could not get a sandbox extension. oldURL: file:///Users/matthias/Desktop/Ohne%20Titel.dst, newURL: file:///Users/matthias/Desktop/Ohne%20Titel.jef I'm currently fixing this issue by determining the UTType for the new file name extension and passing it to super. Unfortunately I have no idea how long this issue was already present, and cannot replicate it with a sample app based on Apple's Xcode 26 template (too many differences to my >15 years old app) - so I won't file a bug report. Take this post just for information in case someone else is facing a similar situation...
0
0
96
Apr ’26
Initial presentation of popover hangs when shown from a button in the toolbar
I have a simple reproducer here: struct ContentView: View { @State private var isOn = false @State private var isPresented = false var body: some View { NavigationStack { Color.blue .toolbar { ToolbarItem(placement: .topBarTrailing) { Button("Press here") { isPresented = true } .popover(isPresented: $isPresented) { Color.green .frame(idealWidth: 400, idealHeight: 500) .presentationCompactAdaptation(.popover) } } } } } } When I tap on the button in the toolbar you can see there is a hang then the popover shows. Then every time after there is no longer a hang so this seems like a bug. Any ideas? I'm using Xcode 26.3 and a iPad Pro 13-inch (M5) (26.4) simulator.
3
3
192
Apr ’26
X button disappeared on iPadOS 26.4 in MFMailComposeViewController
I’m using MFMailComposeViewController to send emails from my app. Since updating to iPadOS 26.4, there is no way to cancel the mail composer because the “X” button in the top-left corner has disappeared. On iPhone with iOS 26.4, everything still seems to work as expected. Is this a known issue, or am I missing something? Has anyone else experienced this, or found a workaround?
Topic: UI Frameworks SubTopic: UIKit
4
0
352
Apr ’26
NSTextAttachment.character symbol suddenly not available anymore resulting in compiler error
I published the latest update of my AppKit app in September with macOS 26.0. I just wanted to create a new update, but compiling on macOS 26.4 now fails because of the symbol NSTextAttachment.character which is referenced in my code. The error is Type 'NSTextAttachment' has no member 'character' I've never experienced before that a symbol suddenly is not available anymore without even a deprecation notice from one OS release to the next, let alone a minor release. Is this a bug in macOS or Xcode, or should I start worrying about symbols becoming unavailable anytime?
3
1
285
Apr ’26
Potentially Unfair Limitation for Third-Party Keyboard Developers
When developing a custom keyboard on iOS, even after enabling Full Access (RequestsOpenAccess = true), it is still not possible to record audio — the recording simply does not start. This is despite the fact that: the user is explicitly warned the user provides informed consent by enabling Full Access According to Apple’s documentation: https://developer.apple.com/documentation/uikit/configuring-open-access-for-a-custom-keyboard “However, with RequestsOpenAccess set to true, the keyboard has all the capabilities in the preceding list.” At the same time, the preceding list includes: “No access to microphone and speaker” This creates ambiguity. The wording suggests that enabling Full Access should lift prior restrictions, yet in practice, microphone access remains unavailable to third-party keyboards. Why this is concerning With Full Access enabled, a keyboard already has: network access the ability to transmit user input From a privacy standpoint, this is already highly sensitive. Preventing microphone access while allowing these capabilities appears inconsistent. Meanwhile, Apple’s own system keyboard supports voice dictation, which creates a functional gap between first-party and third-party keyboards. Competition perspective This raises a broader question about equal access to platform capabilities. Restricting third-party keyboards from using the microphone — while first-party solutions can — may be seen as: unequal treatment of developers a limitation of competition in input methods Such differences are increasingly scrutinized under EU regulations like the Digital Markets Act and Article 102 TFEU, which emphasize fair access to platform features and prohibit self-preferencing by dominant platforms. Request for clarification Is microphone access intentionally restricted for all third-party keyboards, even with Full Access enabled? If so, what is the technical or policy justification? Are there plans to provide a secure and user-consented way to enable audio input for custom keyboards? Clarification on this would help developers better understand platform limitations and design decisions.
0
0
196
Apr ’26
UITextView cursor sometimes jumps up when pressing arrow down key and setting typingAttributes
My app uses TextKit 1 and unfortunately still cannot migrate to TextKit 2 because of some bugs (for instance in FB17103305 I show how NSTextView.shouldDrawInsertionPoint has no effect, but I opened that feedback exactly one year ago and it still has no answer). Unfortunately TextKit 1 has another bug which causes the text cursor to jump unpredictably up or down when pressing the arrow keys and setting UITextView.typingAttributes. Run the code below on iPhone 17 Pro Max Simulator. Scroll the text down until you see “Header 2”. Place the text cursor after “# “. Press the arrow down key twice to move the cursor two lines down. The cursor moves to the top of the view instead. Continuing to press the arrow keys up and down results in the cursor sometimes moving as expected, other times jumping around wildly. Does anyone know a workaround? I created FB22382453. class TextView: UITextView, UITextViewDelegate { override func awakeFromNib() { let _ = layoutManager delegate = self let header = textAttributes(fontSize: 30) let body = textAttributes(fontSize: 15) let string = NSMutableAttributedString(string: String(repeating: "a", count: 2681) + "\n", attributes: body) string.append(NSAttributedString(string: """ # Header 1 """, attributes: header)) string.append(NSMutableAttributedString(string: String(repeating: "a", count: 5198) + "\n", attributes: body)) string.append(NSAttributedString(string: """ # Header 2 """, attributes: header)) string.append(NSMutableAttributedString(string: String(repeating: "a", count: 7048) + "\n", attributes: body)) textStorage.setAttributedString(string) } func textViewDidChangeSelection(_ textView: UITextView) { typingAttributes = textStorage.attributes(at: selectedRange.location - 1, effectiveRange: nil) } private func textAttributes(fontSize: Double) -> [NSAttributedString.Key: Any] { var textAttributes = [NSAttributedString.Key: Any]() textAttributes[.font] = UIFont(name: "Courier", size: fontSize) let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.minimumLineHeight = round(fontSize * 1.3) paragraphStyle.maximumLineHeight = paragraphStyle.minimumLineHeight textAttributes[.paragraphStyle] = paragraphStyle return textAttributes } }
Topic: UI Frameworks SubTopic: UIKit Tags:
0
0
134
Mar ’26
PDFView left-anchors to window edge instead of centering between sidebar and inspector (macOS Tahoe)
I'm building a document viewer on macOS Tahoe with a 3-column NSSplitViewController (sidebar | detail | inspector), trying to replicate how Preview displays PDFs with the page centered in the visible gap between the panels, with content bleeding under them when panning or zooming. I'm using the approach from Build an AppKit app with the new design (WWDC25): detailItem.automaticallyAdjustsSafeAreaInsets = true safeAreaInsets reports the correct values (e.g. left: 208, right: 240), and the frame does extend under both panels. But PDFView with autoScales = true anchors the page to the left edge of the window instead of centering it in the visible gap between the sidebar and inspector. I can get the page to center correctly by constraining PDFView to view.safeAreaLayoutGuide, but then content no longer extends under the panels when panning or zooming, which defeats the whole purpose. What's the correct way to center PDFView content within the visible gap while keeping the frame full-width so content bleeds under the panels? I've attached pictures of how Preview does it.
Topic: UI Frameworks SubTopic: AppKit
0
0
104
Mar ’26
UIScrollView Fast Scrolling have it scroll to the beginning when i press dpad to left while vertical scrolling
I'm triaging the issue with the fast scrolling on UIScrollView and I'm really upset how little to no info this component is on the internet. Like i disabled scrolling and yet after holding the dpad down to scroll downward eventually the fast scroll mechanism is used. The issue I have is that I have a setup where the scrollview scrolling is disabled and whenever cells are focused it'll scroll to the cell's position for that cells to focused on top left side of the scrollview. I start off with the cell placed at position x of 1000. I scroll down enough to enable fast scrolling. While fast scrolling vertically, i press left a couple of time and it somewhat scroll horizontally. Actual scrollView index is suddenly placed to x:0 instead of let say x:950-1000 Expected should be scrolled to the left of cell at position x 1000. Attached the stack trace and the log showcasing it and run it on simulator tvos 26.2. Initial properties of the scrollview we set up scroll = [UIScrollView new]; scroll.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever; scroll.automaticallyAdjustsScrollIndicatorInsets = false; [scroll setDirectionalLockEnabled:TRUE]; [scroll setContentInset:UIEdgeInsetsMake(0, 0, 0, 0)]; scroll.bounces = FALSE; scroll.delaysContentTouches = FALSE; scroll.showsVerticalScrollIndicator = FALSE; scroll.showsHorizontalScrollIndicator = FALSE; The plan for me at least is to somehow disable the horizontal scrolling interaction of it when fast scrolling is enabled, without accessing the private components of it.
0
0
113
Mar ’26
Scene resizing on iPad breaks UIPageViewController's setViewControllers
The following is verbatim of a feedback report (FB22367951) I submitted, shared here as someone else might be interested to see it. I have reproduced this bug on iPadOS 26.3.1 (a) and 26.4. During scene resizing on iPad, UIPageViewController's setViewControllers method fails to do its work. The navigation starts and for a brief moment you can see the new view controller coming from the expected direction, but shortly after it fails and stays on the same [current] view controller. It doesn't even call the completion handler when it fails. When the navigation succeeds (due to not resizing a scene during the navigation) after previously failing at least once, the completion handler is sometimes called more than once. I have created a demo project, which I have pushed to this repo: https://github.com/galijot/SceneResize-Breaks-UIPageViewController I have also attached a zip of the project to this report.
0
0
201
Mar ’26
MapKit in SwiftUI
Anyone worked with MapKit's MapCameraPosition in SwiftUI? I'm building a navigation app and ran into a limitation I can't find a clean solution for when using .userLocation(followsHeading: true) MapKit takes full control of the camera, smooth heading tracking, follows the user automatically. Perfect. But there's no way to set a custom pitch (tilt) on it. The only initializer available is... .userLocation(followsHeading: true, fallback: .automatic) No pitch, no distance parameters.... The workaround I found is setting .camera(MapCamera(..., pitch: 60)) first, waiting 200ms, then switching to .userLocation(followsHeading: true), MapKit inherits the pitch from the rendered camera state before handing off to user tracking.... It works, but it's clearly exploiting an undocumented behaviour in MapKit's state machine rather than a proper API Has anyone found a cleaner way to achieve this? Or is UIViewRepresentable wrapping MKMapView the only proper solution? It would be awesome to have something like this cameraPosition = .userLocation( followsHeading: true, pitch: 60, distance: 800, fallback: .automatic )
1
0
145
Mar ’26
NavigationLink selection in DisclosureGroup not working with .draggable modifier
NavigationLink selection in DisclosureGroup not working with .draggable modifier This was recently also posted here: https://stackoverflow.com/questions/79914290/ I am playing around with a tree data structure with folders and entries.I would like to add dragging of entries and folders between folders, using .draggable and dropDestination. In my current code, dragging works, but selection of entries no longer works, except if I click outside of the Text If I comment out .draggable(subfolder.name) in func FolderRow(), selection works as expected. How can I make sure both selection and drag and drop works for both folders and entries? I also tried using Transferable and Codable, but I get the same result. Here is an MRE: import SwiftData import SwiftUI @Model final class Folder { @Attribute(.unique) var name: String // Parent var parentFolder: Folder? // Child folders @Relationship(deleteRule: .cascade, inverse: \Folder.parentFolder) var subfolders: [Folder] = [] // Leaf entries @Relationship(deleteRule: .cascade, inverse: \Entry.folder) var entries: [Entry] = [] init(name: String, parentFolder: Folder? = nil) { self.name = name self.parentFolder = parentFolder } } @Model final class Entry { @Attribute(.unique) var name: String var detail: String var folder: Folder? // recursive relationship init(name: String, detail: String) { self.name = name self.detail = detail } } @main struct TestMacApp: App { var body: some Scene { WindowGroup { SidebarView() .modelContainer(for: Folder.self) } } } struct SidebarView: View { @Environment(\.modelContext) private var context @Query(filter: #Predicate<Folder> { $0.parentFolder == nil }) private var rootFolders: [Folder] var body: some View { NavigationSplitView { List { ForEach(rootFolders) { folder in FolderRow(folder: folder) .draggable(folder.name) } } } detail: { Text("detail") } .onAppear { seed() } } } struct FolderRow: View { @Environment(\.modelContext) private var context var folder: Folder @State private var isExpanded: Bool = true var body: some View { DisclosureGroup(isExpanded: $isExpanded) { // Subfolders ForEach(folder.subfolders) { subfolder in FolderRow(folder: subfolder) .draggable(subfolder.name) // disabling this line fixes the selection } // Entries (leaf nodes) ForEach(folder.entries) { entry in NavigationLink(destination: EntryDetail(entry: entry)) { EntryRow(entry: entry) } .draggable(entry.name) } } label: { Label(folder.name, systemImage: "folder") } .dropDestination(for: String.self) { names, _ in return handleDrop(of: names) } } } struct EntryRow: View { var entry: Entry var body: some View { Text(entry.name) } } struct EntryDetail: View { var entry: Entry var body: some View { Text(entry.detail) } } extension FolderRow { private func handleDrop(of names: [String]) -> Bool { do { for name in names { if let droppedEntry = try context.fetchFilteredModel(filter: #Predicate<Entry> { x in x.name == name }) { droppedEntry.folder = folder print("dropped \(droppedEntry.name) on \(folder.name)") } else if let droppedFolder = try context.fetchFilteredModel(filter: #Predicate<Folder> { x in x.name == name }) { if droppedFolder.parentFolder != nil && droppedFolder != folder { droppedFolder.parentFolder = folder print("dropped \(droppedFolder.name) on \(folder.name)") } } } return true } catch { debugPrint(error.localizedDescription) return false } } } extension SidebarView { private func seed() { do { // delete current models for folder: Folder in try context.fetchAllModels() { context.delete(folder) } try context.save() let rootFolder = Folder(name: "Root") let entry1 = Entry(name: "One", detail: "Detail One") let entry2 = Entry(name: "Two", detail: "Detail Two") rootFolder.entries.append(contentsOf: [entry1, entry2]) let subFolder1 = Folder(name: "Sub1", parentFolder: rootFolder) let entry3 = Entry(name: "Three", detail: "Detail Three") let entry4 = Entry(name: "Four", detail: "Detail Four") subFolder1.entries.append(contentsOf: [entry3, entry4]) let subFolder2 = Folder(name: "Sub2", parentFolder: rootFolder) let entry5 = Entry(name: "Five", detail: "Detail Five") let entry6 = Entry(name: "Six", detail: "Detail Six") subFolder2.entries.append(contentsOf: [entry5, entry6]) context.insert(rootFolder) } catch { debugPrint(error) } } } extension ModelContext { // convenience methods func fetchAllModels<M>() throws -> [M] where M: PersistentModel { let fetchDescriptor = FetchDescriptor<M>() return try fetch(fetchDescriptor) } func fetchFilteredModels<M>(filter: Predicate<M>) throws -> [M] where M: PersistentModel { let fetchDescriptor = FetchDescriptor<M>(predicate: filter) return try fetch(fetchDescriptor) } func fetchFilteredModel<M>(filter: Predicate<M>) throws -> M? where M: PersistentModel { return try fetchFilteredModels(filter: filter).first } }
Topic: UI Frameworks SubTopic: SwiftUI
1
0
195
Mar ’26
Left navigation bar items become invisible after rotating device and presenting detail view in split view
A user of my app, whose main view is a split view, reported an issue which causes the left navigation bar items to disappear without apparent reason if they rotate the device and later show the detail view, preventing them from using the back button to show the root view again. Am I doing something wrong or is it a bug? I can reproduce the issue with the following steps: Create a new document-based iOS app (as it uses scenes by default, as opposed to a regular app). Paste the code below. In the target build settings, delete "Launch Screen Interface File Base Name" and set "Launch Screen (Generation)" to YES. Without this step, for some reason, the issue doesn't happen. Launch the app in iPhone Simulator. Tap the top left button to show the root view, then the “detail” button to show the detail view. The left navigation bar button is still visible. Rotate the Simulator window right, then left again. Tap the top left button to show the root view, then the “detail” button to show the detail view. Now the left navigation bar button is invisible. Rotating the device right and left again solves the issue. I filed FB22363118. class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { window = UIWindow(windowScene: scene as! UIWindowScene) let split = SplitViewController() window!.rootViewController = split window!.makeKeyAndVisible() split.showDetail() } } class SplitViewController: UISplitViewController, UISplitViewControllerDelegate { var detailNavigationViewController: UINavigationController! init() { super.init(nibName: nil, bundle: nil) detailNavigationViewController = UINavigationController(rootViewController: DetailViewController()) viewControllers = [UINavigationController(rootViewController: RootViewController())] } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } func showDetail() { showDetailViewController(detailNavigationViewController, sender: nil) } func showRoot() { (viewControllers.first as? UINavigationController)?.popViewController(animated: true) } } class RootViewController: UIViewController { override func loadView() { navigationItem.title = "root" let button = UIButton(primaryAction: UIAction(handler: { [self] _ in (splitViewController as! SplitViewController).showDetail() })) button.setTitle("detail", for: .normal) view = button } } class DetailViewController: UIViewController { override func loadView() { navigationItem.title = "detail" view = UIView() registerForTraitChanges([UITraitHorizontalSizeClass.self]) { (self: Self, previousTraitCollection) in if previousTraitCollection.horizontalSizeClass != self.traitCollection.horizontalSizeClass { self.updateBarButtons() } } updateBarButtons() } private func updateBarButtons() { navigationItem.leftBarButtonItem = UIBarButtonItem(primaryAction: UIAction(image: UIImage(systemName: "sidebar.leading")) { [self] _ in (self.splitViewController as! SplitViewController).showRoot() }) navigationItem.rightBarButtonItem = UIBarButtonItem(title: "right") } }
Topic: UI Frameworks SubTopic: UIKit Tags:
0
0
87
Mar ’26
How to recreate Apple Music mini player transition in SwiftUI
Hello, I am building an audio player app in SwiftUI and trying to recreate the behavior of Apple Music's mini player and full player. I'm struggling to get the animation to seamlessly transition between the mini player and the full player. Currently, it feels disconnected and doesn't resemble the smooth animation seen in Apple Music. What I want to achieve: Full player that expands/collapses from/to the mini player Smooth artwork transition between both states Drag down to collapse the full player Support both newer APIs like tabViewBottomAccessory and older iOS versions Questions: What is the best way to build this transition in SwiftUI? Should I use matchedGeometryEffect or something else? Should this be a custom container instead of fullScreenCover? How would you support both new and older iOS versions? What is the best way to implement drag to dismiss? Thanks for any help! Example code: struct ContentView: View { @State private var isFullPlayerPresented = false var body: some View { TabView { Tab("Home", systemImage: "house") { Text("Home") .frame(maxWidth: .infinity, maxHeight: .infinity) .background(.green) } Tab("Library", systemImage: "rectangle.stack.fill") { Text("Library") .frame(maxWidth: .infinity, maxHeight: .infinity) .background(.brown) } } .tabViewBottomAccessory(isEnabled: !isFullPlayerPresented) { MiniPlayerView(isFullPlayerPresented: $isFullPlayerPresented) } .fullScreenCover(isPresented: $isFullPlayerPresented) { // Maybe it's not a full screen cover presentation in Apple Music? FullPlayerView(isFullPlayerPresented: $isFullPlayerPresented) } } } Mini player: struct MiniPlayerView: View { @Binding var isFullPlayerPresented: Bool var body: some View { Button { isFullPlayerPresented = true } label: { HStack { Image(systemName: "photo") .resizable() .scaledToFit() .frame(width: 30, height: 30) .clipShape(.rect(cornerRadius: 8)) Spacer() Text("Tap to open full player") Spacer() Button("", systemImage: "play.fill", action: {}) } .padding(.horizontal) .padding(.vertical, 4) } .foregroundStyle(.white) } } Full player: struct FullPlayerView: View { @Binding var isFullPlayerPresented: Bool var body: some View { // This art work needs to snaps to the artwork in mini player Image(systemName: "photo") .resizable() .scaledToFit() .frame(width: 250, height: 250) .clipShape(.rect(cornerRadius: 20)) .frame(maxWidth: .infinity, maxHeight: .infinity) .background(.red) .overlay(alignment: .topTrailing) { Button(role: .close) { isFullPlayerPresented = false } .foregroundStyle(.white) .padding() } } }
2
0
376
Mar ’26
NSBrowser -deselectAll: broken on macOS Tahoe 26.4
So if I have a selection in NSBrowser. I hit Option+Command+A to invoke "Deselect" the selection in the parent for the last column drops its selections, as expected. But the column doesn't drop off the browser. The delegate method (void)browser:(NSBrowser *)browser didChangeLastColumn:(NSInteger)oldLastColumn toColumn:(NSInteger)column NS_SWIFT_UI_ACTOR; Never fires (since the column isn't dropped off). But we have. dangling last column with no selection in the previous column. Now if there's enough room for me to deselect by clicking the background, the last column drops off, as expected. This seems to be fairly new? Anyone else experiencing this? These 26point updates seem to keep punching me in the face. And yes. deselect all seems somewhat broken in Column view in the Finder as well, but in a seemingly different way. In Finder it just seems to change the selection color like it deactivated the window but doesn't drop the selection. For me, the selection IS dropped but the column remains visible. Maybe they are using Cocoa bindings or something that resyncs the selection after the mess up. I dk.
0
0
156
Mar ’26
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!
0
0
78
Mar ’26
Localization in Swift macOS console Apps.
Is it possible to build localization into console apps, developed in SwiftUI in Xcode26. I have created a catalog, (.xcstrings file) with an English and fr-CA string. I have tried to display the French text without success. I am using the console app to test a package which also has English/French text. English text works fine in both package and the console main, but I cannot generate the French. From what I can discover so far it's not possible without bundling it as a .app, (console app). Looking for anyone who has crossed this bridge.
7
0
469
Mar ’26
DiffableDataSource hangs on apply
About a year ago, I developed and released an app on the App Store (I believe it was running on the Sequoia SDK at the time), and everything was working fine. I’m now revisiting the project using the newer Tahoe SDK, and I’m running into an issue with DiffableDataSource. Specifically, the app hangs and CPU usage spikes to 100% when applying snapshots. Has anyone experienced similar issues after upgrading to newer SDKs? Are there any recent changes or pitfalls with DiffableDataSource (e.g., threading, Hashable requirements, or snapshot handling) that I should be aware of? Any insights or suggestions would be greatly appreciated. extension Section { enum Identifier: Int, CaseIterable { case main } enum Item: Hashable { case file(FileViewData) } } struct FileViewData: Equatable, Hashable, Identifiable { let id: String let name: String var accessoryViewData: KTFDownloadAccessoryViewData init( id: String, name: String, accessoryViewData: KTFDownloadAccessoryViewData = .nothing ) { self.id = id self.name = name self.accessoryViewData = accessoryViewData } } public enum KTFDownloadAccessoryViewData: Equatable, Hashable { case nothing case selected(SelectedState) case completed public enum SelectedState: Equatable, Hashable { case nothing case waiting case downloading(Double) } } When I changed FileViewData as below, no hangs but item appearance doesn't change of course. struct FileViewData: Equatable, Hashable, Identifiable { let id: String let name: String var accessoryViewData: KTFDownloadAccessoryViewData init( id: String, name: String, accessoryViewData: KTFDownloadAccessoryViewData = .nothing ) { self.id = id self.name = name self.accessoryViewData = accessoryViewData } func hash(into hasher: inout Hasher) { hasher.combine(id) } static func == (lhs: FileViewData, rhs: FileViewData) -> Bool { return lhs.id == rhs.id } }
Topic: UI Frameworks SubTopic: UIKit Tags:
Replies
1
Boosts
0
Views
136
Activity
Apr ’26
NSDocument "saveToURL:ofType:..." is using outdated file type
These days I've observed a strange behavior in my document-based app on macOS: Its NSDocument class implementation is overwriting "saveToURL:ofType:forSaveOperation:completionHandler:", performing some additional checks and calling super by passing the original parameters. As my app is supporting various file formats for writing (and exporting those UTIs) the user can open a file in one format and save it to another. NSDocument is calling the mentioned methods implicitly after completing the "Save as..." dialog. If this happens, the passed-on fileType is still the one of format #1, although the file is saved with the file name extension of format #2. This hick-up is not directly obvious to the user. But if the file is re-saved (e.g. after modifications), Cocoa is trying to extend the sandbox for the URL of type #1, and fails with the following error message at the Xcode console: -[STBDocument saveToURL:ofType:forSaveOperation:completionHandler:] [Line 521] typeName: com.janome.jef -[STBDocument saveToURL:ofType:forSaveOperation:completionHandler:] [Line 523] targetTypeUTI: com.tajima.dst NSFileSandboxingRequestRelatedItemExtension: Failed to issue extension for /Users/matthias/Desktop/Ohne Titel.jef because: Error Domain=NSPOSIXErrorDomain Code=3 "No such process" -[NSFileCoordinator itemAtURL:willMoveToURL:] could not get a sandbox extension. oldURL: file:///Users/matthias/Desktop/Ohne%20Titel.dst, newURL: file:///Users/matthias/Desktop/Ohne%20Titel.jef I'm currently fixing this issue by determining the UTType for the new file name extension and passing it to super. Unfortunately I have no idea how long this issue was already present, and cannot replicate it with a sample app based on Apple's Xcode 26 template (too many differences to my >15 years old app) - so I won't file a bug report. Take this post just for information in case someone else is facing a similar situation...
Replies
0
Boosts
0
Views
96
Activity
Apr ’26
iOS26的TabBar是否支持lottie动画?
iOS26的TabBar是否支持lottie动画?
Topic: UI Frameworks SubTopic: UIKit
Replies
1
Boosts
0
Views
66
Activity
Apr ’26
There is a problem using UIImagePickerController
When taking a photo with UIImagePickerController, the text in "Use Photo" shifts. What could be the reason for this?
Topic: UI Frameworks SubTopic: General
Replies
1
Boosts
0
Views
80
Activity
Apr ’26
Initial presentation of popover hangs when shown from a button in the toolbar
I have a simple reproducer here: struct ContentView: View { @State private var isOn = false @State private var isPresented = false var body: some View { NavigationStack { Color.blue .toolbar { ToolbarItem(placement: .topBarTrailing) { Button("Press here") { isPresented = true } .popover(isPresented: $isPresented) { Color.green .frame(idealWidth: 400, idealHeight: 500) .presentationCompactAdaptation(.popover) } } } } } } When I tap on the button in the toolbar you can see there is a hang then the popover shows. Then every time after there is no longer a hang so this seems like a bug. Any ideas? I'm using Xcode 26.3 and a iPad Pro 13-inch (M5) (26.4) simulator.
Replies
3
Boosts
3
Views
192
Activity
Apr ’26
Window size of iOS app running on Mac
I need constraint the window size for an iOS app running on Mac. That's easy for a MacApp, using self.window?.minSize.width = 450 self.window?.maxSize.width = 450 or use func windowDidResize(_ notification: Notification) { } but how to achieve it in UIKit ?
Replies
3
Boosts
0
Views
448
Activity
Apr ’26
X button disappeared on iPadOS 26.4 in MFMailComposeViewController
I’m using MFMailComposeViewController to send emails from my app. Since updating to iPadOS 26.4, there is no way to cancel the mail composer because the “X” button in the top-left corner has disappeared. On iPhone with iOS 26.4, everything still seems to work as expected. Is this a known issue, or am I missing something? Has anyone else experienced this, or found a workaround?
Topic: UI Frameworks SubTopic: UIKit
Replies
4
Boosts
0
Views
352
Activity
Apr ’26
NSTextAttachment.character symbol suddenly not available anymore resulting in compiler error
I published the latest update of my AppKit app in September with macOS 26.0. I just wanted to create a new update, but compiling on macOS 26.4 now fails because of the symbol NSTextAttachment.character which is referenced in my code. The error is Type 'NSTextAttachment' has no member 'character' I've never experienced before that a symbol suddenly is not available anymore without even a deprecation notice from one OS release to the next, let alone a minor release. Is this a bug in macOS or Xcode, or should I start worrying about symbols becoming unavailable anytime?
Replies
3
Boosts
1
Views
285
Activity
Apr ’26
Potentially Unfair Limitation for Third-Party Keyboard Developers
When developing a custom keyboard on iOS, even after enabling Full Access (RequestsOpenAccess = true), it is still not possible to record audio — the recording simply does not start. This is despite the fact that: the user is explicitly warned the user provides informed consent by enabling Full Access According to Apple’s documentation: https://developer.apple.com/documentation/uikit/configuring-open-access-for-a-custom-keyboard “However, with RequestsOpenAccess set to true, the keyboard has all the capabilities in the preceding list.” At the same time, the preceding list includes: “No access to microphone and speaker” This creates ambiguity. The wording suggests that enabling Full Access should lift prior restrictions, yet in practice, microphone access remains unavailable to third-party keyboards. Why this is concerning With Full Access enabled, a keyboard already has: network access the ability to transmit user input From a privacy standpoint, this is already highly sensitive. Preventing microphone access while allowing these capabilities appears inconsistent. Meanwhile, Apple’s own system keyboard supports voice dictation, which creates a functional gap between first-party and third-party keyboards. Competition perspective This raises a broader question about equal access to platform capabilities. Restricting third-party keyboards from using the microphone — while first-party solutions can — may be seen as: unequal treatment of developers a limitation of competition in input methods Such differences are increasingly scrutinized under EU regulations like the Digital Markets Act and Article 102 TFEU, which emphasize fair access to platform features and prohibit self-preferencing by dominant platforms. Request for clarification Is microphone access intentionally restricted for all third-party keyboards, even with Full Access enabled? If so, what is the technical or policy justification? Are there plans to provide a secure and user-consented way to enable audio input for custom keyboards? Clarification on this would help developers better understand platform limitations and design decisions.
Replies
0
Boosts
0
Views
196
Activity
Apr ’26
UITextView cursor sometimes jumps up when pressing arrow down key and setting typingAttributes
My app uses TextKit 1 and unfortunately still cannot migrate to TextKit 2 because of some bugs (for instance in FB17103305 I show how NSTextView.shouldDrawInsertionPoint has no effect, but I opened that feedback exactly one year ago and it still has no answer). Unfortunately TextKit 1 has another bug which causes the text cursor to jump unpredictably up or down when pressing the arrow keys and setting UITextView.typingAttributes. Run the code below on iPhone 17 Pro Max Simulator. Scroll the text down until you see “Header 2”. Place the text cursor after “# “. Press the arrow down key twice to move the cursor two lines down. The cursor moves to the top of the view instead. Continuing to press the arrow keys up and down results in the cursor sometimes moving as expected, other times jumping around wildly. Does anyone know a workaround? I created FB22382453. class TextView: UITextView, UITextViewDelegate { override func awakeFromNib() { let _ = layoutManager delegate = self let header = textAttributes(fontSize: 30) let body = textAttributes(fontSize: 15) let string = NSMutableAttributedString(string: String(repeating: "a", count: 2681) + "\n", attributes: body) string.append(NSAttributedString(string: """ # Header 1 """, attributes: header)) string.append(NSMutableAttributedString(string: String(repeating: "a", count: 5198) + "\n", attributes: body)) string.append(NSAttributedString(string: """ # Header 2 """, attributes: header)) string.append(NSMutableAttributedString(string: String(repeating: "a", count: 7048) + "\n", attributes: body)) textStorage.setAttributedString(string) } func textViewDidChangeSelection(_ textView: UITextView) { typingAttributes = textStorage.attributes(at: selectedRange.location - 1, effectiveRange: nil) } private func textAttributes(fontSize: Double) -> [NSAttributedString.Key: Any] { var textAttributes = [NSAttributedString.Key: Any]() textAttributes[.font] = UIFont(name: "Courier", size: fontSize) let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.minimumLineHeight = round(fontSize * 1.3) paragraphStyle.maximumLineHeight = paragraphStyle.minimumLineHeight textAttributes[.paragraphStyle] = paragraphStyle return textAttributes } }
Topic: UI Frameworks SubTopic: UIKit Tags:
Replies
0
Boosts
0
Views
134
Activity
Mar ’26
PDFView left-anchors to window edge instead of centering between sidebar and inspector (macOS Tahoe)
I'm building a document viewer on macOS Tahoe with a 3-column NSSplitViewController (sidebar | detail | inspector), trying to replicate how Preview displays PDFs with the page centered in the visible gap between the panels, with content bleeding under them when panning or zooming. I'm using the approach from Build an AppKit app with the new design (WWDC25): detailItem.automaticallyAdjustsSafeAreaInsets = true safeAreaInsets reports the correct values (e.g. left: 208, right: 240), and the frame does extend under both panels. But PDFView with autoScales = true anchors the page to the left edge of the window instead of centering it in the visible gap between the sidebar and inspector. I can get the page to center correctly by constraining PDFView to view.safeAreaLayoutGuide, but then content no longer extends under the panels when panning or zooming, which defeats the whole purpose. What's the correct way to center PDFView content within the visible gap while keeping the frame full-width so content bleeds under the panels? I've attached pictures of how Preview does it.
Topic: UI Frameworks SubTopic: AppKit
Replies
0
Boosts
0
Views
104
Activity
Mar ’26
UIScrollView Fast Scrolling have it scroll to the beginning when i press dpad to left while vertical scrolling
I'm triaging the issue with the fast scrolling on UIScrollView and I'm really upset how little to no info this component is on the internet. Like i disabled scrolling and yet after holding the dpad down to scroll downward eventually the fast scroll mechanism is used. The issue I have is that I have a setup where the scrollview scrolling is disabled and whenever cells are focused it'll scroll to the cell's position for that cells to focused on top left side of the scrollview. I start off with the cell placed at position x of 1000. I scroll down enough to enable fast scrolling. While fast scrolling vertically, i press left a couple of time and it somewhat scroll horizontally. Actual scrollView index is suddenly placed to x:0 instead of let say x:950-1000 Expected should be scrolled to the left of cell at position x 1000. Attached the stack trace and the log showcasing it and run it on simulator tvos 26.2. Initial properties of the scrollview we set up scroll = [UIScrollView new]; scroll.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever; scroll.automaticallyAdjustsScrollIndicatorInsets = false; [scroll setDirectionalLockEnabled:TRUE]; [scroll setContentInset:UIEdgeInsetsMake(0, 0, 0, 0)]; scroll.bounces = FALSE; scroll.delaysContentTouches = FALSE; scroll.showsVerticalScrollIndicator = FALSE; scroll.showsHorizontalScrollIndicator = FALSE; The plan for me at least is to somehow disable the horizontal scrolling interaction of it when fast scrolling is enabled, without accessing the private components of it.
Replies
0
Boosts
0
Views
113
Activity
Mar ’26
Scene resizing on iPad breaks UIPageViewController's setViewControllers
The following is verbatim of a feedback report (FB22367951) I submitted, shared here as someone else might be interested to see it. I have reproduced this bug on iPadOS 26.3.1 (a) and 26.4. During scene resizing on iPad, UIPageViewController's setViewControllers method fails to do its work. The navigation starts and for a brief moment you can see the new view controller coming from the expected direction, but shortly after it fails and stays on the same [current] view controller. It doesn't even call the completion handler when it fails. When the navigation succeeds (due to not resizing a scene during the navigation) after previously failing at least once, the completion handler is sometimes called more than once. I have created a demo project, which I have pushed to this repo: https://github.com/galijot/SceneResize-Breaks-UIPageViewController I have also attached a zip of the project to this report.
Replies
0
Boosts
0
Views
201
Activity
Mar ’26
MapKit in SwiftUI
Anyone worked with MapKit's MapCameraPosition in SwiftUI? I'm building a navigation app and ran into a limitation I can't find a clean solution for when using .userLocation(followsHeading: true) MapKit takes full control of the camera, smooth heading tracking, follows the user automatically. Perfect. But there's no way to set a custom pitch (tilt) on it. The only initializer available is... .userLocation(followsHeading: true, fallback: .automatic) No pitch, no distance parameters.... The workaround I found is setting .camera(MapCamera(..., pitch: 60)) first, waiting 200ms, then switching to .userLocation(followsHeading: true), MapKit inherits the pitch from the rendered camera state before handing off to user tracking.... It works, but it's clearly exploiting an undocumented behaviour in MapKit's state machine rather than a proper API Has anyone found a cleaner way to achieve this? Or is UIViewRepresentable wrapping MKMapView the only proper solution? It would be awesome to have something like this cameraPosition = .userLocation( followsHeading: true, pitch: 60, distance: 800, fallback: .automatic )
Replies
1
Boosts
0
Views
145
Activity
Mar ’26
NavigationLink selection in DisclosureGroup not working with .draggable modifier
NavigationLink selection in DisclosureGroup not working with .draggable modifier This was recently also posted here: https://stackoverflow.com/questions/79914290/ I am playing around with a tree data structure with folders and entries.I would like to add dragging of entries and folders between folders, using .draggable and dropDestination. In my current code, dragging works, but selection of entries no longer works, except if I click outside of the Text If I comment out .draggable(subfolder.name) in func FolderRow(), selection works as expected. How can I make sure both selection and drag and drop works for both folders and entries? I also tried using Transferable and Codable, but I get the same result. Here is an MRE: import SwiftData import SwiftUI @Model final class Folder { @Attribute(.unique) var name: String // Parent var parentFolder: Folder? // Child folders @Relationship(deleteRule: .cascade, inverse: \Folder.parentFolder) var subfolders: [Folder] = [] // Leaf entries @Relationship(deleteRule: .cascade, inverse: \Entry.folder) var entries: [Entry] = [] init(name: String, parentFolder: Folder? = nil) { self.name = name self.parentFolder = parentFolder } } @Model final class Entry { @Attribute(.unique) var name: String var detail: String var folder: Folder? // recursive relationship init(name: String, detail: String) { self.name = name self.detail = detail } } @main struct TestMacApp: App { var body: some Scene { WindowGroup { SidebarView() .modelContainer(for: Folder.self) } } } struct SidebarView: View { @Environment(\.modelContext) private var context @Query(filter: #Predicate<Folder> { $0.parentFolder == nil }) private var rootFolders: [Folder] var body: some View { NavigationSplitView { List { ForEach(rootFolders) { folder in FolderRow(folder: folder) .draggable(folder.name) } } } detail: { Text("detail") } .onAppear { seed() } } } struct FolderRow: View { @Environment(\.modelContext) private var context var folder: Folder @State private var isExpanded: Bool = true var body: some View { DisclosureGroup(isExpanded: $isExpanded) { // Subfolders ForEach(folder.subfolders) { subfolder in FolderRow(folder: subfolder) .draggable(subfolder.name) // disabling this line fixes the selection } // Entries (leaf nodes) ForEach(folder.entries) { entry in NavigationLink(destination: EntryDetail(entry: entry)) { EntryRow(entry: entry) } .draggable(entry.name) } } label: { Label(folder.name, systemImage: "folder") } .dropDestination(for: String.self) { names, _ in return handleDrop(of: names) } } } struct EntryRow: View { var entry: Entry var body: some View { Text(entry.name) } } struct EntryDetail: View { var entry: Entry var body: some View { Text(entry.detail) } } extension FolderRow { private func handleDrop(of names: [String]) -> Bool { do { for name in names { if let droppedEntry = try context.fetchFilteredModel(filter: #Predicate<Entry> { x in x.name == name }) { droppedEntry.folder = folder print("dropped \(droppedEntry.name) on \(folder.name)") } else if let droppedFolder = try context.fetchFilteredModel(filter: #Predicate<Folder> { x in x.name == name }) { if droppedFolder.parentFolder != nil && droppedFolder != folder { droppedFolder.parentFolder = folder print("dropped \(droppedFolder.name) on \(folder.name)") } } } return true } catch { debugPrint(error.localizedDescription) return false } } } extension SidebarView { private func seed() { do { // delete current models for folder: Folder in try context.fetchAllModels() { context.delete(folder) } try context.save() let rootFolder = Folder(name: "Root") let entry1 = Entry(name: "One", detail: "Detail One") let entry2 = Entry(name: "Two", detail: "Detail Two") rootFolder.entries.append(contentsOf: [entry1, entry2]) let subFolder1 = Folder(name: "Sub1", parentFolder: rootFolder) let entry3 = Entry(name: "Three", detail: "Detail Three") let entry4 = Entry(name: "Four", detail: "Detail Four") subFolder1.entries.append(contentsOf: [entry3, entry4]) let subFolder2 = Folder(name: "Sub2", parentFolder: rootFolder) let entry5 = Entry(name: "Five", detail: "Detail Five") let entry6 = Entry(name: "Six", detail: "Detail Six") subFolder2.entries.append(contentsOf: [entry5, entry6]) context.insert(rootFolder) } catch { debugPrint(error) } } } extension ModelContext { // convenience methods func fetchAllModels<M>() throws -> [M] where M: PersistentModel { let fetchDescriptor = FetchDescriptor<M>() return try fetch(fetchDescriptor) } func fetchFilteredModels<M>(filter: Predicate<M>) throws -> [M] where M: PersistentModel { let fetchDescriptor = FetchDescriptor<M>(predicate: filter) return try fetch(fetchDescriptor) } func fetchFilteredModel<M>(filter: Predicate<M>) throws -> M? where M: PersistentModel { return try fetchFilteredModels(filter: filter).first } }
Topic: UI Frameworks SubTopic: SwiftUI
Replies
1
Boosts
0
Views
195
Activity
Mar ’26
Left navigation bar items become invisible after rotating device and presenting detail view in split view
A user of my app, whose main view is a split view, reported an issue which causes the left navigation bar items to disappear without apparent reason if they rotate the device and later show the detail view, preventing them from using the back button to show the root view again. Am I doing something wrong or is it a bug? I can reproduce the issue with the following steps: Create a new document-based iOS app (as it uses scenes by default, as opposed to a regular app). Paste the code below. In the target build settings, delete "Launch Screen Interface File Base Name" and set "Launch Screen (Generation)" to YES. Without this step, for some reason, the issue doesn't happen. Launch the app in iPhone Simulator. Tap the top left button to show the root view, then the “detail” button to show the detail view. The left navigation bar button is still visible. Rotate the Simulator window right, then left again. Tap the top left button to show the root view, then the “detail” button to show the detail view. Now the left navigation bar button is invisible. Rotating the device right and left again solves the issue. I filed FB22363118. class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { window = UIWindow(windowScene: scene as! UIWindowScene) let split = SplitViewController() window!.rootViewController = split window!.makeKeyAndVisible() split.showDetail() } } class SplitViewController: UISplitViewController, UISplitViewControllerDelegate { var detailNavigationViewController: UINavigationController! init() { super.init(nibName: nil, bundle: nil) detailNavigationViewController = UINavigationController(rootViewController: DetailViewController()) viewControllers = [UINavigationController(rootViewController: RootViewController())] } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } func showDetail() { showDetailViewController(detailNavigationViewController, sender: nil) } func showRoot() { (viewControllers.first as? UINavigationController)?.popViewController(animated: true) } } class RootViewController: UIViewController { override func loadView() { navigationItem.title = "root" let button = UIButton(primaryAction: UIAction(handler: { [self] _ in (splitViewController as! SplitViewController).showDetail() })) button.setTitle("detail", for: .normal) view = button } } class DetailViewController: UIViewController { override func loadView() { navigationItem.title = "detail" view = UIView() registerForTraitChanges([UITraitHorizontalSizeClass.self]) { (self: Self, previousTraitCollection) in if previousTraitCollection.horizontalSizeClass != self.traitCollection.horizontalSizeClass { self.updateBarButtons() } } updateBarButtons() } private func updateBarButtons() { navigationItem.leftBarButtonItem = UIBarButtonItem(primaryAction: UIAction(image: UIImage(systemName: "sidebar.leading")) { [self] _ in (self.splitViewController as! SplitViewController).showRoot() }) navigationItem.rightBarButtonItem = UIBarButtonItem(title: "right") } }
Topic: UI Frameworks SubTopic: UIKit Tags:
Replies
0
Boosts
0
Views
87
Activity
Mar ’26
How to recreate Apple Music mini player transition in SwiftUI
Hello, I am building an audio player app in SwiftUI and trying to recreate the behavior of Apple Music's mini player and full player. I'm struggling to get the animation to seamlessly transition between the mini player and the full player. Currently, it feels disconnected and doesn't resemble the smooth animation seen in Apple Music. What I want to achieve: Full player that expands/collapses from/to the mini player Smooth artwork transition between both states Drag down to collapse the full player Support both newer APIs like tabViewBottomAccessory and older iOS versions Questions: What is the best way to build this transition in SwiftUI? Should I use matchedGeometryEffect or something else? Should this be a custom container instead of fullScreenCover? How would you support both new and older iOS versions? What is the best way to implement drag to dismiss? Thanks for any help! Example code: struct ContentView: View { @State private var isFullPlayerPresented = false var body: some View { TabView { Tab("Home", systemImage: "house") { Text("Home") .frame(maxWidth: .infinity, maxHeight: .infinity) .background(.green) } Tab("Library", systemImage: "rectangle.stack.fill") { Text("Library") .frame(maxWidth: .infinity, maxHeight: .infinity) .background(.brown) } } .tabViewBottomAccessory(isEnabled: !isFullPlayerPresented) { MiniPlayerView(isFullPlayerPresented: $isFullPlayerPresented) } .fullScreenCover(isPresented: $isFullPlayerPresented) { // Maybe it's not a full screen cover presentation in Apple Music? FullPlayerView(isFullPlayerPresented: $isFullPlayerPresented) } } } Mini player: struct MiniPlayerView: View { @Binding var isFullPlayerPresented: Bool var body: some View { Button { isFullPlayerPresented = true } label: { HStack { Image(systemName: "photo") .resizable() .scaledToFit() .frame(width: 30, height: 30) .clipShape(.rect(cornerRadius: 8)) Spacer() Text("Tap to open full player") Spacer() Button("", systemImage: "play.fill", action: {}) } .padding(.horizontal) .padding(.vertical, 4) } .foregroundStyle(.white) } } Full player: struct FullPlayerView: View { @Binding var isFullPlayerPresented: Bool var body: some View { // This art work needs to snaps to the artwork in mini player Image(systemName: "photo") .resizable() .scaledToFit() .frame(width: 250, height: 250) .clipShape(.rect(cornerRadius: 20)) .frame(maxWidth: .infinity, maxHeight: .infinity) .background(.red) .overlay(alignment: .topTrailing) { Button(role: .close) { isFullPlayerPresented = false } .foregroundStyle(.white) .padding() } } }
Replies
2
Boosts
0
Views
376
Activity
Mar ’26
NSBrowser -deselectAll: broken on macOS Tahoe 26.4
So if I have a selection in NSBrowser. I hit Option+Command+A to invoke "Deselect" the selection in the parent for the last column drops its selections, as expected. But the column doesn't drop off the browser. The delegate method (void)browser:(NSBrowser *)browser didChangeLastColumn:(NSInteger)oldLastColumn toColumn:(NSInteger)column NS_SWIFT_UI_ACTOR; Never fires (since the column isn't dropped off). But we have. dangling last column with no selection in the previous column. Now if there's enough room for me to deselect by clicking the background, the last column drops off, as expected. This seems to be fairly new? Anyone else experiencing this? These 26point updates seem to keep punching me in the face. And yes. deselect all seems somewhat broken in Column view in the Finder as well, but in a seemingly different way. In Finder it just seems to change the selection color like it deactivated the window but doesn't drop the selection. For me, the selection IS dropped but the column remains visible. Maybe they are using Cocoa bindings or something that resyncs the selection after the mess up. I dk.
Replies
0
Boosts
0
Views
156
Activity
Mar ’26
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
0
Boosts
0
Views
78
Activity
Mar ’26
Localization in Swift macOS console Apps.
Is it possible to build localization into console apps, developed in SwiftUI in Xcode26. I have created a catalog, (.xcstrings file) with an English and fr-CA string. I have tried to display the French text without success. I am using the console app to test a package which also has English/French text. English text works fine in both package and the console main, but I cannot generate the French. From what I can discover so far it's not possible without bundling it as a .app, (console app). Looking for anyone who has crossed this bridge.
Replies
7
Boosts
0
Views
469
Activity
Mar ’26