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

Posts under AppKit tag

188 Posts

Post

Replies

Boosts

Views

Activity

How to dynamically update an existing AVComposition when users add a new custom video clip?
I’m building a macOS video editor that uses AVComposition and AVVideoComposition. Initially, my renderer creates a composition with some default video/audio tracks: @Published var composition: AVComposition? @Published var videoComposition: AVVideoComposition? @Published var playerItem: AVPlayerItem? Then I call a buildComposition() function that inserts all the default video segments. Later in the editing workflow, the user may choose to add their own custom video clip. For this I have a function like: private func handlePickedVideo(_ url: URL) { guard url.startAccessingSecurityScopedResource() else { print("Failed to access security-scoped resource") return } let asset = AVURLAsset(url: url) let videoTracks = asset.tracks(withMediaType: .video) guard let firstVideoTrack = videoTracks.first else { print("No video track found") url.stopAccessingSecurityScopedResource() return } renderer.insertUserVideoTrack(from: asset, track: firstVideoTrack) url.stopAccessingSecurityScopedResource() } What I want to achieve is the same behavior professional video editors provide, after the composition has already been initialized and built, the user should be able to add a new video track and the composition should update live, meaning the preview player should immediately reflect the changes without rebuilding everything from scratch manually. How can I structure my AVComposition / AVMutableComposition and my rendering pipeline so that adding a new clip later updates the existing composition in real time (similar to Final Cut/Adobe Premiere), instead of needing to rebuild everything from zero? You can find a playable version of this entire setup at :- https://github.com/zaidbren/SimpleEditor
0
0
389
Nov ’25
Fetching Tinted Folder Icons in macOS Tahoe using Swift
In macOS Tahoe, users can tint folders or add symbols. But when trying to access that customized icon in Swift, the system always returns the default folder icon. NSWorkspace.shared.icon(forFile: url.path) try url.resourceValues(forKeys: [.effectiveIconKey]).effectiveIcon try url.resourceValues(forKeys: [.customIconKey]).customIconKey All of these give back the standard folder icon without any of the user-applied customization. So the question is: Is there any API or workaround in Swift to retrieve the actual customized folder icon (including tint and symbol) as displayed in Finder on macOS Tahoe?
1
0
182
Nov ’25
Unable to Display NSProgressIndicator on macOS Unlock (First Lock) Screen
I'm Trying to add an NSProgressIndicator on the unlock (first lock screen ) in macOS ( the screen with the lock icon ) I already added a label and it works fine and after entering the password on the second (authentication) page I can able to add ProgressIndicator but not on first screen. But Whenever I try to add a Progress indicator, the entire screen turns Black and nothing is displayed Is NSProgressIndicator supported on the first unlock Screen ? Or does macOS block animated UI on this screen Any Guidance would be helpful Thanks
1
0
164
Nov ’25
How to check if a sandboxed app already has the access permission to a URL
I want to check whether a sandboxed application already has access permission to a specific URL. Based on my investigation, the following FileManager method seems to be able to determine it: FileManager.default.isReadableFile(atPath: fileURL.path) However, the method name and description don't explicitly mention this use case, so I'm not confident there aren't any oversights. Also, since this method takes a String path rather than a URL, I'd like to know if there's a more modern API available. I want to use this information to decide whether to prompt the user about the Sandbox restriction in my AppKit-based app.
5
0
450
Nov ’25
NSCollectionLayoutBoundarySupplementaryItem background blur covering the entire layout section
My app has the following UI layout: NSSplitViewController as the windows contentViewController NSPageController in the content (right) split item NSTabViewController as the root items of the NSPageController NSViewController with a collection view in the first tab of that NSTabViewController The collection view is using a NSCollectionViewCompositionalLayout in which the sections are set up to have a header using NSCollectionLayoutBoundarySupplementaryItem with pinToVisibleBounds=true and alignment=top With macOS 26, the pinned supplementary item automatically gets a blurred/semi-transparent background that seamlessly integrates with the toolbar. When the window's title bar has a NSTitlebarAccessoryViewController added, the said semi-transparent background gets a bottom hard edge and a hairline to provide more visual separation from the main content. During runtime, my NSPageController transitions from the NSTabViewController to another view controller. When transitioning back, the semi-transparent blur bleeds into the entire section. This happens no matter if there's a NSTitlebarAccessoryViewController added or not. It doesn't happen 100% of the cases, it seems to depend on section size, header visibility and/or scroll position. But it happens more often than not. Most of the time, a second or so after the back transition - shortly after pageControllerDidEndLiveTransition: of the NSPageControllerDelegate is called - the view updates and the supplementary views are back to normal. Sometimes, the issue also appears not when transitioning using NSPageController, but simply by scrolling through the collection view. Anyone has an idea what is happening here? Below are two screenshots of both the "ok" and "not ok" state I'm on macOS 26.0.1 and I'm using XCode 26.0.1
Topic: UI Frameworks SubTopic: AppKit Tags:
8
0
423
Nov ’25
NSScrollView scrolling hitch
When scrolling a basic NSScrollView there seems to be a sudden jump after each flick. Scrolling does not appear smooth and is disorientating. A scroll jump seems to happen directly after letting go of a scroll flick using a trackpad/mouse. Right at that moment the scroll turns into a momentum scroll, slowly decreasing the speed. But the first frame after the gesture the content jumps forward, more than what is expected. Observations: Counterintuitively, scrolling appears to be smoother when disabling NSScrollView.isCompatibleWithResponsiveScrolling. If disabled using a custom NSScrollView subclass there is no large jump anymore. Scrolling also appears to be smoother using a SwiftUI ScrollView. I assume that has the same behaviour as a disabled isCompatibleWithResponsiveScrolling Ironically a WKWebView scrolls much smoother. No sudden jump is observable. It also seems to scroll with faster acceleration, but the individual frames do appear smoother. Why is this better than a native NSScrollView? Elastic scrolling at the bounds of the scroll view also appears much smoother for WKWebViews. When pulling to refresh there is a jump for NSScrollView/SwiftUI, but not for WKWebView. When using an NSScrollView with isCompatibleWithResponsiveScrolling disabled, scrolling appears just as smooth as WKWebView on macOS 13 Ventura and below. On macOS 14 Sonoma scrolling behaviour is suddenly different. Please see a sample project with 4 different scroll views side by side: https://github.com/floorish/ScrollTest Screen recordings show the sudden jumps when scrolling and when elastic scrolling. Tested on Intel & Arm Macs, macOS 11 Big Sur through 15 Sequoia, built with Xcode 16. Should isCompatibleWithResponsiveScrolling be disabled on Sonoma+? Are there any drawbacks? There is also no overdraw anymore since Monterey, as described in https://developer.apple.com/library/archive/releasenotes/AppKit/RN-AppKitOlderNotes/#10_9Scrolling Even with responsive scrolling disabled, why is WKWebView scrolling much smoother than NSScrollView?
Topic: UI Frameworks SubTopic: AppKit Tags:
8
2
853
Nov ’25
NSRulerView's background color and transparency (macOS 26)
When I compiled my legacy project with Tahoe's macOS 26 SDK, NSRulerViews are showing a very different design: Under prior macOS versions the horizontal and verrical ruler's background were blurring the content view, which was extending under the rulers, showing through their transparency. With Tahoe the horizontal ruler is always reflecting the scrollview's background color, showing the blurred content view beneath. And the vertical ruler is always completely transparent (without any blurring), showing the content together with the ruler's markers and ticks. It's difficult to describe, I'll try to replicate this behavior with a minimal test project, and probably file a bug report / enhancement request. But before I take next steps, can anyone confirm this observation? Maybe it is an intentional design decision by Apple?
6
0
441
Nov ’25
NSButton + TtGC6AppKit18_NSCoreHostingViewVS_12AppKitButton - Image Alignment Changed/Broken in Minor macOS 26.1 Update
I just updated to macOS 26.1. I have a pure AppKit app (I guess that's not possible anymore but as close to a pure AppKit app as you can get). I use NSButton with the glass bezel style and SF symbol images. It looks like the minor OS update brought layout changes because now some of these buttons are scaling the symbol image much larger than was being done on macOS 26. The image can sometimes draw outside the glass 'bezel'. It looks like using the 'info' symbol in a button results in much larger image scaling than it did on the previous Tahoe for some SF Symbols. With the glass bezel style and a SF Symbol image how am I supposed to consistently make the button look good? With certain symbols I have to use imageScaling NSImageScaleProportionallyUpOrDown and on others I have to use NSImageScaleProportionallyDown. If I'm using a system image shouldn't it just do the right thing? Is trial and error the only way to tell? That's what I was doing before but it seems that the minor 26.1 update changed things. Additionally calling -sizeToFit on a button multiple times can cause it to shrink for example: [button sizeToFit]; // <-- At fitting size // Then later [button sizeToFit]; // Now button is shrunk But if the button is already at its fitting size an additional call later shouldn't make it shrink, it should stay the same size. FB20517174 I see I now inherit SwiftUI, not sure if that has anything to do with this but if I wanted to opt in to fragile layout I wouldn't be using Appkit...
1
0
119
Nov ’25
NSScrollView two finger drag being interrupted
I have a fairly robust MacOS application that has an NSScrollView that contains a canvas with various subviews (including web views and text views that contain scroll views), and a couple of peer views that track items in the scroll view (eg: screen space controls). Some of these views interrupt two finger scrolling. Every scroll view, and one of the peer views (essentially a stack view with buttons in it). I have written an additional bare bones application which does roughly the same thing, and my bare bones application works perfectly: Start two-finger dragging, scroll any of these other things under the cursor, I can continue to drag (and start dragging in any of those, and they drag without interfering with the parent scroll view). I have tried everything to recreate the interruption, including drag gestures attached to these various ancillary views, and I cannot figure out why dragging some of these views under the cursor interrupts two finger drag in our application, but not in my testbed. Does anyone have suggestions for how to debug this? I can see that there is a gesture recognizer in the NSScrollView hierarchy, but I don't see it in any of my gesture recognizer handling. I have breakpoints on every variation of hit testing and mouse motion, and none of them are getting hit in unexpected ways. I'm at my wit's end. Thanks.
Topic: UI Frameworks SubTopic: AppKit Tags:
3
1
738
Nov ’25
VZVirtualMachineView in SwiftUI App
I have a working XIB App to run a Linux VM with graphics interface. I am trying to rewrite it in SwiftUI but fall into all sorts of problems when using a combination of a Representable of the VZVirtualMachineView, an associated Coordinator, and @StateObjects. a) The VM display is not updated when running but is displayed if I close the window and reopen it. As the underlying VZVirtualMachineView is created/dismantled many times, there are warnings about negative scanouts that end up crashing the App b) Keyboard focus is not really working. https://developer.apple.com/forums/thread/766014 reports that there is probably a solution making a NSViewControllerReporesentable rather than a VZVirtualMachineViewRepresentable. I think I am fighting against proper SwiftUI lifecycle and would love to have a hint at what shall be the right organization of model and SwiftUI constructs.
3
0
155
Nov ’25
Subclassing NSMenuItem gives error in Xcode 26.1
In Xcode 26.1, for a project with “Swift Language Version” set to swift6 I am getting for the following code I get errors: Error1: Main actor-isolated initializer 'init(title:action:keyEquivalent:)' has different actor isolation from nonisolated overridden declaration Error2: Main actor-isolated initializer 'init(coder:)' has different actor isolation from nonisolated overridden declaration @MainActor class MenuItem: NSMenuItem { // error1 var userInfo: [String : Any] = [:] init(label: String, action: Selector?, target: AnyObject?, userInfo: [String : Any]) { self.userInfo = userInfo super.init(title: label, action: action, keyEquivalent: "") } required init(coder decoder: NSCoder) { // error2 super.init(coder: decoder) } }
1
0
113
Nov ’25
Resizing text to fit available space
My app displays some text that should appear the same regardless of the container view or window size, i.e. it should grow and shrink with the container view or window. On iOS there is UILabel.adjustsFontSizeToFitWidth but I couldn't find any equivalent API on macOS. On the internet some people suggest to iteratively set a smaller font size until the text fits the available space, but I thought there must be a more efficient solution. How does UILabel.adjustsFontSizeToFitWidth do it? My expectation was that setting a font's size to a fraction of the window width or height would do the trick, but when resizing the window I can see a slightly different portion of it. class ViewController: NSViewController { override func loadView() { view = MyView(frame: CGRect(x: 0, y: 0, width: 400, height: 400)) NSLayoutConstraint.activate([view.widthAnchor.constraint(equalTo: view.heightAnchor, multiplier: 3), view.heightAnchor.constraint(greaterThanOrEqualToConstant: 100)]) } } class MyView: NSView { let textField = NSTextField(labelWithString: String(repeating: "a b c d e f g h i j k l m n o p q r s t u v w x y z ", count: 2)) override init(frame frameRect: NSRect) { super.init(frame: frameRect) textField.translatesAutoresizingMaskIntoConstraints = false textField.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) addSubview(textField) NSLayoutConstraint.activate([textField.topAnchor.constraint(equalTo: topAnchor), textField.leadingAnchor.constraint(equalTo: leadingAnchor), textField.trailingAnchor.constraint(equalTo: trailingAnchor)]) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func resize(withOldSuperviewSize oldSize: NSSize) { // textField.font = .systemFont(ofSize: frame.width * 0.05) textField.font = .systemFont(ofSize: frame.height * 0.1) } }
Topic: UI Frameworks SubTopic: AppKit Tags:
13
0
496
Nov ’25
NSOutlineView incorrectly draws disclosure indicator when item views are SwiftUI views.
I am using an NSOutlineView via NSViewRepresentable in a SwiftUI application running on macOS. Everything has been working fine. Up until lately, I've been returning a custom NSView for each item using the standard: func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? { // View recycling omitted. return MyItemView(item) } Now I want to explore using a little bit more SwiftUI and returning an NSHostingView from this delegate method. func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? { // View recycling omitted. let rootView = MySwiftUIView(item) let hostingView = NSHostingView(rootView: rootView) return hostingView } For the most part, this appears to be working fine. NSOutlineView is even correctly applying highlight styling, so that's great. But there's one small glitch. The outline view's disclosure triangles do not align with the hosting view's content. The disclosure triangles appear to just be pinned to the top. Perhaps they can't find a baseline constraint or something? Is there any SwiftUI modifier or AppKit/SwiftUI technique I can apply here to get the disclosure button to appear in the right place? Here is what the SwiftUI + NSHostingView version looks like: Note the offset disclosure indicators. (Image spacing is a bit off as well using Label, but fixable. Here is what an NSView with NSTextFields looks like: Disclosure indicators are correctly aligned, as you would expect.
1
0
156
Nov ’25
AVAssetExportSession ignores frameDuration 60fps and exports at 30fps, but AVPlayer playback is correct
Hey everyone, I'm stuck on a really frustrating AVFoundation problem. I'm building a video editor that uses a custom AVVideoCompositor to add effects, and I need the final output to be 60 FPS. So basically, I create an AVMutableComposition to sequence my video clips. I create an AVMutableVideoComposition and set the frame rate to 60 FPS: videoComposition.frameDuration = CMTime(value: 1, timescale: 60) I assign my CustomVideoCompositor class to the videoComposition. I create an AVPlayerItem with the composition and video composition. The Problem: Playback Works: When I play the AVPlayerItem in an AVPlayer, it's perfect. It plays at a smooth 60 FPS, and my custom compositor's startRequest method is called 60 times per second. Export Fails: When I try to export the exact same composition and video composition using AVAssetExportSession, the final .mp4 file is always 30 FPS (or 29.97). I've logged inside my custom compositor during the export, and it's definitely being called 30 times per second, so it's generating the 30 frames. It seems like AVAssetExportSession is just dropping every other frame when it encodes the video. My source videos are screen recordings which I recorded using ScreenCaptureKit itself with the minimum frame interval to be 60. Here is my export function. I'm using the AVAssetExportPresetHighestQuality preset :- func exportVideo(to outputURL: URL) async throws { guard let composition = composition, let videoComposition = videoComposition else { throw VideoCompositionError.noValidVideos } try? FileManager.default.removeItem(at: outputURL) guard let exportSession = AVAssetExportSession( asset: composition, presetName: AVAssetExportPresetHighestQuality // Is this the problem? ) else { throw VideoCompositionError.trackCreationFailed } exportSession.outputFileType = .mp4 exportSession.videoComposition = videoComposition // This has the 60fps setting try await exportSession.export(to: outputURL, as: .mp4) } I've created a bare bones sample project that shows this exact bug in action. The resulting video is 60fps during playback, but only 30fps during the export. https://github.com/zaidbren/SimpleEditor My Question: Why is AVAssetExportSession ignoring my 60 FPS frameDuration and defaulting to 30 FPS, even though AVPlayer respects it?
1
0
471
Oct ’25
Prevent default file selector in a SwiftUI DocumentGroup app and show a custom welcome window on launch
I’m building a macOS document based app using SwiftUI’s DocumentGroup API. By default, when a document based app launches, macOS automatically shows a file open panel or creates a new untitled document window. However, I want to suppress this default behavior and instead show a custom welcome window when the app starts — something similar to how Xcode or Final Cut Pro shows a “Welcome” or “Start Project” screen first. So basically, when the user opens the app normally, it should not open the document selector or create a document automatically. Instead, it should show my custom SwiftUI or AppKit window. Here is my Code :- //MyApp.swift import SwiftUI import AppKit @main struct PhiaApp: App { @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate var body: some Scene { DocumentGroup(newDocument: MyDocumentModel()) { file in EditorView(document: file.document, filePath: file.fileURL) } Settings { EmptyView() } } } Current I have this code setup for my MainApp.swift, where I am using the AppDelegate to create a custom recording window using appkit and also defining the DocumentGroup to handle the custom .myapp file opens. However, when I launch the app, its showing my appkit window as well as the macOs native file Selector to select the file I want to open. I want when the user opens the app normally, it should not open the document selector or create a document automatically. Instead, it should show my custom SwiftUI or AppKit window. However, the app should still fully support opening .myapp documents by double clicking from Finder, using the standard File → Open and File → New menu options, also having multiple document windows open at once. This is my AppDelegate.swift file :- import AppKit import SwiftUI class AppDelegate: NSObject, NSApplicationDelegate { var panel: Panel? private var statusItem: NSStatusItem? func applicationDidFinishLaunching(_ notification: Notification) { showWindow() } // MARK: - Window control func showWindow() { if panel == nil { let root = RecordingViewMain() let newPanel = Panel(rootView: root) if let screen = NSScreen.main { let size = NSSize(width: 360, height: 240) let origin = NSPoint( x: screen.visibleFrame.midX - size.width / 2, y: screen.visibleFrame.midY - size.height / 2 ) newPanel.setFrame(NSRect(origin: origin, size: size), display: true) } panel = newPanel } panel?.makeKeyAndOrderFront(nil) } func hideWindow() { panel?.orderOut(nil) } @objc private func showPanelAction() { showWindow() } @objc private func quitAction() { NSApp.terminate(nil) } }
2
0
269
Oct ’25
Value of type 'SCRecordingOutput' has no member 'delegate'
Hello, I am trying to capture screen recording ( output.mp4 ) using ScreenCaptureKit and also the mouse positions during the recording ( mouse.json ). The recording and the mouse positions ( tracked based on mouse movements events only ) needs to be perfectly synced in order to add effects in post editing. I started off by using the await stream?.startCapture() and after that starting my mouse tracking function :- try await captureEngine.startCapture(configuration: config, filter: filter, recordingOutput: recordingOutput) let captureStartTime = Date() mouseTracker?.startTracking(with: captureStartTime) But every time I tested, there is a clear inconsistency in sync between the recorded video and the recorded mouse positions. The only thing I want is to know when exactly does the recording "actually" started so that I can start the mouse capture at that same time, and thus I tried using the Delegates, but being able to set them up perfectly. import Foundation import AVFAudio import ScreenCaptureKit import OSLog import Combine class CaptureEngine: NSObject, @unchecked Sendable { private let logger = Logger() private(set) var stream: SCStream? private var streamOutput: CaptureEngineStreamOutput? private var recordingOutput: SCRecordingOutput? private let videoSampleBufferQueue = DispatchQueue(label: "com.francestudio.phia.VideoSampleBufferQueue") private let audioSampleBufferQueue = DispatchQueue(label: "com.francestudio.phia.AudioSampleBufferQueue") private let micSampleBufferQueue = DispatchQueue(label: "com.francestudio.phia.MicSampleBufferQueue") func startCapture(configuration: SCStreamConfiguration, filter: SCContentFilter, recordingOutput: SCRecordingOutput) async throws { // Create the stream output delegate. let streamOutput = CaptureEngineStreamOutput() self.streamOutput = streamOutput do { stream = SCStream(filter: filter, configuration: configuration, delegate: streamOutput) try stream?.addStreamOutput(streamOutput, type: .screen, sampleHandlerQueue: videoSampleBufferQueue) try stream?.addStreamOutput(streamOutput, type: .audio, sampleHandlerQueue: audioSampleBufferQueue) try stream?.addStreamOutput(streamOutput, type: .microphone, sampleHandlerQueue: micSampleBufferQueue) self.recordingOutput = recordingOutput recordingOutput.delegate = self try stream?.addRecordingOutput(recordingOutput) try await stream?.startCapture() } catch { logger.error("Failed to start capture: \(error.localizedDescription)") throw error } } func stopCapture() async throws { do { try await stream?.stopCapture() } catch { logger.error("Failed to stop capture: \(error.localizedDescription)") throw error } } func update(configuration: SCStreamConfiguration, filter: SCContentFilter) async { do { try await stream?.updateConfiguration(configuration) try await stream?.updateContentFilter(filter) } catch { logger.error("Failed to update the stream session: \(String(describing: error))") } } func stopRecordingOutputForStream(_ recordingOutput: SCRecordingOutput) throws { try self.stream?.removeRecordingOutput(recordingOutput) } } // MARK: - SCRecordingOutputDelegate extension CaptureEngine: SCRecordingOutputDelegate { func recordingOutputDidStartRecording(_ recordingOutput: SCRecordingOutput) { let startTime = Date() logger.info("Recording output did start recording \(startTime)") } func recordingOutputDidFinishRecording(_ recordingOutput: SCRecordingOutput) { logger.info("Recording output did finish recording") } func recordingOutput(_ recordingOutput: SCRecordingOutput, didFailWithError error: any Error) { logger.error("Recording output failed with error: \(error.localizedDescription)") } } private class CaptureEngineStreamOutput: NSObject, SCStreamOutput, SCStreamDelegate { private let logger = Logger() override init() { super.init() } func stream(_ stream: SCStream, didOutputSampleBuffer sampleBuffer: CMSampleBuffer, of outputType: SCStreamOutputType) { guard sampleBuffer.isValid else { return } switch outputType { case .screen: break case .audio: break case .microphone: break @unknown default: logger.error("Encountered unknown stream output type:") } } func stream(_ stream: SCStream, didStopWithError error: Error) { logger.error("Stream stopped with error: \(error.localizedDescription)") } } I am getting error Value of type 'SCRecordingOutput' has no member 'delegate' Even though I am targeting macOs 15+ ( macOs 26 actually ) and macOs only. What is the best way to achieving the desired result? Is there any other / better way to do it?
1
0
328
Oct ’25
NSScrollView only scrolls vertically for NSTableView after window resize
Hi everyone, I’m looking for help with an issue where using insertRows to add a row to an NSTableView requires resizing the window before I can scroll to the newly added rows that extend beyond the visible area of the NSScrollView. import Cocoa class ViewController: NSViewController { var data = Constants.movies // Table components var titleField = NSTextField() var directorField = NSTextField() var releaseYearField = NSTextField() var addMovieButton = NSButton(title: "Add Movie", image: NSImage(systemSymbolName: "plus", accessibilityDescription: "")!, target: nil, action: #selector(addMovie)) var table = NSTableView() var titleColumn = NSTableColumn(identifier: NSUserInterfaceItemIdentifier(Constants.titleColumnId)) var directorColumn = NSTableColumn(identifier: NSUserInterfaceItemIdentifier(Constants.directorColumnId)) var releaseYearColumn = NSTableColumn(identifier: NSUserInterfaceItemIdentifier(Constants.releaseColumnId)) // Scroll view let scrollView = NSScrollView() override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. view.wantsLayer = true view.layer?.backgroundColor = NSColor.black.cgColor configure() } private func configure() { titleField.translatesAutoresizingMaskIntoConstraints = false directorField.translatesAutoresizingMaskIntoConstraints = false releaseYearField.translatesAutoresizingMaskIntoConstraints = false addMovieButton.translatesAutoresizingMaskIntoConstraints = false scrollView.translatesAutoresizingMaskIntoConstraints = false table.translatesAutoresizingMaskIntoConstraints = false titleField.placeholderString = "Movie Title" directorField.placeholderString = "Name of Director" releaseYearField.placeholderString = "Year Released" // Configure table table.addTableColumn(titleColumn) table.addTableColumn(directorColumn) table.addTableColumn(releaseYearColumn) titleColumn.title = "Movie Title" directorColumn.title = "Director" releaseYearColumn.title = "Year Released" table.delegate = self table.dataSource = self table.focusRingType = .none // Configure scroll view scrollView.documentView = table scrollView.hasVerticalScroller = true scrollView.hasHorizontalScroller = false scrollView.autohidesScrollers = true // Add views to the hierarchy view.addSubview(titleField) view.addSubview(directorField) view.addSubview(releaseYearField) view.addSubview(addMovieButton) view.addSubview(scrollView) // view.addSubview(table) NSLayoutConstraint.activate([ titleField.widthAnchor.constraint(equalToConstant: 150), directorField.widthAnchor.constraint(equalToConstant: 150), releaseYearField.widthAnchor.constraint(equalToConstant: 150), addMovieButton.widthAnchor.constraint(equalToConstant: 100), titleField.topAnchor.constraint(equalTo: view.topAnchor, constant: 20), directorField.topAnchor.constraint(equalTo: view.topAnchor, constant: 20), releaseYearField.topAnchor.constraint(equalTo: view.topAnchor, constant: 20), addMovieButton.topAnchor.constraint(equalTo: view.topAnchor, constant: 20), directorField.trailingAnchor.constraint(equalTo: view.centerXAnchor, constant: -10), releaseYearField.leadingAnchor.constraint(equalTo: view.centerXAnchor, constant: 10), titleField.trailingAnchor.constraint(equalTo: directorField.leadingAnchor, constant: -20), addMovieButton.leadingAnchor.constraint(equalTo: releaseYearField.trailingAnchor, constant: 20), scrollView.topAnchor.constraint(equalTo: titleField.bottomAnchor, constant: 20), scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20), scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20), scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -20), // table.widthAnchor.constraint(equalTo: scrollView.contentView.widthAnchor), // table.heightAnchor.constraint(equalTo: scrollView.contentView.heightAnchor) ]) } @objc private func addMovie() { let title = titleField.stringValue let director = directorField.stringValue guard let year = Int(releaseYearField.stringValue) else { return } let movie = Movie(title: title, director: director, releaseYear: year) data.append(movie) // Approach 1 // table.reloadData() // Approach 2 let newRow = data.count - 1 table.insertRows(at: IndexSet(integer: newRow), withAnimation: .slideDown) } Using table.reloadData works fine and I can scroll to the new rows as I add them, but it doesn't have the smooth animation I want to achieve. What is the best practice and correct way for using table.insertRows to avoid this issue? Note: I am using programmatic constraints only.
1
0
236
Oct ’25
SwiftUI buttons behind NSToolbarView are not clickable on macOS 26 beta
Overview Starting with macOS 26 beta 1, a new NSGlassContainerView is added inside NSToolbarView. This view intercepts mouse events, so any SwiftUI Button (or other interactive view) overlaid on the title‑bar / toolbar area no longer receives clicks. (The same code works fine on macOS 15 and earlier.) Filed as FB18201935 via Feedback Assistant. Reproduction (minimal project) macOS 15 or earlier → button is clickable macOS 26 beta → button cannot be clicked (no highlight, no action call) @main struct Test_macOS26App: App { init() { // Uncomment to work around the issue (see next section) // enableToolbarClickThrough() } var body: some Scene { WindowGroup { ContentView() } .windowStyle(.hiddenTitleBar) // ⭐️ hide the title bar } } struct ContentView: View { var body: some View { NavigationSplitView { List { Text("sidebar") } } detail: { HSplitView { listWithOverlay listWithOverlay } } } private var listWithOverlay: some View { List(0..<30) { Text("item: \($0)") } .overlay(alignment: .topTrailing) { // ⭐️ overlay in the toolbar area Button("test") { print("test") } .glassEffect() .ignoresSafeArea() } } } Investigation In Xcode View Hierarchy Debugger, a layer chain NSToolbarView > NSGlassContainerView sits in front of the button. -[NSView hitTest:] on NSGlassContainerView returns itself, so the event never reaches the SwiftUI layer. Swizzling hitTest: to return nil when the result is the view itself makes the click go through: func enableToolbarClickThrough() { guard let cls = NSClassFromString("NSGlassContainerView"), let m = class_getInstanceMethod(cls, #selector(NSView.hitTest(_:))) else { return } typealias Fn = @convention(c)(AnyObject, Selector, NSPoint) -> Unmanaged<NSView>? let origIMP = unsafeBitCast(method_getImplementation(m), to: Fn.self) let block: @convention(block)(AnyObject, NSPoint) -> NSView? = { obj, pt in guard let v = origIMP(obj, #selector(NSView.hitTest(_:)), pt)?.takeUnretainedValue() else { return nil } return v === (obj as AnyObject) ? nil : v // ★ make the container transparent } method_setImplementation(m, imp_implementationWithBlock(block)) } Questions / Call for Feedback Is this an intentional behavioral change? If so, what is the recommended public API or pattern for allowing clicks to reach views overlaid behind the toolbar? Any additional data points or confirmations are welcome—please reply if you can reproduce the issue or know of an official workaround. Thanks in advance!
3
0
319
Oct ’25
What is the standard process of subclassing NSTextLocation?
Cannot find any guidance in the forums and Developer Doc, the WWDC session Meet TextKit2 says this protocol is served for any location type espacially useful when the underlying doc model is not linear. But when I try to subclass the NSTextLocation with my own type to define a more structured location rather than a linear one, also with my own NSTextContentManager subclass implementation, I keep receiving the system internal Location model like NSCountableTextLocation compare to my location which cause the app to crash. -[NSCountableTextLocation compare:] receiving unmatching type <MarkdownTK2.ChildIndexPathLocation: 0x9b2402aa0> In my own NSTextContentManager subclass: public override func offset( from: any NSTextLocation, to: any NSTextLocation ) -> Int { this method will also both receive my own location and some times receiving the system defined location that I can not calculate the offset then just return zero. The doc only says If you provide your own implementation of the NSTextLocation protocol to manage locations in your content, subclass NSTextContentManager and implement your own storage object to support those locations. OS Development environment: Xcode 26.0, macOS 26.0 Run-time configuration: macOS 26.0
3
0
355
Oct ’25
How to dynamically update an existing AVComposition when users add a new custom video clip?
I’m building a macOS video editor that uses AVComposition and AVVideoComposition. Initially, my renderer creates a composition with some default video/audio tracks: @Published var composition: AVComposition? @Published var videoComposition: AVVideoComposition? @Published var playerItem: AVPlayerItem? Then I call a buildComposition() function that inserts all the default video segments. Later in the editing workflow, the user may choose to add their own custom video clip. For this I have a function like: private func handlePickedVideo(_ url: URL) { guard url.startAccessingSecurityScopedResource() else { print("Failed to access security-scoped resource") return } let asset = AVURLAsset(url: url) let videoTracks = asset.tracks(withMediaType: .video) guard let firstVideoTrack = videoTracks.first else { print("No video track found") url.stopAccessingSecurityScopedResource() return } renderer.insertUserVideoTrack(from: asset, track: firstVideoTrack) url.stopAccessingSecurityScopedResource() } What I want to achieve is the same behavior professional video editors provide, after the composition has already been initialized and built, the user should be able to add a new video track and the composition should update live, meaning the preview player should immediately reflect the changes without rebuilding everything from scratch manually. How can I structure my AVComposition / AVMutableComposition and my rendering pipeline so that adding a new clip later updates the existing composition in real time (similar to Final Cut/Adobe Premiere), instead of needing to rebuild everything from zero? You can find a playable version of this entire setup at :- https://github.com/zaidbren/SimpleEditor
Replies
0
Boosts
0
Views
389
Activity
Nov ’25
Fetching Tinted Folder Icons in macOS Tahoe using Swift
In macOS Tahoe, users can tint folders or add symbols. But when trying to access that customized icon in Swift, the system always returns the default folder icon. NSWorkspace.shared.icon(forFile: url.path) try url.resourceValues(forKeys: [.effectiveIconKey]).effectiveIcon try url.resourceValues(forKeys: [.customIconKey]).customIconKey All of these give back the standard folder icon without any of the user-applied customization. So the question is: Is there any API or workaround in Swift to retrieve the actual customized folder icon (including tint and symbol) as displayed in Finder on macOS Tahoe?
Replies
1
Boosts
0
Views
182
Activity
Nov ’25
Unable to Display NSProgressIndicator on macOS Unlock (First Lock) Screen
I'm Trying to add an NSProgressIndicator on the unlock (first lock screen ) in macOS ( the screen with the lock icon ) I already added a label and it works fine and after entering the password on the second (authentication) page I can able to add ProgressIndicator but not on first screen. But Whenever I try to add a Progress indicator, the entire screen turns Black and nothing is displayed Is NSProgressIndicator supported on the first unlock Screen ? Or does macOS block animated UI on this screen Any Guidance would be helpful Thanks
Replies
1
Boosts
0
Views
164
Activity
Nov ’25
How to check if a sandboxed app already has the access permission to a URL
I want to check whether a sandboxed application already has access permission to a specific URL. Based on my investigation, the following FileManager method seems to be able to determine it: FileManager.default.isReadableFile(atPath: fileURL.path) However, the method name and description don't explicitly mention this use case, so I'm not confident there aren't any oversights. Also, since this method takes a String path rather than a URL, I'd like to know if there's a more modern API available. I want to use this information to decide whether to prompt the user about the Sandbox restriction in my AppKit-based app.
Replies
5
Boosts
0
Views
450
Activity
Nov ’25
NSCollectionLayoutBoundarySupplementaryItem background blur covering the entire layout section
My app has the following UI layout: NSSplitViewController as the windows contentViewController NSPageController in the content (right) split item NSTabViewController as the root items of the NSPageController NSViewController with a collection view in the first tab of that NSTabViewController The collection view is using a NSCollectionViewCompositionalLayout in which the sections are set up to have a header using NSCollectionLayoutBoundarySupplementaryItem with pinToVisibleBounds=true and alignment=top With macOS 26, the pinned supplementary item automatically gets a blurred/semi-transparent background that seamlessly integrates with the toolbar. When the window's title bar has a NSTitlebarAccessoryViewController added, the said semi-transparent background gets a bottom hard edge and a hairline to provide more visual separation from the main content. During runtime, my NSPageController transitions from the NSTabViewController to another view controller. When transitioning back, the semi-transparent blur bleeds into the entire section. This happens no matter if there's a NSTitlebarAccessoryViewController added or not. It doesn't happen 100% of the cases, it seems to depend on section size, header visibility and/or scroll position. But it happens more often than not. Most of the time, a second or so after the back transition - shortly after pageControllerDidEndLiveTransition: of the NSPageControllerDelegate is called - the view updates and the supplementary views are back to normal. Sometimes, the issue also appears not when transitioning using NSPageController, but simply by scrolling through the collection view. Anyone has an idea what is happening here? Below are two screenshots of both the "ok" and "not ok" state I'm on macOS 26.0.1 and I'm using XCode 26.0.1
Topic: UI Frameworks SubTopic: AppKit Tags:
Replies
8
Boosts
0
Views
423
Activity
Nov ’25
NSScrollView scrolling hitch
When scrolling a basic NSScrollView there seems to be a sudden jump after each flick. Scrolling does not appear smooth and is disorientating. A scroll jump seems to happen directly after letting go of a scroll flick using a trackpad/mouse. Right at that moment the scroll turns into a momentum scroll, slowly decreasing the speed. But the first frame after the gesture the content jumps forward, more than what is expected. Observations: Counterintuitively, scrolling appears to be smoother when disabling NSScrollView.isCompatibleWithResponsiveScrolling. If disabled using a custom NSScrollView subclass there is no large jump anymore. Scrolling also appears to be smoother using a SwiftUI ScrollView. I assume that has the same behaviour as a disabled isCompatibleWithResponsiveScrolling Ironically a WKWebView scrolls much smoother. No sudden jump is observable. It also seems to scroll with faster acceleration, but the individual frames do appear smoother. Why is this better than a native NSScrollView? Elastic scrolling at the bounds of the scroll view also appears much smoother for WKWebViews. When pulling to refresh there is a jump for NSScrollView/SwiftUI, but not for WKWebView. When using an NSScrollView with isCompatibleWithResponsiveScrolling disabled, scrolling appears just as smooth as WKWebView on macOS 13 Ventura and below. On macOS 14 Sonoma scrolling behaviour is suddenly different. Please see a sample project with 4 different scroll views side by side: https://github.com/floorish/ScrollTest Screen recordings show the sudden jumps when scrolling and when elastic scrolling. Tested on Intel & Arm Macs, macOS 11 Big Sur through 15 Sequoia, built with Xcode 16. Should isCompatibleWithResponsiveScrolling be disabled on Sonoma+? Are there any drawbacks? There is also no overdraw anymore since Monterey, as described in https://developer.apple.com/library/archive/releasenotes/AppKit/RN-AppKitOlderNotes/#10_9Scrolling Even with responsive scrolling disabled, why is WKWebView scrolling much smoother than NSScrollView?
Topic: UI Frameworks SubTopic: AppKit Tags:
Replies
8
Boosts
2
Views
853
Activity
Nov ’25
NSRulerView's background color and transparency (macOS 26)
When I compiled my legacy project with Tahoe's macOS 26 SDK, NSRulerViews are showing a very different design: Under prior macOS versions the horizontal and verrical ruler's background were blurring the content view, which was extending under the rulers, showing through their transparency. With Tahoe the horizontal ruler is always reflecting the scrollview's background color, showing the blurred content view beneath. And the vertical ruler is always completely transparent (without any blurring), showing the content together with the ruler's markers and ticks. It's difficult to describe, I'll try to replicate this behavior with a minimal test project, and probably file a bug report / enhancement request. But before I take next steps, can anyone confirm this observation? Maybe it is an intentional design decision by Apple?
Replies
6
Boosts
0
Views
441
Activity
Nov ’25
NSButton + TtGC6AppKit18_NSCoreHostingViewVS_12AppKitButton - Image Alignment Changed/Broken in Minor macOS 26.1 Update
I just updated to macOS 26.1. I have a pure AppKit app (I guess that's not possible anymore but as close to a pure AppKit app as you can get). I use NSButton with the glass bezel style and SF symbol images. It looks like the minor OS update brought layout changes because now some of these buttons are scaling the symbol image much larger than was being done on macOS 26. The image can sometimes draw outside the glass 'bezel'. It looks like using the 'info' symbol in a button results in much larger image scaling than it did on the previous Tahoe for some SF Symbols. With the glass bezel style and a SF Symbol image how am I supposed to consistently make the button look good? With certain symbols I have to use imageScaling NSImageScaleProportionallyUpOrDown and on others I have to use NSImageScaleProportionallyDown. If I'm using a system image shouldn't it just do the right thing? Is trial and error the only way to tell? That's what I was doing before but it seems that the minor 26.1 update changed things. Additionally calling -sizeToFit on a button multiple times can cause it to shrink for example: [button sizeToFit]; // <-- At fitting size // Then later [button sizeToFit]; // Now button is shrunk But if the button is already at its fitting size an additional call later shouldn't make it shrink, it should stay the same size. FB20517174 I see I now inherit SwiftUI, not sure if that has anything to do with this but if I wanted to opt in to fragile layout I wouldn't be using Appkit...
Replies
1
Boosts
0
Views
119
Activity
Nov ’25
NSScrollView two finger drag being interrupted
I have a fairly robust MacOS application that has an NSScrollView that contains a canvas with various subviews (including web views and text views that contain scroll views), and a couple of peer views that track items in the scroll view (eg: screen space controls). Some of these views interrupt two finger scrolling. Every scroll view, and one of the peer views (essentially a stack view with buttons in it). I have written an additional bare bones application which does roughly the same thing, and my bare bones application works perfectly: Start two-finger dragging, scroll any of these other things under the cursor, I can continue to drag (and start dragging in any of those, and they drag without interfering with the parent scroll view). I have tried everything to recreate the interruption, including drag gestures attached to these various ancillary views, and I cannot figure out why dragging some of these views under the cursor interrupts two finger drag in our application, but not in my testbed. Does anyone have suggestions for how to debug this? I can see that there is a gesture recognizer in the NSScrollView hierarchy, but I don't see it in any of my gesture recognizer handling. I have breakpoints on every variation of hit testing and mouse motion, and none of them are getting hit in unexpected ways. I'm at my wit's end. Thanks.
Topic: UI Frameworks SubTopic: AppKit Tags:
Replies
3
Boosts
1
Views
738
Activity
Nov ’25
VZVirtualMachineView in SwiftUI App
I have a working XIB App to run a Linux VM with graphics interface. I am trying to rewrite it in SwiftUI but fall into all sorts of problems when using a combination of a Representable of the VZVirtualMachineView, an associated Coordinator, and @StateObjects. a) The VM display is not updated when running but is displayed if I close the window and reopen it. As the underlying VZVirtualMachineView is created/dismantled many times, there are warnings about negative scanouts that end up crashing the App b) Keyboard focus is not really working. https://developer.apple.com/forums/thread/766014 reports that there is probably a solution making a NSViewControllerReporesentable rather than a VZVirtualMachineViewRepresentable. I think I am fighting against proper SwiftUI lifecycle and would love to have a hint at what shall be the right organization of model and SwiftUI constructs.
Replies
3
Boosts
0
Views
155
Activity
Nov ’25
Subclassing NSMenuItem gives error in Xcode 26.1
In Xcode 26.1, for a project with “Swift Language Version” set to swift6 I am getting for the following code I get errors: Error1: Main actor-isolated initializer 'init(title:action:keyEquivalent:)' has different actor isolation from nonisolated overridden declaration Error2: Main actor-isolated initializer 'init(coder:)' has different actor isolation from nonisolated overridden declaration @MainActor class MenuItem: NSMenuItem { // error1 var userInfo: [String : Any] = [:] init(label: String, action: Selector?, target: AnyObject?, userInfo: [String : Any]) { self.userInfo = userInfo super.init(title: label, action: action, keyEquivalent: "") } required init(coder decoder: NSCoder) { // error2 super.init(coder: decoder) } }
Replies
1
Boosts
0
Views
113
Activity
Nov ’25
Does SwiftUI for macOS have an equivalent of NSVisualEffectView's blendingMode?
NSVisualEffectView in AppKit has two main properties: material and blendingMode. Material is well supported in SwiftUI, but I can't seem to find an equivalent for blendingMode. What is the SwiftUI equivalent to NSVisualEffect.BlendingMode?
Replies
1
Boosts
1
Views
149
Activity
Nov ’25
Resizing text to fit available space
My app displays some text that should appear the same regardless of the container view or window size, i.e. it should grow and shrink with the container view or window. On iOS there is UILabel.adjustsFontSizeToFitWidth but I couldn't find any equivalent API on macOS. On the internet some people suggest to iteratively set a smaller font size until the text fits the available space, but I thought there must be a more efficient solution. How does UILabel.adjustsFontSizeToFitWidth do it? My expectation was that setting a font's size to a fraction of the window width or height would do the trick, but when resizing the window I can see a slightly different portion of it. class ViewController: NSViewController { override func loadView() { view = MyView(frame: CGRect(x: 0, y: 0, width: 400, height: 400)) NSLayoutConstraint.activate([view.widthAnchor.constraint(equalTo: view.heightAnchor, multiplier: 3), view.heightAnchor.constraint(greaterThanOrEqualToConstant: 100)]) } } class MyView: NSView { let textField = NSTextField(labelWithString: String(repeating: "a b c d e f g h i j k l m n o p q r s t u v w x y z ", count: 2)) override init(frame frameRect: NSRect) { super.init(frame: frameRect) textField.translatesAutoresizingMaskIntoConstraints = false textField.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) addSubview(textField) NSLayoutConstraint.activate([textField.topAnchor.constraint(equalTo: topAnchor), textField.leadingAnchor.constraint(equalTo: leadingAnchor), textField.trailingAnchor.constraint(equalTo: trailingAnchor)]) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func resize(withOldSuperviewSize oldSize: NSSize) { // textField.font = .systemFont(ofSize: frame.width * 0.05) textField.font = .systemFont(ofSize: frame.height * 0.1) } }
Topic: UI Frameworks SubTopic: AppKit Tags:
Replies
13
Boosts
0
Views
496
Activity
Nov ’25
NSOutlineView incorrectly draws disclosure indicator when item views are SwiftUI views.
I am using an NSOutlineView via NSViewRepresentable in a SwiftUI application running on macOS. Everything has been working fine. Up until lately, I've been returning a custom NSView for each item using the standard: func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? { // View recycling omitted. return MyItemView(item) } Now I want to explore using a little bit more SwiftUI and returning an NSHostingView from this delegate method. func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? { // View recycling omitted. let rootView = MySwiftUIView(item) let hostingView = NSHostingView(rootView: rootView) return hostingView } For the most part, this appears to be working fine. NSOutlineView is even correctly applying highlight styling, so that's great. But there's one small glitch. The outline view's disclosure triangles do not align with the hosting view's content. The disclosure triangles appear to just be pinned to the top. Perhaps they can't find a baseline constraint or something? Is there any SwiftUI modifier or AppKit/SwiftUI technique I can apply here to get the disclosure button to appear in the right place? Here is what the SwiftUI + NSHostingView version looks like: Note the offset disclosure indicators. (Image spacing is a bit off as well using Label, but fixable. Here is what an NSView with NSTextFields looks like: Disclosure indicators are correctly aligned, as you would expect.
Replies
1
Boosts
0
Views
156
Activity
Nov ’25
AVAssetExportSession ignores frameDuration 60fps and exports at 30fps, but AVPlayer playback is correct
Hey everyone, I'm stuck on a really frustrating AVFoundation problem. I'm building a video editor that uses a custom AVVideoCompositor to add effects, and I need the final output to be 60 FPS. So basically, I create an AVMutableComposition to sequence my video clips. I create an AVMutableVideoComposition and set the frame rate to 60 FPS: videoComposition.frameDuration = CMTime(value: 1, timescale: 60) I assign my CustomVideoCompositor class to the videoComposition. I create an AVPlayerItem with the composition and video composition. The Problem: Playback Works: When I play the AVPlayerItem in an AVPlayer, it's perfect. It plays at a smooth 60 FPS, and my custom compositor's startRequest method is called 60 times per second. Export Fails: When I try to export the exact same composition and video composition using AVAssetExportSession, the final .mp4 file is always 30 FPS (or 29.97). I've logged inside my custom compositor during the export, and it's definitely being called 30 times per second, so it's generating the 30 frames. It seems like AVAssetExportSession is just dropping every other frame when it encodes the video. My source videos are screen recordings which I recorded using ScreenCaptureKit itself with the minimum frame interval to be 60. Here is my export function. I'm using the AVAssetExportPresetHighestQuality preset :- func exportVideo(to outputURL: URL) async throws { guard let composition = composition, let videoComposition = videoComposition else { throw VideoCompositionError.noValidVideos } try? FileManager.default.removeItem(at: outputURL) guard let exportSession = AVAssetExportSession( asset: composition, presetName: AVAssetExportPresetHighestQuality // Is this the problem? ) else { throw VideoCompositionError.trackCreationFailed } exportSession.outputFileType = .mp4 exportSession.videoComposition = videoComposition // This has the 60fps setting try await exportSession.export(to: outputURL, as: .mp4) } I've created a bare bones sample project that shows this exact bug in action. The resulting video is 60fps during playback, but only 30fps during the export. https://github.com/zaidbren/SimpleEditor My Question: Why is AVAssetExportSession ignoring my 60 FPS frameDuration and defaulting to 30 FPS, even though AVPlayer respects it?
Replies
1
Boosts
0
Views
471
Activity
Oct ’25
Prevent default file selector in a SwiftUI DocumentGroup app and show a custom welcome window on launch
I’m building a macOS document based app using SwiftUI’s DocumentGroup API. By default, when a document based app launches, macOS automatically shows a file open panel or creates a new untitled document window. However, I want to suppress this default behavior and instead show a custom welcome window when the app starts — something similar to how Xcode or Final Cut Pro shows a “Welcome” or “Start Project” screen first. So basically, when the user opens the app normally, it should not open the document selector or create a document automatically. Instead, it should show my custom SwiftUI or AppKit window. Here is my Code :- //MyApp.swift import SwiftUI import AppKit @main struct PhiaApp: App { @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate var body: some Scene { DocumentGroup(newDocument: MyDocumentModel()) { file in EditorView(document: file.document, filePath: file.fileURL) } Settings { EmptyView() } } } Current I have this code setup for my MainApp.swift, where I am using the AppDelegate to create a custom recording window using appkit and also defining the DocumentGroup to handle the custom .myapp file opens. However, when I launch the app, its showing my appkit window as well as the macOs native file Selector to select the file I want to open. I want when the user opens the app normally, it should not open the document selector or create a document automatically. Instead, it should show my custom SwiftUI or AppKit window. However, the app should still fully support opening .myapp documents by double clicking from Finder, using the standard File → Open and File → New menu options, also having multiple document windows open at once. This is my AppDelegate.swift file :- import AppKit import SwiftUI class AppDelegate: NSObject, NSApplicationDelegate { var panel: Panel? private var statusItem: NSStatusItem? func applicationDidFinishLaunching(_ notification: Notification) { showWindow() } // MARK: - Window control func showWindow() { if panel == nil { let root = RecordingViewMain() let newPanel = Panel(rootView: root) if let screen = NSScreen.main { let size = NSSize(width: 360, height: 240) let origin = NSPoint( x: screen.visibleFrame.midX - size.width / 2, y: screen.visibleFrame.midY - size.height / 2 ) newPanel.setFrame(NSRect(origin: origin, size: size), display: true) } panel = newPanel } panel?.makeKeyAndOrderFront(nil) } func hideWindow() { panel?.orderOut(nil) } @objc private func showPanelAction() { showWindow() } @objc private func quitAction() { NSApp.terminate(nil) } }
Replies
2
Boosts
0
Views
269
Activity
Oct ’25
Value of type 'SCRecordingOutput' has no member 'delegate'
Hello, I am trying to capture screen recording ( output.mp4 ) using ScreenCaptureKit and also the mouse positions during the recording ( mouse.json ). The recording and the mouse positions ( tracked based on mouse movements events only ) needs to be perfectly synced in order to add effects in post editing. I started off by using the await stream?.startCapture() and after that starting my mouse tracking function :- try await captureEngine.startCapture(configuration: config, filter: filter, recordingOutput: recordingOutput) let captureStartTime = Date() mouseTracker?.startTracking(with: captureStartTime) But every time I tested, there is a clear inconsistency in sync between the recorded video and the recorded mouse positions. The only thing I want is to know when exactly does the recording "actually" started so that I can start the mouse capture at that same time, and thus I tried using the Delegates, but being able to set them up perfectly. import Foundation import AVFAudio import ScreenCaptureKit import OSLog import Combine class CaptureEngine: NSObject, @unchecked Sendable { private let logger = Logger() private(set) var stream: SCStream? private var streamOutput: CaptureEngineStreamOutput? private var recordingOutput: SCRecordingOutput? private let videoSampleBufferQueue = DispatchQueue(label: "com.francestudio.phia.VideoSampleBufferQueue") private let audioSampleBufferQueue = DispatchQueue(label: "com.francestudio.phia.AudioSampleBufferQueue") private let micSampleBufferQueue = DispatchQueue(label: "com.francestudio.phia.MicSampleBufferQueue") func startCapture(configuration: SCStreamConfiguration, filter: SCContentFilter, recordingOutput: SCRecordingOutput) async throws { // Create the stream output delegate. let streamOutput = CaptureEngineStreamOutput() self.streamOutput = streamOutput do { stream = SCStream(filter: filter, configuration: configuration, delegate: streamOutput) try stream?.addStreamOutput(streamOutput, type: .screen, sampleHandlerQueue: videoSampleBufferQueue) try stream?.addStreamOutput(streamOutput, type: .audio, sampleHandlerQueue: audioSampleBufferQueue) try stream?.addStreamOutput(streamOutput, type: .microphone, sampleHandlerQueue: micSampleBufferQueue) self.recordingOutput = recordingOutput recordingOutput.delegate = self try stream?.addRecordingOutput(recordingOutput) try await stream?.startCapture() } catch { logger.error("Failed to start capture: \(error.localizedDescription)") throw error } } func stopCapture() async throws { do { try await stream?.stopCapture() } catch { logger.error("Failed to stop capture: \(error.localizedDescription)") throw error } } func update(configuration: SCStreamConfiguration, filter: SCContentFilter) async { do { try await stream?.updateConfiguration(configuration) try await stream?.updateContentFilter(filter) } catch { logger.error("Failed to update the stream session: \(String(describing: error))") } } func stopRecordingOutputForStream(_ recordingOutput: SCRecordingOutput) throws { try self.stream?.removeRecordingOutput(recordingOutput) } } // MARK: - SCRecordingOutputDelegate extension CaptureEngine: SCRecordingOutputDelegate { func recordingOutputDidStartRecording(_ recordingOutput: SCRecordingOutput) { let startTime = Date() logger.info("Recording output did start recording \(startTime)") } func recordingOutputDidFinishRecording(_ recordingOutput: SCRecordingOutput) { logger.info("Recording output did finish recording") } func recordingOutput(_ recordingOutput: SCRecordingOutput, didFailWithError error: any Error) { logger.error("Recording output failed with error: \(error.localizedDescription)") } } private class CaptureEngineStreamOutput: NSObject, SCStreamOutput, SCStreamDelegate { private let logger = Logger() override init() { super.init() } func stream(_ stream: SCStream, didOutputSampleBuffer sampleBuffer: CMSampleBuffer, of outputType: SCStreamOutputType) { guard sampleBuffer.isValid else { return } switch outputType { case .screen: break case .audio: break case .microphone: break @unknown default: logger.error("Encountered unknown stream output type:") } } func stream(_ stream: SCStream, didStopWithError error: Error) { logger.error("Stream stopped with error: \(error.localizedDescription)") } } I am getting error Value of type 'SCRecordingOutput' has no member 'delegate' Even though I am targeting macOs 15+ ( macOs 26 actually ) and macOs only. What is the best way to achieving the desired result? Is there any other / better way to do it?
Replies
1
Boosts
0
Views
328
Activity
Oct ’25
NSScrollView only scrolls vertically for NSTableView after window resize
Hi everyone, I’m looking for help with an issue where using insertRows to add a row to an NSTableView requires resizing the window before I can scroll to the newly added rows that extend beyond the visible area of the NSScrollView. import Cocoa class ViewController: NSViewController { var data = Constants.movies // Table components var titleField = NSTextField() var directorField = NSTextField() var releaseYearField = NSTextField() var addMovieButton = NSButton(title: "Add Movie", image: NSImage(systemSymbolName: "plus", accessibilityDescription: "")!, target: nil, action: #selector(addMovie)) var table = NSTableView() var titleColumn = NSTableColumn(identifier: NSUserInterfaceItemIdentifier(Constants.titleColumnId)) var directorColumn = NSTableColumn(identifier: NSUserInterfaceItemIdentifier(Constants.directorColumnId)) var releaseYearColumn = NSTableColumn(identifier: NSUserInterfaceItemIdentifier(Constants.releaseColumnId)) // Scroll view let scrollView = NSScrollView() override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. view.wantsLayer = true view.layer?.backgroundColor = NSColor.black.cgColor configure() } private func configure() { titleField.translatesAutoresizingMaskIntoConstraints = false directorField.translatesAutoresizingMaskIntoConstraints = false releaseYearField.translatesAutoresizingMaskIntoConstraints = false addMovieButton.translatesAutoresizingMaskIntoConstraints = false scrollView.translatesAutoresizingMaskIntoConstraints = false table.translatesAutoresizingMaskIntoConstraints = false titleField.placeholderString = "Movie Title" directorField.placeholderString = "Name of Director" releaseYearField.placeholderString = "Year Released" // Configure table table.addTableColumn(titleColumn) table.addTableColumn(directorColumn) table.addTableColumn(releaseYearColumn) titleColumn.title = "Movie Title" directorColumn.title = "Director" releaseYearColumn.title = "Year Released" table.delegate = self table.dataSource = self table.focusRingType = .none // Configure scroll view scrollView.documentView = table scrollView.hasVerticalScroller = true scrollView.hasHorizontalScroller = false scrollView.autohidesScrollers = true // Add views to the hierarchy view.addSubview(titleField) view.addSubview(directorField) view.addSubview(releaseYearField) view.addSubview(addMovieButton) view.addSubview(scrollView) // view.addSubview(table) NSLayoutConstraint.activate([ titleField.widthAnchor.constraint(equalToConstant: 150), directorField.widthAnchor.constraint(equalToConstant: 150), releaseYearField.widthAnchor.constraint(equalToConstant: 150), addMovieButton.widthAnchor.constraint(equalToConstant: 100), titleField.topAnchor.constraint(equalTo: view.topAnchor, constant: 20), directorField.topAnchor.constraint(equalTo: view.topAnchor, constant: 20), releaseYearField.topAnchor.constraint(equalTo: view.topAnchor, constant: 20), addMovieButton.topAnchor.constraint(equalTo: view.topAnchor, constant: 20), directorField.trailingAnchor.constraint(equalTo: view.centerXAnchor, constant: -10), releaseYearField.leadingAnchor.constraint(equalTo: view.centerXAnchor, constant: 10), titleField.trailingAnchor.constraint(equalTo: directorField.leadingAnchor, constant: -20), addMovieButton.leadingAnchor.constraint(equalTo: releaseYearField.trailingAnchor, constant: 20), scrollView.topAnchor.constraint(equalTo: titleField.bottomAnchor, constant: 20), scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20), scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20), scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -20), // table.widthAnchor.constraint(equalTo: scrollView.contentView.widthAnchor), // table.heightAnchor.constraint(equalTo: scrollView.contentView.heightAnchor) ]) } @objc private func addMovie() { let title = titleField.stringValue let director = directorField.stringValue guard let year = Int(releaseYearField.stringValue) else { return } let movie = Movie(title: title, director: director, releaseYear: year) data.append(movie) // Approach 1 // table.reloadData() // Approach 2 let newRow = data.count - 1 table.insertRows(at: IndexSet(integer: newRow), withAnimation: .slideDown) } Using table.reloadData works fine and I can scroll to the new rows as I add them, but it doesn't have the smooth animation I want to achieve. What is the best practice and correct way for using table.insertRows to avoid this issue? Note: I am using programmatic constraints only.
Replies
1
Boosts
0
Views
236
Activity
Oct ’25
SwiftUI buttons behind NSToolbarView are not clickable on macOS 26 beta
Overview Starting with macOS 26 beta 1, a new NSGlassContainerView is added inside NSToolbarView. This view intercepts mouse events, so any SwiftUI Button (or other interactive view) overlaid on the title‑bar / toolbar area no longer receives clicks. (The same code works fine on macOS 15 and earlier.) Filed as FB18201935 via Feedback Assistant. Reproduction (minimal project) macOS 15 or earlier → button is clickable macOS 26 beta → button cannot be clicked (no highlight, no action call) @main struct Test_macOS26App: App { init() { // Uncomment to work around the issue (see next section) // enableToolbarClickThrough() } var body: some Scene { WindowGroup { ContentView() } .windowStyle(.hiddenTitleBar) // ⭐️ hide the title bar } } struct ContentView: View { var body: some View { NavigationSplitView { List { Text("sidebar") } } detail: { HSplitView { listWithOverlay listWithOverlay } } } private var listWithOverlay: some View { List(0..<30) { Text("item: \($0)") } .overlay(alignment: .topTrailing) { // ⭐️ overlay in the toolbar area Button("test") { print("test") } .glassEffect() .ignoresSafeArea() } } } Investigation In Xcode View Hierarchy Debugger, a layer chain NSToolbarView > NSGlassContainerView sits in front of the button. -[NSView hitTest:] on NSGlassContainerView returns itself, so the event never reaches the SwiftUI layer. Swizzling hitTest: to return nil when the result is the view itself makes the click go through: func enableToolbarClickThrough() { guard let cls = NSClassFromString("NSGlassContainerView"), let m = class_getInstanceMethod(cls, #selector(NSView.hitTest(_:))) else { return } typealias Fn = @convention(c)(AnyObject, Selector, NSPoint) -> Unmanaged<NSView>? let origIMP = unsafeBitCast(method_getImplementation(m), to: Fn.self) let block: @convention(block)(AnyObject, NSPoint) -> NSView? = { obj, pt in guard let v = origIMP(obj, #selector(NSView.hitTest(_:)), pt)?.takeUnretainedValue() else { return nil } return v === (obj as AnyObject) ? nil : v // ★ make the container transparent } method_setImplementation(m, imp_implementationWithBlock(block)) } Questions / Call for Feedback Is this an intentional behavioral change? If so, what is the recommended public API or pattern for allowing clicks to reach views overlaid behind the toolbar? Any additional data points or confirmations are welcome—please reply if you can reproduce the issue or know of an official workaround. Thanks in advance!
Replies
3
Boosts
0
Views
319
Activity
Oct ’25
What is the standard process of subclassing NSTextLocation?
Cannot find any guidance in the forums and Developer Doc, the WWDC session Meet TextKit2 says this protocol is served for any location type espacially useful when the underlying doc model is not linear. But when I try to subclass the NSTextLocation with my own type to define a more structured location rather than a linear one, also with my own NSTextContentManager subclass implementation, I keep receiving the system internal Location model like NSCountableTextLocation compare to my location which cause the app to crash. -[NSCountableTextLocation compare:] receiving unmatching type <MarkdownTK2.ChildIndexPathLocation: 0x9b2402aa0> In my own NSTextContentManager subclass: public override func offset( from: any NSTextLocation, to: any NSTextLocation ) -> Int { this method will also both receive my own location and some times receiving the system defined location that I can not calculate the offset then just return zero. The doc only says If you provide your own implementation of the NSTextLocation protocol to manage locations in your content, subclass NSTextContentManager and implement your own storage object to support those locations. OS Development environment: Xcode 26.0, macOS 26.0 Run-time configuration: macOS 26.0
Replies
3
Boosts
0
Views
355
Activity
Oct ’25