Thanks for being a part of WWDC25!

How did we do? We’d love to know your thoughts on this year’s conference. Take the survey here

Construct and manage a graphical, event-driven user interface for your macOS app using AppKit.

AppKit Documentation

Posts under AppKit subtopic

Post

Replies

Boosts

Views

Activity

NSTableView batch update issue
I create a simple list, there are two kind of item: conversation and message, if click a conversation, it will expand it's messages. So there will be some insert and remove updates. I calculate the difference results and perform the batch update. displayItems = newDisplayItems tableView.beginUpdates() tableView.removeRows(at: IndexSet(removeIndex), withAnimation: [.effectFade, .slideUp]) tableView.insertRows(at: IndexSet(insertIndex), withAnimation: [.effectFade, .slideDown]) tableView.endUpdates() In most cases, the animation looks fine, but when the mock data is large (> 1000?) and called scrollRowToVisible to scroll to the last row in viewDidAppear, then click some conversations above, the UI broken. Is there something wrong? Would appreciate help from someone experienced with NSTableView. Example code
Topic: UI Frameworks SubTopic: AppKit
2
0
35
May ’25
memory leak upon mouse down in a NSTextField
Dear all, Sorry if the topic has already been commented but I could not be able to find it in over 10,000 picks using the forum search field... My problem is that with any NSTextField in my app, a click will result in a memory leak. There is no code attached, just bindings to NSNumber properties. How can I fix this ? Thanks for your help. Best regards Chris
Topic: UI Frameworks SubTopic: AppKit
0
0
41
May ’25
Getting the content of an NSView in an NSImage in OSX15 sequoia
as all the nice system services that were CGWindowListCreateImage, lockFocus are now deprecated, and drastic restictions are imposed to CALayer/renderInContext:, i just cant figure out how to design a single generic obj C method to capture the content of an NSView , whatever this view is ( subclassed or not, layer backed of not ). As simple as that. this was the method, which now no longer works : //============================================================================= +(NSImage*) viewImageShot:(NSView*)view :(BOOL)bFlip //============================================================================= // capture an NSImage of the given view content // pass window.contentView to capture the whole window,cropping the title bar { CGFloat wantedLayer = view.wantsLayer; BOOL wasHidden= view.hidden; view.hidden= NO; view.wantsLayer= YES; NSImage *requiredImage = [[NSImage alloc] initWithSize:view.bounds.size]; [requiredImage lockFocus]; CGContextRef context= [[NSGraphicsContext currentContext] CGContext]; if(bFlip) FlipContext(context, view.bounds.size.height); [view.layer renderInContext:context]; [requiredImage unlockFocus]; view.wantsLayer = wantedLayer; view.hidden = wasHidden; return requiredImage; } Is there here some hope to get some help or direction to acheive this goal ? thanks to those whow will answer.
Topic: UI Frameworks SubTopic: AppKit
1
0
51
May ’25
macOS: First Hangul input ignored/separated after locale change or initial focus with default Korean IME
I reported Korean IME bug to QT Bug report. Please refer to below link. https://bugreports.qt.io/browse/QTBUG-136128?jql=text%20~%20korean%20ORDER%20BY%20created%20DESC But, QT reponsed me like follwing. Thank you for reporting. However, this issue seems like a known issue with apple's Korean IME. There are many threads in Korean community about the same problem with Non-Qt apps. If this issue is a really Qt issue, feel free to open it again. Is there any workaround to fix this IME bug ? Thanks, Ted
0
0
63
May ’25
Collection view with self-sizing cells with SwiftUI content
I am trying to make a collection view with self-sizing cells that adapt to SwiftUI content. My test platform is macOS, but it should work on iOS all the same. I chose macOS because on macOS, you can resize the window and cause more interesting scenarios with that. My layout intent is fairly simple: a one-column collection view with cells with SwiftUI content, where the collection view cells should adapt to the height of their SwiftUI content. I got it working almost correctly. The one scenario that I don’t have working is window resizing. When the window resizes, the layout and cells should adapt to the content and change their heights. I feel that I am missing something fairly basic. How do I change this project so that the layout works correctly when I change the macOS window width? Example project and video of the behavior: https://gist.github.com/jaanus/66e3d863941ba645c88220b8a22970e1
Topic: UI Frameworks SubTopic: AppKit
1
0
324
Apr ’25
NSOpenPanel fails with NSUserDefaults domain 'nil' under SDL2 with macOS Sequoia . SDL2 C++
This is with SDL2 and C++ Due to the new security design of Sequoia involving the sandboxed helper processes (via ViewBridge) to show open/save panels, my existing code for invoking Open/SaveAs/FolderSelect dialogs no longer works and instead terminates with ViewBridge Code=14 "(null)" error. Even in the simplest of forms such as; nfdresult_t NFD_OpenDialogN_With_Impl(nfdversion_t version, nfdnchar_t** outPath, const nfdopendialognargs_t* args) { nfdresult_t result = NFD_CANCEL; NSOpenPanel* dialog = [NSOpenPanel openPanel]; if ([dialog runModal] == NSModalResponseOK) { result = NFD_OKAY; } return result; } ...Will no longer work. My Question is essentially, how can I resolve this NSUserDefaults domain empty/nil issue ( currently I don't pass anything for sharing defaults during the process ). Dump of fault provided in crash.txt ( the program doesn't actually crash, it just doesn't invoke the file-open dialog ) crash.txt
1
1
129
Apr ’25
Force NSDocument save panel to select most specific type in format popup button
My app supports different plain text file formats, including the standard .txt and Markdown. When creating a new document, my app already asks which format it should have, so when saving it, I would expect that the save panel already selects that format in the popup button, but currently it always selects "Plain Text". For example, I would expect for a Markdown document that it selects "Markdown" instead of "Plain Text". Is there a way to force it to select the most specific format matching the document format?
Topic: UI Frameworks SubTopic: AppKit Tags:
5
1
55
Apr ’25
Overriding NSDocument.prepareSavePanel(_:) hides file format popup button
I would like to provide a default filename when saving a document depending on the document data. I thought I could do so by overriding NSDocument.prepareSavePanel(_:) and setting NSSavePanel.nameFieldStringValue, but simply implementing that method seems to hide the file format popup button shown by default (see image). Calling super doesn't help. Is it possible to set a default filename and keep the file format popup button? On macOS 15, I can toggle NSSavePanel.showsContentTypes, but how about macOS 14 and older?
Topic: UI Frameworks SubTopic: AppKit Tags:
4
0
53
Apr ’25
NSTableView.clickedRow sometimes is greater than number of rows
Xcode has been downloading many similar crash reports for my app for some time now, related to an index out of range runtime exception when accessing a Swift array. The crashes always happen in methods triggered by user input or during menu item validation when I try to access the data source array by using the following code to determine the indexes of the relevant table rows: let indexes = clickedRow == -1 || selectedRowIndexes.contains(clickedRow) ? selectedRowIndexes : IndexSet(integer: clickedRow) I was never able to reproduce the crash until today. When the app crashed in the Xcode debugger, I examined the variables clickedRow and selectedRowIndexes.first, which were 1 and 0 respectively. What's interesting: the table view only contained one row, so clickedRow was effectively invalid. I tried to reproduce the issue several times afterwards, but it never happened again. What could cause this issue? What are the circumstances where it is invalid? Do I always have to explicitly check if clickedRow is within the data source range?
Topic: UI Frameworks SubTopic: AppKit Tags:
4
0
44
Apr ’25
How to match Status/Menu Bar font (Date and Time)
Hello, I am trying to match the font and the position of the date and time displayed. This is what it looks like: .font: NSFont.systemFont(ofSize: NSFont.systemFontSize, weight: .regular), .foregroundColor: NSColor.labelColor, .baselineOffset: 0.5 It looks great on built-in display, but on external 4K it is incorrect. The baselineOffest is unnecessary, and the font looks skinny in comparison. Can anyone comment on such issue?
5
0
77
Apr ’25
📍 Inconsistent POI Visibility Between Apple Maps and MKLocalSearch / CLGeocoder
Summary: Hi! I'm experiencing an issue between what Apple Maps displays in the UI and what is accessible via MapKit APIs such as MKLocalSearch and CLGeocoder. Specifically, some verified and publicly visible businesses (with proper POI categories like .cafe) are not returned by coordinate-based POI searches or reverse geocoding, even when their exact address and coordinates are known. Repro Steps: In Apple Maps (iOS), search for Rustic Knead located at: 1130 State St, Lemont, IL 60439 Note that: The business appears visually on the map It is categorized as a cafe The address is 1130 State St Now in code, attempt both: A coordinate-based POI search using MKLocalSearch.Request(region:) A reverse geocoding lookup via CLGeocoder.reverseGeocodeLocation Example coordinates: latitude: 41.673604, longitude: -88.002295 Expected Behavior: Given the precise coordinates and Apple Maps showing the POI publicly, I expect either or both of the following: MKLocalSearch should return the POI (Rustic Knead) within a reasonable radius and appropriate .pointOfInterestFilter. CLGeocoder should return a CLPlacemark with either name = "Rustic Knead" or an areasOfInterest value containing it. Actual Behavior: MKLocalSearch returns 0 POIs at that location, even with filters removed and radius increased to 500+ meters. CLGeocoder.reverseGeocodeLocation returns a valid postal address but does not include the business name in .name or areasOfInterest. Searching by business name (e.g., "Rustic Knead") does work, but coordinate/address-only lookups do not. Questions: Are there known limitations in how MapKit surfaces POIs by coordinate or reverse geocode? Is there a way to ensure public, categorized POIs are accessible programmatically via MapKit APIs? Is there a better strategy to provide coordinates and return a business name?
1
0
40
Apr ’25
NSToolbar doesn't restore displayMode when NSWindow.titleVisibility = .hidden
Apparently when setting a window to hide its title, the toolbar's displayMode is not restored when relaunching the app. For example, by default my app sets to show toolbar icons only, but when right-clicking it, selecting "Icon and Text" and relaunching the app, it's again "Icon Only". Is there a workaround? I've filed FB17144212. class ViewController: NSViewController, NSToolbarDelegate { override func viewDidAppear() { let toolbar = NSToolbar(identifier: "toolbar") toolbar.delegate = self toolbar.autosavesConfiguration = true toolbar.displayMode = .iconOnly view.window?.titleVisibility = .hidden view.window?.toolbar = toolbar view.window?.toolbarStyle = .unified } func toolbarAllowedItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] { return [.init(rawValue: "item")] } func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] { return [.init(rawValue: "item")] } func toolbar(_ toolbar: NSToolbar, itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier, willBeInsertedIntoToolbar flag: Bool) -> NSToolbarItem? { let item = NSToolbarItem(itemIdentifier: itemIdentifier) item.image = NSImage(named: NSImage.addTemplateName)! item.label = "item" return item } }
Topic: UI Frameworks SubTopic: AppKit Tags:
1
0
38
Apr ’25
NSLayoutManager returning inconsistent values for a glyph's text container and its line fragment rect
TLDR: NSLayoutManager's textContainer(forGlyphAt:effectiveRange:) and lineFragmentRect(forGlyphRange:effectiveRange:) are returning inconsistent results. Context: I'm developing a word processing app that paginates from an NSTextStorage using NSLayoutManager. My app uses a text attribute (.columnType) to paginate sub-ranges of the text at a time, ensuring that each columnRange gets a container (or series of containers across page breaks) to fit. This is to support both multi-column and standard full-page-width content. After any user edit, I update pagination data in my Paginator model class. I calcuate frames/sizes for the views/containers, along with what superview they belong to (page). The UI updates accordingly. In order to determine whether the columnRange has overflowed from a container due to a page break OR whether the range of text hasn't overflowed its container and is actually using less space than available and should be sized down, I call both: layoutManager.textContainer(forGlyphAt: lastGlyphOfColumn, effectiveRange: &actualGlyphRangeInContainer)` // and `layoutManager.lineFragmentRect(forGlyphAt: lastGlyphOfColumn, effectiveRange: nil) Apple Documentation notes that both these calls force glyph generation and layout. As I'm in early development, I have not set non-contiguous layout. So these should be causing full layout, assuring accurate return values. Or so I'd hoped. This does work fine in many cases. I edit. Pagination works. But then I'll encounter UI-breaking inconsistent returns from these two calls. By inconsistent, I mean that the second call returns a line fragment rect that is in the container coordinates of A DIFFERENT container than the container returned by the first call. To be specific, the line fragment rect seems to be in the coordinates of the container that comes next in layoutManager.textContainers. Example Code: if !layoutManager.textContainers.indices.contains(i) { containerToUse = createTextContainer(with: availableSize) layoutManager.addTextContainer(containerToUse) } else { // We have a container already but it may be // the wrong size. containerToUse = layoutManager.textContainers[i] if containerToUse.size.width != availableSize.width { // Mandatory that we resize if we don't have // a matching width. Height resizing is not // mandatory and requires a layout check below. containerToUse.size = availableSize } } let glyphRange = layoutManager.glyphRange(forCharacterRange: remainingColumnRange, actualCharacterRange: nil) let lastGlyphOfColumn = NSMaxRange(glyphRange) - 1 var containerForLastGlyphOfColumn = layoutManager.textContainer(forGlyphAt: lastGlyphOfColumn, effectiveRange: &actualGlyphRangeInContainer) if containerForLastGlyphOfColumn != containerToUse && containerToUse.size.height < availableSize.height { // If we are here, we overflowed the container, // BUT the container we overflowed didn't use // the maximum remaining page space (this // means it was a pre-existing container that // needs to be sized up and checked once more). // NOTE RE: THE BUG: // at this point, prints show... // containerToUse.size.height // =628 // availableSize.height // =648 containerToUse.size = availableSize containerForLastGlyphOfColumn = layoutManager.textContainer(forGlyphAt: lastGlyphOfColumn, effectiveRange: &actualGlyphRangeInContainer) } // We now check again, knowing that the container we // are testing flow into is the max size it can be. if containerForLastGlyphOfColumn != containerToUse { // If we are here, we have overflowed the // container, so containerToUse size SHOULD be // final/accurate, since it is fully used. actualCharRangeInContainer = layoutManager.characterRange(forGlyphRange: actualGlyphRangeInContainer, actualGlyphRange: nil) // Start of overflow range is the first character // in the container that was overflowed into. let overflowLoc = actualCharRangeInContainer.location remainingColumnRange = NSRange(location: overflowLoc, length: remainingColumnRange.length - overflowLoc) // Update page count as we have broken to a new page currentPage += 1 } else { // If we are here, we have NOT overflowed // from the container. BUT... // THE BUG: // ***** HERE IS THE BUG! ***** lineFragmentRectForLastChar = layoutManager.lineFragmentRect(forGlyphAt: lastGlyphOfColumn, effectiveRange: nil) let usedHeight = lineFragmentRectForLastChar.maxY // BUG: ^The lines of code above return a // fragment rect that is in the coordinates // of the WRONG text container. Prints show: // usedHeight // =14 // usedHeight shouldn't be just 14 if this is // the SAME container that, when it was 628 // high, resulted in text overflowing. // Therefore, the line fragment here seems // to be in the coordinates of the ENSUING // container that we overflowed INTO, but // that shouldn't be possible, since we're in // a closure for which we know: // // containerForLastGlyphOfColumn == containerToUse // // If the last glyph container is the container // we just had to size UP, why does the final // glyph line fragment rect have a maxY of 14!? // Including ensuing code below only for context. if usedHeight < containerToUse.size.height { // Adjust container size down to usedRect containerToUse.size = CGSize(width: containerToUse.size.width, height: usedHeight) } else if usedHeight == availableSize.height { // We didn't force break to a new page BUT // we've used exactly the height of our page // to layout this column range, so need to // break to a new page for any ensuing text // columns. currentPage += 1 } else if usedHeight > containerToUse.size.height { // We should have caught this earlier. Text // has overflowed, but this should've been // caught when we checked // containerForLastGlyphOfColumn != // containerToUse. // // Note: this error has never thrown. throw PaginationError.unknownError("Oops.") } } Per my comments in the code block above, I don't understand why the very same text container that just overflowed and so had to be sized up from 628 to 648 in order to try to fit a glyph would now report that same glyph as both being IN that same container and having a line fragment rect with a maxY of just 14. A glyph couldn't fit in a container when it was 628 high, but if I size it up to 648, it only needs 14? There's something very weird going on here. Working with NSLayoutManager is a bit of a nightmare given the unclear documentation. Any help or insight here would be massively, massively appreciated.
2
0
413
Apr ’25
Handle double click on a file associated with my application
When my application starts up I want to check if the application starts because of double click in Finder, or open file in Finder. If the file is opened from Finder I will not show Welcome screen and open directly the file. But if there is no such file it will show Welcome screen. I can register for kAEOpenDocuments but I had to wait for the event to be triggered and how long should I wait. What would be the proper way to handle this
1
0
37
Apr ’25
Menu Bar Icon NOT Showing Up
Hi, I'm trying to make a weather menu bar app, and I want to have it so that the icon of the app in the menu changes with the actual weather, but the icon isn't showing up. There is still a space in the menu bar where I can click and open the app, it's just that the icon has disappeared. Any ideas to fix it?
1
1
545
Apr ’25
Save fails after Save As
I have an app with two file types with the following extensions: gop (an exported type), sgf (an imported type). The Save command fails after the following sequence of events: I open a gop file, say the file "A.gop". I save this file as an sgf file, say "A.sgf". This Save As works perfectly and the document name in the document’s title bar has changed to "A.sgf". I change something in the document and then try to Save this change. This should just resave the document to "A.sgf", but "A.sgf" remains untouched. Instead I get a system alert with the message The document “A.sgf” could not be saved. A file with the name “A.gop” already exists. To save the file, either provide a different name, or move aside or delete the existing file, and try again. In the Xcode console I get the following diagnostic: NSFileSandboxingRequestRelatedItemExtension: an error was received from pboxd instead of a token. Domain: NSPOSIXErrorDomain, code: 2 [NSFileCoordinator itemAtURL:willMoveToURL:] could not get a sandbox extension. oldURL: file:///Users/francois/Desktop/A.sgf, newURL: file:///Users/francois/Desktop/A.gop The problem seems to relate to the sandbox. But I am at a loss to find a solution. (After closing the alert, I check that A.sgf did not register the change.) If I open an sgf file, say "B.sgf", save it as "B.gop", make a change in the document and then try to save this change (into "B.gop"), I hit the same problem, with "gop" and "sgf" interchanged. If, instead of saving "A.gop" as "A.sgf", I save it as "B.sgf", make a change in the document and then try to save this change into "B.sgf", I get the following system alert: The document “B.sgf” could not be saved. You don’t have permission. To view or change permissions, select the item in the Finder and choose File &gt; Get Info. And in the Xcode console I get the following diagnostic: NSFileSandboxingRequestRelatedItemExtension: an error was received from pboxd instead of a token. Domain: NSPOSIXErrorDomain, code: 2 [NSFileCoordinator itemAtURL:willMoveToURL:] could not get a sandbox extension. oldURL: file:///Users/francois/Desktop/B.sgf, newURL: file:///Users/francois/Desktop/B.gop Again the sandbox ! (After closing the alert, I check that B.sgf did not register the change.) It’s clear my code is missing something, but what?
0
0
50
Apr ’25
self.window.isVisible = NO not working in windowDidLoad
The following code won't work: - (void)windowDidLoad { [super windowDidLoad]; self.window.isVisible = NO; } The only main window still shows on application startup (in a minimal newly created app). One of my published apps in App Store relies on this behavior which had been working for many years since I started Xcode development.
Topic: UI Frameworks SubTopic: AppKit
11
0
95
Apr ’25
NSTextView.shouldDrawInsertionPoint doesn't work with TextKit 2
The following code only ever causes shouldDrawInsertionPoint to be printed (no drawInsertionPoint), but even if that method returns false, the blinking insertion point is still drawn. On the other hand, with TextKit 1 it works as expected. Is there a way to hide the default insertion point in TextKit 2? My app draws its own. I've filed FB13684251. class TextView: NSTextView { override var shouldDrawInsertionPoint: Bool { print("shouldDrawInsertionPoint") return false } override func drawInsertionPoint(in rect: NSRect, color: NSColor, turnedOn flag: Bool) { print("drawInsertionPoint", flag) } } ``
Topic: UI Frameworks SubTopic: AppKit Tags:
3
0
55
Apr ’25
NSDocumentController not adding "Open Recent" menu
I have a SwiftUI based app. For lots of reasons I was forced to use NSDocument instead of using DocumentGroup. I configure the main menu in my AppDelegate. It has the following code: let fileMenuItem = NSMenuItem() let fileMenu = NSMenu(title: "File") fileMenu.addItem(withTitle: "New", action: #selector(NSDocumentController.newDocument(_:)), keyEquivalent: "n") fileMenu.addItem(withTitle: "Open...", action: #selector(NSDocumentController.openDocument(_:)), keyEquivalent: "o") The New and Open work as expected. It is my understanding that the NSDocumentController should automatically add the "Open Recent" menu when it sees the Open action based on the NSDocumentController. It is not appearing. When I print the state of the recent documents using print("recent documents \(NSDocumentController.shared.recentDocumentURLs), maximum \(NSDocumentController.shared.maximumRecentDocumentCount)") I see the recent document urls and a count of 10. What can I do to make the menu appear? Thanks for the help.
5
0
189
Apr ’25
Get NSTextView selection frame with NSTextLayoutManager
I'm trying to update my app to use TextKit 2. The one thing that I'm still not sure about is how I can get the selection frame. My app uses it to auto-scroll the text to keep the cursor at the same height when the text wraps onto a new line or a newline is manually inserted. Currently I'm using NSLayoutManager.layoutManager!.boundingRect(forGlyphRange:in:). The code below almost works. When editing the text or changing the selection, the current selection frame is printed out. My expectation is that the selection frame after a text or selection change should be equal to the selection frame before the next text change. I've noticed that this is not always true when the text has a NSParagraphStyle with spacing > 0. As long as I type at the end of the text, everything's fine, but if I insert some lines, then move the selection somewhere into the middle of the text and insert another newline, the frame printed after manually moving the selection is different than the frame before the newline is inserted. It seems that the offset between the two frames is exactly the same as the paragraph style's spacing. Instead when moving the selection with the arrow key the printed frames are correct. I've filed FB17104954. class ViewController: NSViewController, NSTextViewDelegate { private var textView: NSTextView! override func loadView() { let scrollView = NSScrollView(frame: CGRect(x: 0, y: 0, width: 400, height: 400)) textView = NSTextView(frame: scrollView.frame) textView.autoresizingMask = [.width, .height] textView.delegate = self let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.lineSpacing = 40 textView.typingAttributes = [.foregroundColor: NSColor.labelColor, .paragraphStyle: paragraphStyle] scrollView.documentView = textView scrollView.hasVerticalScroller = true view = scrollView } func textView(_ textView: NSTextView, shouldChangeTextIn affectedCharRange: NSRange, replacementString: String?) -> Bool { print("before", selectionFrame.maxY, selectionFrame) return true } func textDidChange(_ notification: Notification) { print("after ", selectionFrame.maxY, selectionFrame) } func textViewDidChangeSelection(_ notification: Notification) { print("select", selectionFrame.maxY, selectionFrame) } var selectionFrame: CGRect { guard let selection = textView.textLayoutManager!.textSelections.first?.textRanges.first else { return .null } var frame = CGRect.null textView.textLayoutManager!.ensureLayout(for: selection) textView.textLayoutManager!.enumerateTextSegments(in: selection, type: .selection, options: [.rangeNotRequired]) { _, rect, _, _ in frame = rect return false } return frame } }
0
1
52
Apr ’25