I'm wondering what the correct, or recommended, way is to dismiss a SwiftUI that is being presented as a sheet hosted by an NSHostingController. The usual technique of invoking @Environment(\.dismiss) does not appear to work.
Consider the code below. An NSWindowController is attempting to display a SwiftUI SettingsView as a sheet. The sheet is correctly presented, but the SettingsView is unable to dismiss itself.
I am able to make it work by passing a closure into SettingsView that calls back to the NSWindowController but it's rather convoluted because SettingsView doesn't know the view controller that's hosting it until after SettingsView has been created, which means "finding" that view controller in the window controller to dismiss is more involved than it should be.
Is there a better strategy to leverage here?
final class MyViewController: NSViewController {
@IBAction func buttonClicked(_ sender: NSButton) {
if let presenter = window?.contentViewController {
presenter.presentAsSheet(NSHostingController(rootView: SettingsView()))
}
}
}
struct SettingsView: View {
@Environment(\.dismiss) private var dismiss
var body: some View {
VStack {
Button("Cancel", role: .cancel) {
dismiss() // This call does not dismiss the sheet.
} .keyboardShortcut(.cancelAction)
}
}
}
Thank you.
macOS 15.4.1 (24E263), Xcode 16.3 (16E140)
AppKit
RSS for tagConstruct and manage a graphical, event-driven user interface for your macOS app using AppKit.
Posts under AppKit tag
200 Posts
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
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?
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?
Trying to incorporate a LookAroundView into my macOS application but unable to make the LookAroundView interactive at all. I can get it to display a static image, but there's no interactivity at all and no controls visible.
This is using the SwiftUI LookAroundPreview view as well as trying to wrap MKLookAroundViewController inside an NSViewRepresentable.
The navigation properties are set to true but that doesn't seem to make a difference. Would love to incorporate this feature but without interactivity its value is limited.
macOS 15.4.1 (24E263), Xcode Version 16.3 (16E140)
In AppKit, NSSegmentedControl has various styles defined by NSSegmentStyle and various tracking modes defined by NSSegmentSwitchTracking.
How can we set these properties in SwiftUI?
I'm currently using a Picker with the view modifier .pickerStyle(.segmented) applied but this seems to produce a segmented control with tracking set to "select one".
In particular I'm looking for momentary tracking so that I can create navigation-style buttons for backward/forward navigation.
Under AppKit, the canonical way to do this is an NSSegmentedControl of style separated and tracking momentary.
Is that possible under SwiftUI for macOS? (Using the latest versions of everything.)
When my macOS Cocoa app displays a modal alert with beginSheetModal(for:completionHandler:), VoiceOver sometimes seems to focus on an "illegal" upper level, where any attempts at navigation will give the unhelpful response "Alert, dialog", until you "drill down" with VO + shift + down or switch apps. After that, things will work as expected.
Is this a known bug? Does it happen to anybody else, or am I doing something wrong?
Hi,
In mac os swift ui application when i set window.isReleasedWhenClosed and when i close the window the app is getting crashed with exc_bad_access. but when i leave it to default value the app is not crashing. for some windows setting window.isReleasedWhenClosed to true is woking properly when closing the windows. But for some windows it is crashing. If i dont set it to true the window is not removed from NSApplication.shared.windows sometimes. I am confused about setting isReleasedWhenClosed to true
Could someone calrify on this please.
thank in advance.
Without resorting to NSViewRepresentable, is there a view or view modifier in SwiftUI that can create an NSComboButton on macOS?
NSComboButton was introduced in macOS 13 and is (relatively) new to AppKit:
Apple Developer - NSComboButton
I only require support on macOS for this control.
Note that this is not to be confused with NSComboBox, which is a completely different control.
I'm currently working on a project to integrate some SwiftUI components into an existing AppKit application. The application makes extensive use of NSViewControllers. I can easily bridge between AppKit and SwiftUI using a view model that conforms to ObservableObject and is shared between the NSViewController and the SwiftUI View. But it's kind of tedious creating a view model for every view.
Is it "safe" and "acceptable" for the NSViewController to "hold on" to the SwiftUI View that it creates and then access its @State or @StateObject properties?
The lifecycle of DetailsView, a SwiftUI View, isn't clear to me when viewed through the lens of an NSViewController. Consider the following:
import AppKit
import SwiftUI
struct DetailsView: View {
@State var details: String = ""
var body: some View {
Text(details)
}
}
final class ViewController: NSViewController {
private let detailsView: DetailsView
init() {
self.detailsView = DetailsView()
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
view.addSubview(NSHostingView(rootView: detailsView))
}
func updateDetails(_ details: String) {
// Is this 'safe' and 'acceptable'?
self.detailsView.details = details
}
}
Is the view controller guaranteed to always be updating the correct @State property or is there a chance that the view controller's reference to it somehow becomes stale because of a SwiftUI update?
For my macOS app, I'm trying to change the mouse cursor to a pointing hand while hovering over a specific view. However, when the view is scaled with an animation triggered by hovering (using .scaleEffect() and .animation()), the cursor doesn't change as expected. Is there any workaround to fix this?
This is a sample code:
struct ContentView: View {
@State private var hovering = false
var body: some View {
VStack {
Text("Hover me")
.padding()
.background(hovering ? Color.blue : Color.gray)
.scaleEffect(hovering ? 1.2 : 1.0)
.animation(.linear(duration: 0.2), value: hovering)
.onHover { hovering in
self.hovering = hovering
if hovering {
NSCursor.pointingHand.push()
} else {
NSCursor.pop()
}
}
}
.frame(width: 200, height: 200)
}
}
This is how it works:
As you can see, when the pointer enters the view, the cursor changes momentarily before reverting back to the arrow icon.
I also tried using NSTrackingArea with an NSView placed over the view, but it did not solve the issue. It might be that the combination of .scaleEffect() and .animation() is causing a forced cursor reset (possibly related to the use of NSWindow.disableCursorRects() or something similar). However, I'm not entirely sure.
Any insights or suggestions would be greatly appreciated. Thanks!
Security scoped bookmarks that were created before updating to macOS 14.7.5 cannot be resolved anymore after updating to macOS 14.7.5.
Reproduction:
Sandboxed app on macOS version 14.7.4
Create and store a security scoped bookmark to a user selected folder:
let url: URL = <user selected url from NSOpenPanel>
let data = try url.bookmarkData(options: [.withSecurityScope], includingResourceValuesForKeys: nil, relativeTo: nil)
<persistently store data>
Update to macOS 14.7.5
Resolve the previously stored bookmark:
let data: Data = <restore data from persistent storage>
var stale: Bool = true
let url = try URL(resolvingBookmarkData: data, options: [.withSecurityScope], relativeTo: nil, bookmarkDataIsStale: &stale)
Expected:
The bookmark is resolved correctly and the resulting url can be used to access the folder/file in the sandboxed app after starting access.
Observed:
URL(resolvingBookmarkData:) throws an error:
Error Domain=NSCocoaErrorDomain Code=259 "The file couldn’t be opened because it isn’t in the correct format."
New security scoped bookmarks created on macOS 14.5.7 can be resolved without issue.
The same appears to happen with macOS 13.7.5.
Entitlements:
com.apple.security.app-sandbox
com.apple.security.files.bookmarks.app-scope
com.apple.security.files.user-selected.read-write
This is very disruptive, as it appears that Sandboxed apps cannot access any previously stored bookmarks anymore.
Particularly after the recent ScopedBookmarkAgent issues in 14.7.1 and 15.0, which were resolved in 14.7.3/15.1 respectively: https://developer.apple.com/forums/thread/764435
Take a look at this simple code:
import Cocoa
import SwiftUI
struct DemoView: View {
var body: some View {
Text("Click me!")
.onTapGesture {
print("Clicked")
}
}
}
class FlippedView: NSView {
override var isFlipped: Bool {
return true
}
}
class ViewController: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
let stackView = NSStackView()
stackView.orientation = .vertical
stackView.alignment = .leading
stackView.spacing = 0
stackView.translatesAutoresizingMaskIntoConstraints = false
let hostView = NSHostingView(rootView: DemoView())
stackView.addArrangedSubview(hostView)
let scrollView = NSScrollView()
scrollView.translatesAutoresizingMaskIntoConstraints = false
let flippedView = FlippedView()
flippedView.addSubview(stackView)
scrollView.documentView = flippedView
view.addSubview(scrollView)
NSLayoutConstraint.activate([
scrollView.topAnchor.constraint(equalTo: view.topAnchor),
scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
])
}
}
I need my scroll view to start at the very top, so i put it inside a flipped document view.
But now .onTapGesture does not fire.
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?
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 > 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?
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
}
}
Hello,
is there a way to implement Continuity Markup in our own apps?
(This is what I'm talking about: https://support.apple.com/en-us/102269 , scroll down to "Use Continuity Markup").
Also, why does a QuickLook panel (QLPreviewPanel.shared()) not display the markup options when triggered from my app for png image files in my app's Group Container? Do I need to implement certain NSServicesMenuRequestor methods for that?
Sadly, I could not find any docs on that.
Thank you,
– Matthias
To get menubar size, we can call.
let menuBarHeight = NSStatusBar.system.thickness
That is returning 24 and it is the same as my external screen. I did command + shift + 5 and use the screen capture tool to rougly measure the size of menubar. It is roughly 24px.
However, for my macbook pro 14 inches m2 pro. The menubar seem thicker because of the webcam. Is there a way to find out the size in Swift?
All the threads only contain system calls. The crashed thread only contains a single call to my app's code which is main.swift:13.
What could cause such a crash?
crash.crash
It seems that NSTextView has an issue with deleting text and setting any attribute at the same time, when it also has a textContainerInset.
With the code below, after 1 second, the empty line in the text view is automatically deleted and the first line is colored red. The top part of the last line remains visible at its old position. Selecting the whole text and then deselecting it again makes the issue disappear.
Is there a workaround?
I've created FB16897003.
class ViewController: NSViewController {
@IBOutlet var textView: NSTextView!
override func viewDidAppear() {
textView.textContainerInset = CGSize(width: 0, height: 8)
let _ = textView.layoutManager
textView.textStorage!.setAttributedString(NSAttributedString(string: "1\n\n2\n3\n4"))
textView.textStorage!.addAttribute(.foregroundColor, value: NSColor.labelColor, range: NSRange(location: 0, length: textView.textStorage!.length))
DispatchQueue.main.asyncAfter(deadline: .now() + 1) { [self] in
textView.selectedRange = NSRange(location: 3, length: 0)
textView.deleteBackward(nil)
textView.textStorage!.beginEditing()
textView.textStorage!.addAttribute(.foregroundColor, value: NSColor.red, range: NSRange(location: 0, length: 2))
textView.textStorage!.endEditing()
}
}
}
Environment:
• macOS: Sequoia 15.3.2
• Xcode: 16.2
• Framework: AppKit (Objective-C)
Issue:
When programmatically setting the first responder to an NSSecureTextField shortly after its containing window loads or becomes key, a visual anomaly intermittently occurs (roughly 50% of the time).
A semi-transparent UI element—likely related to the system’s Keychain password suggestion/autofill feature—appears detached from the text field. Instead of anchoring to the field, it renders elsewhere on the screen.
I found similar issues discussed here:
https://stackoverflow.com/questions/74220070/strange-transparent-view-appears-beneath-textfield-in-mac-catalyst-app
https://stackoverflow.com/questions/73277582/swiftui-view-with-textfield-and-securefield-buggy-on-macos-shows-strange-view/73615876#73615876
https://developer.apple.com/forums/thread/708075