使用 NSTextView/UITextView 排除 rects 不正确移动文本
Exclusion rects not shifting text properly with NSTextView/UITextView
我有一个带有一些排除项的 NSTextView
,这样我就可以垂直移动文本,而不必向字符串添加换行符。但是,我注意到排除矩形相当有限,甚至可能有错误,因为我不能将文本垂直移动超过 textview 高度的 ~45%。我找到了一个解决方法,我将 textview 的高度增加了 2 倍,但这感觉很恶心,我宁愿这样做 "properly"
在上图中,我有三个文本视图(从左到右)
- 以编程方式将其封装在
NSScrollView
、2x 高度 中
- 以编程方式使用
NSScrollView
封装。 2x 高度
- 使用界面生成器,常规高度。
排除矩形是用 CAShapeLayer
绘制的,您可以看到 "hello world new world" 没有正确定位在排除矩形之外。
我尝试了所有三个示例,以确保在封装在 NSScrollView
中时我没有遗漏有关 IB 默认设置或文本视图动态的内容(我是 AppKit 和 TextKit 的新手),然而,所有 3 个示例都出现了相同的错误。
更新文本排除矩形的代码
(每次滑块在右下角移动时,都会更新其文本矩形)
label.stringValue = "excl: \(sender.integerValue): height: \(view.frame.height)"
print(sender.integerValue)
let exclHeight: CGFloat = CGFloat(slider.integerValue)
[aTextView, bTextView, cTextView]
.compactMap { [=11=] }
.forEach {
let rect = CGRect(
x: 5,
y: 5,
width: aTextView.frame.width - 10,
height: exclHeight)
[=11=].wantsLayer = true
[=11=].textContainer?.exclusionPaths.removeAll()
[=11=].textContainer?.exclusionPaths.append(.init(rect: rect))
[=11=].layer?.sublayers?.forEach {
[=11=].removeFromSuperlayer()
}
let layer = CAShapeLayer()
layer.frame = rect
layer.backgroundColor = NSColor.blue.withAlphaComponent(0.2).cgColor
layer.borderWidth = 1
layer.borderColor = NSColor.white.cgColor
[=11=].layer?.addSublayer(layer)
}
}
问题似乎是 exclusionPath 在第一行之前。
只是玩弄参数,一个两行示例文本,其中 rect y
位于第一行之后,没有任何问题。
所以看起来问题是在 -[NSLayoutManager usedRectForTextContainer:]
中以 exclusionPaths 开头时计算容器高度
@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
不是从 exclusionPaths 和行片段高度返回 y
位置,而是这个 returns 从 0, 0
开始的大矩形。只要 NSTextView 只包含一个文本容器,这就应该有效。
我有一个带有一些排除项的 NSTextView
,这样我就可以垂直移动文本,而不必向字符串添加换行符。但是,我注意到排除矩形相当有限,甚至可能有错误,因为我不能将文本垂直移动超过 textview 高度的 ~45%。我找到了一个解决方法,我将 textview 的高度增加了 2 倍,但这感觉很恶心,我宁愿这样做 "properly"
在上图中,我有三个文本视图(从左到右)
- 以编程方式将其封装在
NSScrollView
、2x 高度 中
- 以编程方式使用
NSScrollView
封装。 2x 高度 - 使用界面生成器,常规高度。
排除矩形是用 CAShapeLayer
绘制的,您可以看到 "hello world new world" 没有正确定位在排除矩形之外。
我尝试了所有三个示例,以确保在封装在 NSScrollView
中时我没有遗漏有关 IB 默认设置或文本视图动态的内容(我是 AppKit 和 TextKit 的新手),然而,所有 3 个示例都出现了相同的错误。
更新文本排除矩形的代码
(每次滑块在右下角移动时,都会更新其文本矩形)
label.stringValue = "excl: \(sender.integerValue): height: \(view.frame.height)"
print(sender.integerValue)
let exclHeight: CGFloat = CGFloat(slider.integerValue)
[aTextView, bTextView, cTextView]
.compactMap { [=11=] }
.forEach {
let rect = CGRect(
x: 5,
y: 5,
width: aTextView.frame.width - 10,
height: exclHeight)
[=11=].wantsLayer = true
[=11=].textContainer?.exclusionPaths.removeAll()
[=11=].textContainer?.exclusionPaths.append(.init(rect: rect))
[=11=].layer?.sublayers?.forEach {
[=11=].removeFromSuperlayer()
}
let layer = CAShapeLayer()
layer.frame = rect
layer.backgroundColor = NSColor.blue.withAlphaComponent(0.2).cgColor
layer.borderWidth = 1
layer.borderColor = NSColor.white.cgColor
[=11=].layer?.addSublayer(layer)
}
}
问题似乎是 exclusionPath 在第一行之前。
只是玩弄参数,一个两行示例文本,其中 rect y
位于第一行之后,没有任何问题。
所以看起来问题是在 -[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
不是从 exclusionPaths 和行片段高度返回 y
位置,而是这个 returns 从 0, 0
开始的大矩形。只要 NSTextView 只包含一个文本容器,这就应该有效。