Post

Replies

Boosts

Views

Activity

Reply to How do I force wrapping to isolate an attachment on a line?
Thanks. That is one of the things I already tried. You are right that this achieves the goal of having the attachment alone on the line. The problem with that approach is that you then can't put the cursor behind the attachment. The cursor only goes right at the end of the line. I note that apps like Apple Notes can put the cursor behind the attachment (not at the end of the line), so I assume it is possible.
Jun ’20
Reply to How do I force wrapping to isolate an attachment on a line?
The method can return the wide (text container) width when asked for a position training other characters in the line to push the attachment out to the next line. It can return its natural size in other cases. This indeed works. It was one of the approaches I had tried. The problem is then the trailing text... In order to push out characters following the attachment, you can use layoutManager:shouldBreakLineByWordBeforeCharacterAtIndex:. I tried this, but it seems unrelated to the problem. This method seems to be about wrapping policy; it doesn't give you a way to actually force a wrap at any given character or word boundary, which is more what I need. I also experimented with layoutManager(_:, shouldSetLineFragmentRect:, lineFragmentUsedRect:, baselineOffset:, in:, forGlyphRange:), but couldn't get that working either. I wish there was just a method very similar to the attachment bounding rect one for standard text layout. Then it would be trivial. But I can't find a way to intervene in the text layout to cause it to wrap. I think inserting line breaks (which is what I did up until now) is probably the only way.
Jul ’20
Reply to How do I force wrapping to isolate an attachment on a line?
I apologize. You are quite correct that you can use layoutManager:shouldBreakLineByWordBeforeCharacterAtIndex: to get the trailing text to wrap. I just needed to "wrap" my head around what that method was doing. For anyone wondering, the trick is to keep returning false from the delegate method until it passes in the word just after your attachment, at which point you return true. The layout manager will start with the last word on the line, and work back. By returning false, you are telling it you want to break earlier in the line. When you reach the point you actually want to break, you return true to indicate to make the wrap.
Jul ’20
Reply to How do I force wrapping to isolate an attachment on a line?
Spoke too soon, as it turns out. Using layoutManager:shouldBreakLineByWordBeforeCharacterAtIndex: is effective if the trailing text is longer than the remaining space on the line. In this case, it does what I described, and you can choose where to put the line break. The problem is, if the trailing text is short, and fits on the line after the attachment, the layout manager doesn't even look for a line break. It isn't needed, and so there is no way to intervene. Think inserting line break characters is the most foolproof approach.
Jul ’20
Reply to "No options were provided for this parameter" in Edit Widget menu
I wasted too much time on this problem, because I missed one very important step. In case it saves someone else some pain, when you provide your widget with dynamic options, you must do that in a separate Intent target in your app. I had just added the IntentHandler straight to the widget extension target, and couldn't figure out why it wasn't working. Dumb mistake, but also one that leads to the above behavior.
Jun ’22
Reply to Catalyst Developer ID support for Shared with You
We are seeing the same thing in an AppKit macOS app that is distributed outside the Mac App Store. We sign with the Developer ID, but it says that Shared with You is not in the provisioning profile. I saw this a few years ago too, only that was with Sign in with Apple. Apple later confirmed that indeed Sign in with Apple is not supported outside the Mac App Store, and the provisioning portal just strips that entitlement out for non-MAS provisioning profiles. I am seeing this very same behavior with Shared with You. My conclusion is that Shared with You only works with Mac App Store apps, and that you will be able to get it working by distributing it via TestFlight. If anyone from Apple can confirm that indeed Shared with You is not supported outside the Mac App Store, it would be appreciated. (This sort of thing should be documented; it would have saved me weeks last time).
Sep ’22
Reply to NSAttributedString doesn't format HTML tables properly on Sonoma
We are seeing this too. What seems to be happening is that the NSTextTableBlock objects representing each cell have different table pointers. In other words, each cell gets put into its own table, leading them to appear under one another. Interestingly, if you convert this attributed string to RTF, and back again, you get something else. You still get separate tables for each cell you input, but it creates empty cells so that the original cells at least have the correct column index, like this... Conclusion: the HTML input seems to generate a new table object for each cell, instead of a single table object for all cells. Submitted this a while back under FB13254682. Have added above FB to that.
Nov ’23
Reply to NSAttributedString doesn't format HTML tables properly on Sonoma
I have developed a workaround. Basically, you can copy each cell, using the first table object found for a given set of table cells. It's ugly, but does seem to work. Including some code below to show how you can do it. class ViewController: NSViewController { @IBOutlet var textView: NSTextView! override func viewDidAppear() { super.viewDidAppear() let html = """ <html> <head> <title>Testing Tables</title> <style> td { border: 1px solid; padding: 10px; } </style> </head> <body> <table> <tr> <td>Cell 1</td> <td>Cell 2</td> </tr> <tr> <td>Cell 3</td> <td>Cell 4</td> </tr> </table> </body> </html> """ let data = html.data(using: .utf8)! let string = NSMutableAttributedString(html: data, documentAttributes: nil)! let wholeRange = NSMakeRange(0, string.length) var table: NSTextTable? string.enumerateAttribute(.paragraphStyle, in: wholeRange) { value, range, _ in guard let paragraphStyle = value as? NSParagraphStyle else { return } let tableBlocks = paragraphStyle.textBlocks.compactMap { $0 as? NSTextTableBlock } for block in tableBlocks { if table == nil { table = block.table } // Keep first table found let newBlock = block.copy(for: table!) let newStyle = paragraphStyle.mutableCopy() as! NSMutableParagraphStyle newStyle.textBlocks = [newBlock] string.addAttribute(.paragraphStyle, value: newStyle, range: range) } if tableBlocks.isEmpty { table = nil } } textView.textStorage!.setAttributedString(string) } } extension NSTextTableBlock { func copy(for table: NSTextTable) -> NSTextTableBlock { let newBlock = NSTextTableBlock(table: table, startingRow: startingRow, rowSpan: rowSpan, startingColumn: startingColumn, columnSpan: columnSpan) newBlock.backgroundColor = backgroundColor newBlock.verticalAlignment = verticalAlignment for edge: NSRectEdge in [.minX, .minY, .maxX, .maxY] { for layer: NSTextBlock.Layer in [.border, .margin, .padding] { let width = width(for: layer, edge: edge) let valueType = widthValueType(for: layer, edge: edge) newBlock.setWidth(width, type: valueType, for: layer, edge: edge) } newBlock.setBorderColor(borderColor(for: edge), for: edge) } for dimension: NSTextBlock.Dimension in [.height, .maximumHeight, .maximumWidth, .minimumHeight, .minimumWidth, .width] { let value = value(for: dimension) let valueType = valueType(for: dimension) newBlock.setValue(value, type: valueType, for: dimension) } return newBlock } }
Nov ’23