I have an NSTextView
with some exclusion rects so I can shift the text vertically without having to resort to adding newlines to the string. However, I've noticed that the exclusion rect is rather limited or perhaps even buggy because I cannot shift the text vertically more than ~45% of the textview's height. I have found a workaround where I increase the textview's height by 2x but this feels gross and I'd rather do it "properly"
In the image above, I have three text views (from left to right)
NSScrollView
, 2x heightNSScrollView
encapsulation. 2x heightThe exclusion rect is drawn with a CAShapeLayer
and you can see that "hello world new world" isn't properly positioned outside of the exclusion rect.
I tried all three examples to make sure I wasn't missing something with regards to IB default settings or the dynamics of the text view when encapsulated in an NSScrollView
(Im new to AppKit and TextKit), however all 3 examples exhibit the same bug.
(Each time the slider moves on the bottom right, it will update their text rect)
label.stringValue = "excl: \(sender.integerValue): height: \(view.frame.height)"
print(sender.integerValue)
let exclHeight: CGFloat = CGFloat(slider.integerValue)
[aTextView, bTextView, cTextView]
.compactMap { $0 }
.forEach {
let rect = CGRect(
x: 5,
y: 5,
width: aTextView.frame.width - 10,
height: exclHeight)
$0.wantsLayer = true
$0.textContainer?.exclusionPaths.removeAll()
$0.textContainer?.exclusionPaths.append(.init(rect: rect))
$0.layer?.sublayers?.forEach {
$0.removeFromSuperlayer()
}
let layer = CAShapeLayer()
layer.frame = rect
layer.backgroundColor = NSColor.blue.withAlphaComponent(0.2).cgColor
layer.borderWidth = 1
layer.borderColor = NSColor.white.cgColor
$0.layer?.addSublayer(layer)
}
}
The problem seems to be that the exclusionPath is before the first line.
Just playing around with the parameters, a two line sample text with the rect y
positioned after the first line works without any problems.
So it looks like the issue is calculating the container height when it starts with a exclusionPaths in -[NSLayoutManager usedRectForTextContainer:]
@interface LOLayoutManager : NSLayoutManager
@end
@implementation LOLayoutManager
- (NSRect)usedRectForTextContainer:(NSTextContainer *)container
{
NSRect rect = [super usedRectForTextContainer:container];
NSRect newRect = NSMakeRect(0, 0, NSMaxX(rect), NSMaxY(rect));
return newRect;
}
@end
Instead of returning y
position from the exclusionPaths and the line fragments height, this returns a big rect starting at 0, 0
. This should work as long as the NSTextView only contains one text container.