I am revising my app to support NSWritingToolsCoordinator/ NSWritingToolsCoordinatorDelegate.
When proofreading some paragraphs, it works well.
But, when proofreading many paragraphs (for example, 56 paragraphs), it can't complete the proofreading.
I am not sure which is wrong with my app or macOS API.
I think I implemented all NSWritingToolsCoordinatorDelegate methods.
Is there any information for such an issue?
Phenomenon
For paragraphs 1-9, text animation completed. But, for paragraphs 10-56, text animation does not complete.
It shows 5 corrected items, but I can't jump to items 3, 4, 5, when I click the ">" button in the "Proofread" window.
Items 3, 4, 5 were not corrected actually.
Log
For each NSWritingToolsCoordinatorDelegate method, the method name and main arguments are output by NSLog.
requestsContextsForScope
willChangeToState newState:2
requestsPreviewForTextAnimation range:(0, 18233)
prepareForTextAnimation range:(0, 18233)
willChangeToState newState:3
requestsPreviewForTextAnimation range:(0, 18233)
finishTextAnimation range:(0, 18233)
requestsPreviewForRect
requestsPreviewForTextAnimation range:(0, 1837)
replaceRange proposedText:an range:(208, 2)
replaceRange proposedText:you range:(443, 4)
prepareForTextAnimation range:(1836, 16396)
requestsPreviewForTextAnimation range:(0, 1836)
requestsBoundingBezierPathsForRange range:(208, 2)
requestsBoundingBezierPathsForRange range:(443, 3)
requestsPreviewForRect
prepareForTextAnimation range:(0, 1836)
prepareForTextAnimation range:(1836, 0)
finishTextAnimation range:(1836, 16396)
requestsPreviewForTextAnimation range:(1836, 16396)
requestsBoundingBezierPathsForRange range:(208, 2)
requestsBoundingBezierPathsForRange range:(443, 3)
prepareForTextAnimation range:(1836, 16396)
finishTextAnimation range:(0, 1836)
finishTextAnimation range:(0, 1836)
replaceRange proposedText:an range:(208, 2)
requestsUnderlinePathsForRange range:(208, 2)
requestsUnderlinePathsForRange range:(443, 3)
selectRanges ranges.count:1
requestsBoundingBezierPathsForRange range:(208, 2)
replaceRange proposedText:an range:(208, 2)
requestsUnderlinePathsForRange range:(208, 2)
requestsUnderlinePathsForRange range:(443, 3)
selectRanges ranges.count:1
replaceRange proposedText:you range:(443, 3)
requestsUnderlinePathsForRange range:(208, 2)
requestsUnderlinePathsForRange range:(443, 3)
selectRanges ranges.count:1
requestsBoundingBezierPathsForRange range:(443, 3)
replaceRange proposedText:you range:(443, 3)
requestsUnderlinePathsForRange range:(208, 2)
requestsUnderlinePathsForRange range:(443, 3)
selectRanges ranges.count:1
macOS version is 15.3.1 (24D70)
AppKit
RSS for tagConstruct and manage a graphical, event-driven user interface for your macOS app using AppKit.
Post
Replies
Boosts
Views
Activity
Hi all,
I am trying to allow users of my app to select extra options when opening documents, and to remember those options when re-opening documents at launch.
So far best idea I have is:
Subclass NSDocumentController to provide an NSOpenPanel.accessoryView with the options
Create a URL bookmark for each opened file and keep a mapping of bookmarks to options
On launch and when the recent documents list changes, prune the stored mappings to match only the recent items
Has anyone done this before, or know of a better approach?
Thank you.
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.
Starting from Sequoia release 15.2 apps crash with following call stack, when adding static text controls. First call to [NSTextField setStringValue] causes following crash
0 libobjc.A.dylib 0x19f2f5820 objc_msgSend + 32
1 AppKit 0x1a3355460 -[NSCell _objectValue:forString:errorDescription:] + 144
2 AppKit 0x1a3355348 -[NSCell setStringValue:] + 48
3 AppKit 0x1a33af9fc -[NSControl setStringValue:] + 104
4 AppKit 0x1a3d1f190 -[NSTextField setStringValue:] + 52
It happens on specific MacBook Pro models(16 in MacBook Pro).
Crash analysis found that isa pointer of the object was corrupted for the object NSCell. Enabled Zombies in debugging, not found any issue. Also tried address sanitizer. Since the issue started with a recent release of macOS, any changes in the Appkit in the recent releases trigger the crash? Any help/suggestions would be appreciated.
macOS: 15.0
macFUSE: 4.8.3
I am using rclone + macFUSE and mount my netdisk where it has created three subdirectories in its root directory: /user, /share, and /group.
When I save a file to /[root]/user using NSSavePanel and name it test.txt, I expect the file to be saved as:
/[root]/user/test.txt
However, occasionally, the delegate method:
- (BOOL)panel:(id)sender validateURL:(NSURL *)url error:(NSError **)outError {
}
returns an incorrect path:
/[root]/test.txt
This issue only occurs when selecting /user. The same operation works correctly for /share and /group.
Is there any logs I could provide to help solving this issue?
Many thanks!
In a macOS Swift app I'm trying to get mouse events to not coalesce. This used to work in an Obj-C app by calling [NSEvent setMouseCoalescingEnabled:NO]. setMouseCoalescingEnabled is not exposed in the Swift version of NSEvent. I built a bridge over to a .mm file that calls [NSEvent setMouseCoalescingEnabled:NO] and checked NSEvent.isMouseCoalescingEnabled in Swift after calling and it returns false saying that coalescing is disabled. The mouse is still skipping points (func mouseDragged(with theEvent: NSEvent)) when it is dragged across the window fast. I'm calling [NSEvent setMouseCoalescingEnabled:NO] in applicationDidFinishLaunching().
I'm not quite sure where the problem is, but I will describe what I am doing to recreate the issue, and am happy to provide whatever information I can to be more useful.
I am changing the ActivationPolicy for my app in order to make it unobtrusive when in the background (e.g. hiding it from the dock and using only a menu bar status item). When the user activates the app with a hotkey, it changes from NSApplicationActivationPolicyAccessory back to NSApplicationActivationPolicyRegular. This allows normal usage (dock icon, menu bar, etc.)
This works fine, except in a rare situation which I finally just tracked down. If there is a window open in the app and I use the hotkey to convert back to an accessory, and then disconnect and reconnect the display on which the app was previously displayed, when I convert the app back to "regular mode", the menu bar has disappeared (and I am left with an empty space at the top of the screen). I can also trigger this bug by having the display in question briefly mirror the other display (effectively "orphaning" the hidden app), and then restoring the original side-by-side configuration before activating the app again.
The app otherwise works, but the menu bar is missing. Switching back and forth with other apps does not fix the problem. Quitting and restarting the app resolves the issue. As does disabling the accessory only mode and forcing the app to always remain in "regular mode" with a dock icon (there is a preference for this in my app). Once fixed, I can then re-enable the "accessory mode" and all is well until the bug is triggered again.
The bug would normally occur quite sporadically, presumably requiring a particular combination of changing Spaces or displays, or having the computer go to sleep while this app was in accessory mode. Thus far, the above is the only way I have found that can replicate this issue on demand.
If I close all windows before hiding the app, then it works fine when I revert to "regular mode". It only happens if there is a window open at the time.
Using applicationDidChangeScreenParameters: on my AppDelegate indicates that there is a change in screen, and logging window.screen.frame for each open window in [NSApp orderedWindows] shows that the size changes from e.g. 1920x1080 to 0x0 and back while the display is disconnected or mirrored.
There is also an error in the console in Xcode when this happens -- invalid display identifier <some UUID>.
I have tried various options for window collectionBehavior, as well as various settings for Spaces (which I normally use). None of these changes has fixed the behavior thus far.
I use [NSApp hide:self]; from my AppDelegate to hide the app, and [[NSRunningApplication currentApplication] activateWithOptions:NSApplicationActivateAllWindows];[NSApp unhide:self]; to bring it back to the front.
I welcome any ideas for things to chase down, or requests for more specific information that would be useful.
Thank you!
Fletcher
I'm testing using Group Activities and having no trouble iOS<->iOS or starting an activity on macOS and joining via iOS. However, when I start an activity and then try to join it from another macOS client, the starting side joins the session just fine, but the receiving side acts like I don't have the required app, even when it is already running.
I see the active SharePlay icon in the menu bar, and the Current Activity is shown, but instead of an "Open" button there is a "MyApp Required" string and a "View" button that goes to the App Store. (Where the app is not available yet, as expected, since I'm still working on it.) There is no GroupSession started on that Mac yet, obviously.
I'm looking for any hints to help debug what is going on. How does Group Activities find the app for the activity on macOS and how can I figure out why it isn't finding mine?
Thanks!
In an AppKit document-based project created by Xcode, setting canConcurrentlyReadDocuments to true allows new documents to open normally in Swift 5, but switching to Swift 6 causes an error.
Judging from the error message, it seems to be a threading issue, but I’m not sure how to adjust the code to support the Swift 6 environment.
The project is the most basic code from an Xcode-created document-based project without any modifications, except for changing the Swift version to 6 and setting canConcurrentlyReadDocuments to true.
Source code: https://drive.google.com/file/d/1ryb2TaU6IX884q0h5joJqqZwSX95Q335/view?usp=sharing
AppDelegate.swift
import Cocoa
@main
class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(_ aNotification: Notification)
{
// Insert code here to initialize your application
}
func applicationWillTerminate(_ aNotification: Notification) {
// Insert code here to tear down your application
}
func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool {
return true
}
}
Document.swift
import Cocoa
class Document: NSDocument {
override init() {
super.init()
// Add your subclass-specific initialization here.
}
override class var autosavesInPlace: Bool {
return true
}
override class func canConcurrentlyReadDocuments(ofType typeName: String) -> Bool {
true
}
override func canAsynchronouslyWrite(to url: URL, ofType typeName: String, for saveOperation: NSDocument.SaveOperationType) -> Bool {
true
}
override func makeWindowControllers() {
// Returns the Storyboard that contains your Document window.
let storyboard = NSStoryboard(name: NSStoryboard.Name("Main"), bundle: nil)
let windowController = storyboard.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier("Document Window Controller")) as! NSWindowController
self.addWindowController(windowController)
}
override func data(ofType typeName: String) throws -> Data {
// Insert code here to write your document to data of the specified type, throwing an error in case of failure.
// Alternatively, you could remove this method and override fileWrapper(ofType:), write(to:ofType:), or write(to:ofType:for:originalContentsURL:) instead.
// throw NSError(domain: NSOSStatusErrorDomain, code: unimpErr, userInfo: nil)
return Data()
}
override func read(from data: Data, ofType typeName: String) throws {
// Insert code here to read your document from the given data of the specified type, throwing an error in case of failure.
// Alternatively, you could remove this method and override read(from:ofType:) instead.
// If you do, you should also override isEntireFileLoaded to return false if the contents are lazily loaded.
// throw NSError(domain: NSOSStatusErrorDomain, code: unimpErr, userInfo: nil)
}
}
ViewController.swift
import Cocoa
class ViewController: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
}
I would like to provide a way to choose an app icon as a reward for using the referral feature of the app.
I found that setting the image via NSApp.applicationIconImage works, but only when the app is running, and the app icon is reset on the next launch.
I also found NSDockTile plugin APIs, and that seems to be the way, but it won't be allowed on the Mac App Store.
Is there really no way to do it legit way and pass the Mac App Store review?
Seems to be another really strange limitation imposed.
In summation: I have a nasty bug where my layout manager is laying out text visually overlapping on top of other text, i.e., into a container that it should have left in the rear view as it continues to lay out into ensuing containers. Details below...
I'm coding a word processing app with some custom pagination that involves multiple pages, within which there can be multiple NSTextView/NSTextContainer pairs that represent single column or dual column runs of text.
I generate pagination data by using a measuring NSLayoutManager. This process ensures that no containers overlap, and that they are sized correctly for their associated ranges of text (i.e., non-overlapping, continuous ranges from a single NSTextStorage).
I determine frame sizes by a series of checks, most importantly, by finding the last glyph in a column. Prior to the code below, remainingColumnRange represents the remaining range of my textStorage that is of a consistent column type (i.e., single, left column, or right column). My measuring passes consist of my measuringLayoutManager laying out text into its textContainers, the final of which is an extra overflowContainer (i.e., == measuringLayoutManager.textContainers.last!) which I only use to find the last glyph in the second to last container (measuringContainer, which is thus == measuringLayoutManager.textContainers[count - 2])
let glyphRangeOfLastColumnChar = measuringLayoutManager.glyphRange(forCharacterRange: remainingColumnRange, actualCharacterRange: nil)
let lastGlyphIndex = NSMaxRange(glyphRangeOfLastColumnChar) - 1
measuringLayoutManager.ensureLayout(for: measuringContainer) // Not sure if this is necessary, but I've added it to insure I'm getting accurate measurements.
if measuringLayoutManager.textContainer(forGlyphAt: lastGlyphOfColumnIndex, effectiveRange: &actualGlyphRangeInContainer) == overflowContainer {
actualCharRangeInContainer = measuringLayoutManager.characterRange(forGlyphRange: actualGlyphRangeInContainer, actualGlyphRange: nil)
let overflowLoc = actualCharRangeInContainer.location
remainingColumnRange = NSRange(location: overflowLoc, length: remainingColumnRange.length - overflowLoc)
currentPage += 1
} else {
lineFragmentRectForLastChar = measuringLayoutManager.lineFragmentRect(forGlyphAt: lastGlyphIndex, effectiveRange: nil)
// Resize measuring container if needed.
let usedHeight = lineFragmentRectForLastChar.maxY
if usedHeight < measuringContainer.size.height {
measuringContainer.size = CGSize(width: measuringContainer.size.width, height: usedHeight)
} else if usedHeight == measuringContainer.size.height {
currentPage += 1 // we perfectly filled the page
} else {
// This would be an error case, because all cases should have been handled prior to arriving here. I throw an error. I have never fallen through here.
throw MyClass.anError
}
}
// I use the above data to create a PageLayoutItem, which is a struct that has frame data (CGRect/x,y,w,h), a containerIndex (Int), pageNumber (Int), textRange (NSRange), columnType (custom enum).
// After this I remove the overflowContainer, and continue to iterate through. This is inefficient but I'm simplifying my code to identify the root issue.
I don't explicitly use these containers when done with my pagination process. Rather, I use the PageLayoutItems I have created to generate/resize/remove textContainers/textViews for the UI as needed. My UI-interfacing/generating NSLayoutManager, which is of course assigned to the same NSTextStorage as the measuring layout manager, then iterates through my paginator model class' pageLayoutItems array to generate/resize/remove.
I have verified my pagination data. None of my frames overlap. They are sized exactly the same as they should be per my measurement passes. The number of containers/views needed is correct.
But here's the issue:
My views render the text that SHOULD appear in my final textContainer/textView as visually overlapping the text in my second to last textContainer/textView. I see a garble of text.
When I iterate through my UI textContainers, I get this debug print:
TextContainer 0 glyphRange: {0, 172}
TextContainer 1 glyphRange: {172, 55}
TextContainer 2 glyphRange: {227, 100} // this is wrong, final 31 chars should be in container 3
TextContainer 3 glyphRange: {327, 0} // empty range here, odd
I have tried setting textContainers for glyph ranges explicitly, via:
// Variable names just for clarity here
layoutManager.setTextContainer(correctTextView.textContainer!, forGlyphRange: correctGlyphRangeForThisContainer)
Debug prints show that I'm setting the right ranges there. But they don't retain.
I have tried resizing my final text container to be much larger in case that was the issue. No dice. My final range of text/glyphs still lays out in the wrong container and overlaps the other content laid out there.
Any help here?? I've scoured the forums and have been dealing with this bug for two weeks straight with no hope in sight.
I have discovered an odd issue with NSTextView, NSAdaptiveImageGlyph, and NSLayoutManager.
(Forgive the Objective-C... I'm old-school.)
I can easily display an attributed string containing text and Genmoji NSAdaptiveImageGlyphs with something very basic like this:
textView = [[NSTextView alloc] initWithFrame:NSMakeRect(100.0, 100.0, 500.0, 100.0)];
[textView.textStorage setAttributedString:sampleText];
[self addSubview:textView];
//NSLayoutManager *layoutManager = textView.layoutManager;
I can even insert or paste new Genmoji into there as well.
However, if I uncomment out that last line to retrieve the layoutManager of the text view it breaks the rendering of Genmoji, even if I do nothing with the layoutManager! Just getting it causes the NSTextView to skip past Genmoji glyphs when spacing out glyphs and when rendering.
I thought perhaps getting the layoutManager caused an internal cache to break so I tried ensureLayoutForTextContainer but that didn't help.
Interestingly if I paste a new Genmoji into the NSTextView it doesn't display either. Yet if I select all, copy, and paste into TextEdit I see all the Genmoji just fine so they are there, just invisible and zero size. Using the arrow keys I have to skip past the invisible Genmoji.
But if I comment out that line again I see all Genmoji with no issues.
I do need to use a layoutManager to measure things like text bounds and heights. Elsewhere in the code, my existing drawing routines would like to draw the resulting attributed string not with drawAtPoint (which works fine) but with drawGlyphsForGlyphRange but that doesn't work either as it uses NSLayoutManager.
Is there a trick to getting NSAdaptiveImageGlyphs working with NSLayoutManager?
Thanks for any assistance!
This class is throwing an NSInternalInconsistencyException immediately after my app delegate gets an ApplicationWillResignActive message as shown below. This happens only on 15.2.
In the Event processing run loop in my NSApplication subclass, I added a couple of lines to monitor the value of "mainWindow" as defined in NSApplication. It contains a valid window pointer until after the ApplicationWillResignActive message. FYI I do not appear to ever get a ApplicationDidResignActive call.
My questions:
What is NSUIActivityDocumentMonitor and what exactly does it do? I can find no documentation on it at all. Should I assume it is created by NSDocument? Can or should I prevent its creation?
The error text implies that my NSApplication subclass is not sending out a notification when its "mainWindow" property changes (in my case it appears to get changed to nil as a result of resigning the active state). That has never been an issue before now.
This does not occur on ANY other prior macOS releases including 15.1. How can I prevent this error that is being thrown by a previously unknown class? Are there new recommended actions I should take when I get the ApplicationWillResignActive call? Wouldn't NSApplication/NSObject handle the KVO compliance issue (notify observers of a change to "mainWindow")?
FYI, this only happens when I have an opened document window (either new or opened from the desktop).
If I ignore the error in my run loop, the app continues normally in the background and can be brought back to be the front app no problem.
I'm at my wits end trying to get rid of this (properly instead of ignoring the error) and could use some guidance. This is a mature app in use by many clients. Objective C.
I have an app on the Mac App Store (so sandboxed) that includes a QuickLook Preview Extension that targets Markdown files. It established a QLPreviewingController instance for the macOS QuickLook system to access and it works.
I'm in the process of updating it so that it displays inline images referenced in the file as well as styling the file's text. However, despite setting Downloads folder read-only access permission (and user-selected, though I know that shouldn't be required: no open/save dialogs here) in the extension's entitlements, Sandbox refuses too allow access to the test image: I always get a deny(1) file-read-data error in the log.
FWIW, the test file is referenced in the source Markdown as an absolute unix file path.
I've tried different signings and no joy. I’ve tried placing the referenced image in various other locations. Also no joy. All I can display is the error-case bundle image for 'missing image'.
Question is, is this simply something that QuickLook extensions cannot do from within the sandbox, or am I missing something? Is there anything extra I can do to debug this?
The aim is to save the data of a program in 2 different formats of choice, say type1 (default) and type2.
No problem when + (BOOL)autosavesInPlace is NO, you can save as… and get a choice.
No problem when + (BOOL)autosavesInPlace is YES and you created a new document, you can choose when saving.
But you do not get a choice when you created the new file by duplicating a existing file. It takes the type of the latter.
(Using dataOfType:error:, but did not find a solution either by using writeToURL:ofType:error:, duplicateDocument:, etc.)
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?
Hi,
I detect dark mode on macOS like following:
NSAppearance *appearance = NSApp.mainWindow.effectiveAppearance;
NSString *interface_style = appearance.name;
NSAppearanceName basicAppearance = [appearance bestMatchFromAppearancesWithNames:@[
NSAppearanceNameAqua,
NSAppearanceNameDarkAqua
]];
if([basicAppearance isEqualToString:NSAppearanceNameDarkAqua]){
theme = "Adwaita:dark";
dark_mode = TRUE;
}
if([interface_style isEqualToString:NSAppearanceNameDarkAqua]){
theme = "Adwaita:dark";
dark_mode = TRUE;
}else if([interface_style isEqualToString:NSAppearanceNameVibrantDark]){
theme = "Adwaita:dark";
dark_mode = TRUE;
}else if([interface_style isEqualToString:NSAppearanceNameAccessibilityHighContrastAqua]){
theme = "HighContrast";
}else if([interface_style isEqualToString:NSAppearanceNameAccessibilityHighContrastDarkAqua]){
theme = "HighContrast:dark";
dark_mode = TRUE;
}else if([interface_style isEqualToString:NSAppearanceNameAccessibilityHighContrastVibrantDark]){
theme = "HighContrast:dark";
dark_mode = TRUE;
}
But this doesn't work if my window is in background. As the application window is put into background, it loses dark mode. Howto fix it?
regards, Joël
I have a share extension in my app, that shall allow users to send CSV files, custom app files, and selected text to my app via the share sheet for importing that data.
So, the share extension should activate when the user has selected either:
CSV or plain text files
Custom UTI app files
Text selected in other apps
The supported file types have been defined in as a predicate query according to the example in the docs
This works all fine on iOS, and the file sharing also works on the Mac.
However, on macOS, my app is not shown as a target in the share sheet when the user selects text in other apps and tries to share that text via the context menu.
Does macOS need a different configuration to enable a share extension for selected text?
This is how my Info.plist of the Mac share extension looks like:
...
<plist version="1.0">
<dict>
<key>NSExtension</key>
<dict>
<key>NSExtensionAttributes</key>
<dict>
<key>NSExtensionActivationRule</key>
<string>SUBQUERY (
extensionItems,
$extensionItem,
SUBQUERY (
$extensionItem.attachments,
$attachment,
ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.plain-text" ||
ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "com.myCompany.myApp.customFormat" ||
ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.delimited-values-text"
).@count == $extensionItem.attachments.@count
).@count >= 1</string>
</dict>
...
</dict>
</plist>
I know there is a NSExtensionActivationSupportsText but it seems this cannot be combined with a subquery rule.
Is there a way to explicitly enable text activation within the subquery rule?
Hi, we are developing a cross-platform library for creating desktop applications in C++: https://github.com/aseprite/laf
For this reason, in macOS, we cannot rely on the default NSApplication.run() event loop, so we decided to implement our custom event loop using the nextEventMatchingMask method.
Then, when a window is in fullscreen mode, for some reason the window stops receiving mouseMove events when the mouse pointer enters an area at the top of the window.
You can see this issue in action by trying the following example project:
https://github.com/martincapello/custom-event-loop-issue
This project just opens one window and uses a custom event loop, it displays the current mouse position at every mouseMove event received, and when the aforementioned area is entered it suddenly stops updating.
There is also a video showing how to reproduce it.
I was able to see that when the position stops updating, we still receive mouseMove events, but for a different window, a borderless window that is added to the NSApplication.windows collection when switching to fullscreen, and which seems to be taken the mouseMove events before reaching the main window.
Also, this issue doesn't happen when using the default NSApplication.run method, despite the borderless windows being added as well.
Our MacOS app has for some time been able to create so-called "add-on" services, which are dynamically written to individual bundles in ~/Library/Services/ (as opposed to being statically defined in the app's Info.plist). These worked fine for a long time until recently. They still appear in other app Services menus, but do not get as far as calling the specified instance method in our app. Does anyone know if add-on services are no longer supported? Perhaps due to some new security constraint? The documentation on app services in general seems to be out of date.
I did try copying the add-on service definition from the add-on plist into the app's Info.plist. That seemed to work, so the basic specification doesn't seem to have changed.