UITextView与NSTextContainer尺寸不匹配及白色区域消除方案咨询
嘿,我之前在做TextKit自定义排版的时候也踩过几乎一模一样的坑!结合你说的情况,应该是三个核心问题没处理好:文本容器尺寸不对、背景色只覆盖了字符范围、还有默认内边距导致的“白色边框”。给你几个具体的解决思路:
1. 先把NSTextContainer和NSTextView的尺寸对齐
默认情况下,NSTextContainer的size是(1000, 1000),如果你的NSTextView frame比这个小,就会出现容器尺寸和视图不匹配的问题,导致多余的空白区域。你需要在视图布局更新的时候同步容器尺寸:
// macOS用viewDidLayout,iOS用layoutSubviews override func viewDidLayout() { super.viewDidLayout() guard let textView = yourTextView else { return } // 考虑textContainerInset的影响,把容器尺寸设为视图内边后的大小 let insetBounds = textView.bounds.inset(by: textView.textContainerInset) textView.textContainer?.size = insetBounds.size // 如果是滚动的文本视图,高度可以设为无限大,让文本自动换行 if textView.isScrollEnabled { textView.textContainer?.size.height = CGFloat.greatestFiniteMagnitude } }
2. 让背景色覆盖整个文本区域(包括最后字符后的空白)
你之前只给字符串长度对应的range设置backgroundColor,自然只会覆盖字符本身的范围。要覆盖文本实际占用的整个排版区域,有两种靠谱的方式:
方式一:自定义NSLayoutManager绘制背景
重写NSLayoutManager的drawBackground(forGlyphRange:at:)方法,先绘制文本实际占用区域的背景,再绘制原有的文本背景:
class CustomLayoutManager: NSLayoutManager { var customBackgroundColor: NSColor = .yourDesiredColor // macOS用NSColor,iOS用UIColor override func drawBackground(forGlyphRange glyphsToShow: NSRange, at origin: CGPoint) { guard let textContainer = textContainer(forGlyphAt: glyphsToShow.location, effectiveRange: nil) else { super.drawBackground(forGlyphRange: glyphsToShow, at: origin) return } // 获取文本实际占用的区域,而非整个容器 let usedRect = usedRect(for: textContainer) let drawRect = usedRect.offsetBy(dx: origin.x, dy: origin.y) customBackgroundColor.setFill() NSRectFill(drawRect) // macOS用这个,iOS用UIRectFill(drawRect) // 如果需要保留原来字符范围的背景,就调用父类方法;不需要的话可以去掉 super.drawBackground(forGlyphRange: glyphsToShow, at: origin) } }
记得把这个自定义的layoutManager关联到你的textView上。
方式二:直接计算文本排版范围绘制背景
在NSTextView的draw(_:)方法里,计算文本实际占用的rect,然后手动绘制背景:
override func draw(_ dirtyRect: NSRect) { super.draw(dirtyRect) guard let layoutManager = layoutManager, let textContainer = textContainer else { return } let usedRect = layoutManager.usedRect(for: textContainer) let drawRect = usedRect.offsetBy(dx: textContainerInset.left, dy: textContainerInset.top) NSColor.yourDesiredColor.setFill() NSRectFill(drawRect) }
3. 去掉白色边框
那个“白色边框”大概率是NSTextContainer默认的lineFragmentPadding导致的——这个属性默认是5,会在文本和容器边缘之间留空白,如果你textView的背景是白色,就会看起来像边框。直接把它设为0就行:
yourTextView.textContainer?.lineFragmentPadding = 0
另外,如果你不需要textView本身的背景,记得把drawsBackground设为false,避免和你自定义的背景冲突:
yourTextView.drawsBackground = false
把这几步都试一下,应该就能解决你遇到的所有问题了!
内容的提问来源于stack exchange,提问作者Greg

