What is the recommended way to render a divider line in a NSTextView?

I would like to render a divider line in a NSTextView (like the <div> HTML item) on macOS 10.13 or later. I use Objective-C.

I've searched for solutions and it looks like the recommended solution is to use a NSTextAttachmentCell. There was supposedly an example provided on the cocoa-dev mailing list but the archives for this useful mailing list are gone.

The issue I'm observing with the NSTextAttachmentCell solution is that it is working almost well with a simple NSTextView example but when used in a more complex application, the rendering of the divider is still fine but there's a huge vertical gap between the divider and the text below the divider.

The online documentation for the NSTextAttachmentCell class is not really helpful (and contains typos: "With the exceptions of cellBaselineOffset, attachment, and attachment, all of these methods are implemented by the NSCell class.For general information on text attachments, " -> "attachment" twice, no space between "class." and "For general").

Here's the simple code I'm currently testing for the attachment cell:

@implementationMyDividerTextAttachmentCell

- (NSRect)cellFrameForTextContainer:(NSTextContainer *)textContainer proposedLineFragment:(NSRect)lineFrag glyphPosition:(NSPoint)position characterIndex:(NSUInteger)charIndex
{
	return NSMakeRect(lineFrag.origin.x, lineFrag.origin.y, textContainer.size.width, 10);
}

- (void)drawWithFrame:(NSRect)cellFrame inView:(nullable NSView *)controlView
{
	[[NSColor darkGrayColor] setStroke];
	
	[NSBezierPath strokeLineFromPoint:NSMakePoint(0, NSMidY(cellFrame)) toPoint:NSMakePoint(NSMaxX(cellFrame), NSMidY(cellFrame))];
}

[Q] Is there an obvious reason why there would be a huge gap (think 200 pixels/points) below the divider? What is the recommended way to render a divider line in a NSTextView if it's not NSTextAttachmentCell.

Are you actually running on macOS 10.13?

I tried to do this using NSTextAttachmentViewProvider and I couldn't get it to work. That requires macOS 12. But apparently, this TextKit 1/2 logic is quite flaky and delicate. So Perhaps you're actually running on TextKit 2 and that's what you should be seeing due to some bug. Once something is a lightly documented at custom text attachments, it's risky.

I'll try again with an NSTextAttachmentCell. Just because views are (theoretically) supported doesn't mean the cell shouldn't still work.

I think i found an answer for you. Someone asked the exact same question on Stack Overflow 14 years ago: https://stackoverflow.com/questions/8558853/nstextattachmentcell-is-a-mile-high

But I still have to debug my own text views. It looks like I may be inadvertenly triggering a TextKit 1 fallback. Thanks for opening this can-o-worms for me. It's better that I find out about it now before I ship.

Creating your own NSTextAttachment subclass and implementing the NSTextAttachmentContainer methods will be a better choice, because that is a more "modern" way than using NSTextAttachmentCell, given that your target platform is macOS 10.13+ (and so TextKit 2 is not a choice).

If you need to stick to NSTextAttachmentCell, yeah, try return NSMakeRect(0, 0, lineFrag.size.width, 10); in cellFrameForTextContainer:.... The method doesn't expect a frame in the text container's coordinate system; it expects the attachment bounds relative to the glyph's baseline position (i.e., origin is usually (0, 0) or (0, baselineOffset)). The documentation is indeed confusing.

Also, in drawWithFrame:inView:, try to draw from NSMinX(cellFrame) instead of 0 so it respects container padding.

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

I found and removed any use of NSLayoutManager. So I'm definitely on TextKit 2 now.

I'm using NSTextAttachmentView, but I discovered a couple of things that may also apply to NSTextAttachmentContainer.

First of all, it's good that you're using Objective-C. These APIs haven't been updated for modern Swift. I hacked it a couple of places but finally gave up and re-did it in Objective-C.

My first attempt worked fine on macOS 27, but then crashed on macOS 15. I figured out how to fix that.

But probably the most important thing is that you may have to figure this out on your own. For NSTextAttachmentView at least, none of this is documented. What documentation I found on the internet was completely wrong. And quite a bit of Apple's API is wrong too. I don't want to give away too much in this age of AI. But I was very pleased to see AIs be so confidently wrong.