(Here's a supplemental reply to my previous one.)
5. NSStackView on macOS 10.9 might have serious rendering efficiency issues. At least, in VMware Fusion, its rendering speed is slower than SwiftUI.
Post
Replies
Boosts
Views
Activity
This issue has been solved. Here's the things to beware of when migrating NSStackView things to macOS 10.9:
(Not talking about macOS 10.10 - 10.12 due to lack of tests.)
macOS 10.9 doesn't draw shadow for opaque windows.
In macOS 10.9, even if you set .isOpaque = true and the background color to NSColor.clear, it won't make clear backgrounds like what macOS 10.13 and later behaves.
NSStackView in macOS 10.9 bugs, requiring every of its components to be constraint-set in order to make the entire view size looks as big as what behaves on macOS 10.13 and later.
The subViews of an NSStackView might be more than the views you actually added into it. Instead of directly enumerating the .subviews of an NSStackView, you create another Swift array to track these subviews before adding them to the NSStackView instance.
I solved this.
Prerequisites:
NSTableView instance must have its DataSource object assigned:
myNSTableViewInstance.dataSource = ***
The DataSource object conforms to NSTableViewDataSource protocol, having two necessary functions implemented (will discuss below).
Whenever appropriate (e.g.: .windowDidLoad(), .viewDidLoad(), etc.), register the .registerForDraggedTypes for NSTableView instance.
In the prerequisite 3, the registration method is:
// Constructing the NSPasteboard.PasteboardType.
tblClients.registerForDraggedTypes([.init(rawValue: kUTTypeFileURL as String)]) // Compatible with macOS 10.9 Mavericks.
tblClients.registerForDraggedTypes([.fileURL]) // Since macOS 10.13 High Sierra.
In the prerequisite 2, the registration method is:
// Constructing the NSPasteboard.PasteboardType.
.init(rawValue: kUTTypeApplicationBundle as String)
We start implementing. Suppose that (in this case):
the DataSource object is the WindowController itself.
the name of the NSTableView instance is tblClients.
Since the two functions mentioned above shares a lot of things, we extract them as a standalone function with lambda expressions:
/// See whether the incoming NSDraggingInfo is an array of App Bundles.
/// - Parameters:
/// - info: the incoming NSDraggingInfo object。
/// - onError: On error perform this given lambda expression.
/// - handler: When conditions are met, perform this given lambda expression to process the URL array.
private func validatePasteboardForAppBundles(
neta info: NSDraggingInfo, onError: @escaping () -> Void?, handler: @escaping ([URL]) -> Void?
) {
let board = info.draggingPasteboard
let type = NSPasteboard.PasteboardType(rawValue: kUTTypeApplicationBundle as String)
let options: [NSPasteboard.ReadingOptionKey: Any] = [
.urlReadingFileURLsOnly: true,
.urlReadingContentsConformToTypes: [type],
]
guard let urls = board.readObjects(forClasses: [NSURL.self], options: options) as? [URL], !urls.isEmpty else {
onError()
return
}
handler(urls)
}
Finally, the two implementations.
The one to check whether the dragged file object is the one we need to let the NSTableView response to:
func tableView(
_: NSTableView, validateDrop info: NSDraggingInfo, proposedRow _: Int,
proposedDropOperation _: NSTableView.DropOperation
) -> NSDragOperation {
var result = NSDragOperation.copy
validatePasteboardForAppBundles(
neta: info, onError: { result = .init(rawValue: 0) }, // NSDragOperationNone。
handler: { _ in }
)
return result
}
The handling process once the mouse click releases:
func tableView(
_: NSTableView, acceptDrop info: NSDraggingInfo,
row _: Int, dropOperation _: NSTableView.DropOperation
) -> Bool {
var result = true // Outer report: Operation successful or not.
validatePasteboardForAppBundles(
neta: info, onError: { result = false } // NSDragOperationNone。
) { theURLs in
var dealt = false // Inner report. Will be true if at least one URL entry is processed with successful result.
theURLs.forEach { url in
// See whether this is an app bundle with valid bundleID:
guard let bundle = Bundle(url: url), let bundleID = bundle.bundleIdentifier else { return }
// Performing customized operations toward this bundleID string:
self.applyNewValue(bundleID, highMitigation: true)
// Mark this operation as "successful".
dealt = true
}
// Tell the outer report that whether we did at least one successful operation.
result = dealt
}
defer { if result { self.tblClients.reloadData() } }
return result
}
This should work unless there are other things screwed up.
$ EOF.
You need to talk to your lawyer directly, keeping all of your communication records with Apple as evidences. Complaining this in developer forum is not helpful at all, I am afraid: Developer forum is considered a place for talking about code-level problems.
Replying to @Claude31 :
There's no results printed in the Xcode output console, indicating that the buttonPressed(_:) is not called at all.
(I dunno why my previous comment disappeared again and again, hence this standalone reply.)
This issue is simple: Bind the TextEditor to a String (@Binding) which is NOT a @State.
P.S.: I know AttributedString() initiated with NSAttributed string can do this. However, this leads to serious performance issues when rendering dozens of labels at once.