Post

Replies

Boosts

Views

Activity

Reply to NSTableView.clickedRow sometimes is greater than number of rows
It would still be great if an Apple engineer could comment on whether this can indeed happen (and when) or if it's likely a macOS issue. Not only was clickedRow outside of the valid index range, but it was also not -1, and I thought that it would only be set for the duration of table view action methods (e.g. context menu item actions, double click action), not during methods called by buttons outside of the table view.
Topic: UI Frameworks SubTopic: AppKit Tags:
Apr ’25
Reply to NSTableView.clickedRow sometimes is greater than number of rows
I'm not sure anymore, but it's possible that it had two rows at some point. Then I would have clicked a button that changed the data source array and the table view would have been reloaded with reloadData(). And in the end I clicked another button to add a new item to the data source array and insert the corresponding row in the table view, but since clickedRow was invalid, it crashed. I have been using NSTableView for different apps for more than 10 years now so I have some experience with it, and the implementation in this case is quite simple. array is the one that can gets swapped whenever category changes, and otherArray is another one that is displayed when no category is selected. func numberOfRows(in tableView: NSTableView) -> Int { return array.isEmpty ? 0 : category.map({ array[$0]?.count ?? 0 }) ?? otherArray.count } func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? { let cell = MyTableCellView() ... return cell } func tableView(_ tableView: NSTableView, rowActionsForRow row: Int, edge: NSTableView.RowActionEdge) -> [NSTableViewRowAction] { ... } func tableView(_ tableView: NSTableView, shouldSelectRow row: Int) -> Bool { ... } func tableViewSelectionDidChange(_ notification: Notification) { ... }
Topic: UI Frameworks SubTopic: AppKit Tags:
Apr ’25
Reply to Printing NSTextStorage over multiple UITextView produces weird results
Thanks, that almost solves all of my issues. On macOS, and when showing the text views directly in the iOS view hierarchy, using the page break character produces the expected result. For example with the string (0..<1).map({ "\($0)" }).joined(separator: "\n") + String(UnicodeScalar(12)) + (0..<5).map({ "\($0)" }).joined(separator: "\n") the view hierarchy looks like this: But when printing them, the text views seem to overlap. I'm afraid this is now a question for a printing expert. The code below produces both the view hierarchy and the print preview. class ViewController: UIViewController { override func viewDidAppear(_ animated: Bool) { let printController = UIPrintInteractionController.shared let printPageRenderer = PrintPageRenderer() printController.printPageRenderer = printPageRenderer printController.present(animated: true) { _, _, error in if let error = error { print(error.localizedDescription) } } var y = 0.0 for textView in printPageRenderer.createTextViews.textViews { textView.frame.origin = CGPoint(x: 0, y: y) view.addSubview(textView) y += textView.frame.height } } } class CreateTextViews: NSObject, NSLayoutManagerDelegate { let pageSize: CGSize let textStorage: NSTextStorage private let layoutManager = NSLayoutManager() private(set) var textViews = [UITextView]() override init() { pageSize = CGSize(width: 100, height: 100) textStorage = NSTextStorage(string: (0..<1).map({ "\($0)" }).joined(separator: "\n") + String(UnicodeScalar(12)) + (0..<5).map({ "\($0)" }).joined(separator: "\n"), attributes: [.font: UIFont.systemFont(ofSize: 30)]) super.init() layoutManager.delegate = self textStorage.addLayoutManager(layoutManager) if textStorage.length > 0 { let glyphRange = layoutManager.glyphRange(forCharacterRange: NSRange(location: textStorage.length - 1, length: 0), actualCharacterRange: nil) layoutManager.textContainer(forGlyphAt: glyphRange.location, effectiveRange: nil) } for textContainer in layoutManager.textContainers { let textView = UITextView(frame: CGRect(origin: .zero, size: pageSize), textContainer: textContainer) textViews.append(textView) } } func layoutManager(_ layoutManager: NSLayoutManager, didCompleteLayoutFor textContainer: NSTextContainer?, atEnd layoutFinishedFlag: Bool) { if textContainer == nil { addPage() } } private func addPage() { let textContainer = NSTextContainer(size: pageSize) layoutManager.addTextContainer(textContainer) } } class PrintPageRenderer: UIPrintPageRenderer { let createTextViews = CreateTextViews() override var numberOfPages: Int { if !Thread.isMainThread { return DispatchQueue.main.sync { [self] in numberOfPages } } printFormatters = nil var page = 0 for textView in createTextViews.textViews { let printFormatter = textView.viewPrintFormatter() printFormatter.maximumContentHeight = createTextViews.pageSize.height addPrintFormatter(printFormatter, startingAtPageAt: page) page += 1 } return page } }
Topic: UI Frameworks SubTopic: UIKit Tags:
Apr ’25
Reply to Printing NSTextStorage over multiple UITextView produces weird results
Thanks for the suggestion, that solves the issue with the reversed pages. But each text view still contains the actual text I would expect it to contain, plus the text of all the following ones. class PrintPageRenderer: UIPrintPageRenderer, NSLayoutManagerDelegate { var pageSize: CGSize! var textStorage: NSTextStorage! private let layoutManager = NSLayoutManager() private var textViews = [UITextView]() override var numberOfPages: Int { if !Thread.isMainThread { return DispatchQueue.main.sync { [self] in numberOfPages } } printFormatters = nil layoutManager.delegate = self textStorage.addLayoutManager(layoutManager) if textStorage.length > 0 { let glyphRange = layoutManager.glyphRange(forCharacterRange: NSRange(location: textStorage.length - 1, length: 0), actualCharacterRange: nil) layoutManager.textContainer(forGlyphAt: glyphRange.location, effectiveRange: nil) } for textContainer in layoutManager.textContainers { let textView = UITextView(frame: CGRect(origin: .zero, size: pageSize), textContainer: textContainer) textViews.append(textView) } for textView in textViews { let _ = layoutManager.glyphRange(for: textView.textContainer) } var page = 0 for textView in textViews { let printFormatter = textView.viewPrintFormatter() addPrintFormatter(printFormatter, startingAtPageAt: page) page += printFormatter.pageCount } return page } func layoutManager(_ layoutManager: NSLayoutManager, didCompleteLayoutFor textContainer: NSTextContainer?, atEnd layoutFinishedFlag: Bool) { if textContainer == nil { addPage() } } private func addPage() { let textContainer = NSTextContainer(size: pageSize) layoutManager.addTextContainer(textContainer) } }
Topic: UI Frameworks SubTopic: UIKit Tags:
Apr ’25
Reply to Force NSDocument save panel to select most specific type in format popup button
For instance take the following code: class AppDelegate: NSObject, NSApplicationDelegate { func applicationDidFinishLaunching(_ aNotification: Notification) { DispatchQueue.main.async { let document = try! NSDocumentController.shared.makeUntitledDocument(ofType: "net.daringfireball.markdown") print(document.fileType!, UTType(document.fileType!)!) NSDocumentController.shared.addDocument(document) document.makeWindowControllers() document.windowControllers[0].window!.title = "Markdown" document.showWindows() } } } And Info.plist: <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>UTImportedTypeDeclarations</key> <array> <dict> <key>UTTypeConformsTo</key> <array> <string>public.plain-text</string> </array> <key>UTTypeDescription</key> <string>Markdown</string> <key>UTTypeIcons</key> <dict/> <key>UTTypeIdentifier</key> <string>net.daringfireball.markdown</string> <key>UTTypeTagSpecification</key> <dict> <key>public.filename-extension</key> <array> <string>md</string> <string>mdown</string> <string>markdown</string> <string>text</string> </array> <key>public.mime-type</key> <array> <string>text/markdown</string> <string>text/x-markdown</string> <string>text/x-web-markdown</string> </array> </dict> </dict> </array> <key>CFBundleDocumentTypes</key> <array> <dict> <key>CFBundleTypeName</key> <string>Plain Text</string> <key>CFBundleTypeRole</key> <string>Editor</string> <key>LSHandlerRank</key> <string>Default</string> <key>LSItemContentTypes</key> <array> <string>public.plain-text</string> </array> <key>NSDocumentClass</key> <string>$(PRODUCT_MODULE_NAME).Document</string> <key>NSUbiquitousDocumentUserActivityType</key> <string>org.desairem.uFocus.OpenDocument</string> </dict> <dict> <key>CFBundleTypeName</key> <string>Markdown</string> <key>CFBundleTypeRole</key> <string>Editor</string> <key>LSHandlerRank</key> <string>Default</string> <key>LSItemContentTypes</key> <array> <string>net.daringfireball.markdown</string> </array> <key>NSDocumentClass</key> <string>$(PRODUCT_MODULE_NAME).Document</string> <key>NSUbiquitousDocumentUserActivityType</key> <string>org.desairem.uFocus.OpenDocument</string> </dict> </array> </dict> </plist> After making sure that the document with the window title Markdown is active, press Command-S to open the default save panel: Plain Text is selected instead of Markdown.
Topic: UI Frameworks SubTopic: AppKit Tags:
Apr ’25
Reply to Change NSTextView Position
I'm interested in adding a margin to a NSTextView by wrapping it in a custom view or adding sibling views, but wasn't able to get the scroll view to keep showing the entire text view. Would you mind sharing your code?
Topic: UI Frameworks SubTopic: AppKit Tags:
Apr ’25
Reply to How to debug Quick Look Preview Extension
Unfortunately this is an ongoing issue and I still don't know how to debug my app's Quick Look Preview Extension. I tried creating a default Xcode project and I can debug it by running the Extension target, selecting Finder in the Xcode dialog, then selecting a file in the Finder and pressing Spacebar. But this doesn't work for my already existing app: the preview just shows a spinning progress indicator, even when using the exact same code in PreviewViewController.swift. The only notable difference I can think of is that my app has an iCloud entitlement. Removing that entitlement and changing the bundle identifiers of the host app and the extension to those used in the sample app get rid of the endless progress indicator... but instead cause an immediate error to be shown at the top of the preview window: "Couldn’t find extension org.domain.problem.QuickLook” I would appreciate any help, as currently the only way to test the Preview Extension is to publish it on the App Store, download it, and see if it behaves as expected, and if not, try something different and repeat the cycle. I created FB17066774.
Topic: App & System Services SubTopic: General Tags:
Apr ’25