我正在尝试实现一个非常简单的文本编辑器,该编辑器既适用于macOS上的NSTextView
,也适用于iOS上的UITextView
。此文本编辑器有一个工具栏按钮“Section Break”,每次单击时都会在当前光标位置插入一个新部分。一节应该是:
在another question,我问如何解决第一个问题,不幸的是,我还没有找到答案(即使有一个solution that works on macOS only)。
但是,这个问题集中在第二个方面: 如何在文本视图中维护所有部分的列表,其中每个部分都能准确引用相关文本?
此任务的复杂性在于用户可以随时复制和粘贴或简单地编辑文本的任何部分。因此,我不能保留一个简单的段落数字或类似的东西。
NSTextStorage
并使用一组可变属性字符串作为其内部存储。然后我会使用NSTextAttachment
的一个特殊子类,并将其用作文本存储中的分节符指示符。问题是the text view only calls the following methods whenever the user edits text:
func replaceCharacters(in range: NSRange,
with str: String)
和
func setAttributes(_ attrs: [NSAttributedString.Key : Any]?,
range: NSRange)
第一种方法只传递没有任何属性的普通字符串,第二种方法只获取属性。这意味着在第一种方法中,我无法判断替换字符是否实际上应该是分节符,因此我无法决定在内部文本存储数组中何处创建新元素。
在第二种方法中,我不知道用户是否实际添加了新文本(在这种情况下,我必须在我的文本存储数组中为每个分节符属性添加一个新元素),或者如果用户只是更改了现有的一些属性text(在这种情况下,之前已经创建了新的数组元素)。NSLayoutManager
进行子类化,但是关于它的文档很薄,对我来说似乎是错误的地方。 (毕竟,布局管理器的职责是文本的布局,而不是跟踪其结构。)如何在文本视图中维护所有部分的列表,其中每个部分都能准确引用相关文本?
首先确定一个部分是什么。直观地说,它似乎是一块连续的文本,其中几个串在一起形成一个完整的文档。但是试着比这更深入:一节中的所有文字都有相同的风格吗?相同的利润率?您是否真的需要跟踪部分,或者它是否也可以跟踪分节符?
接下来,既然你已经决定通过使用TextKit和NSTextView
购买UITextView
,并且由于你的需求超出了最常见的“带有一些可编辑文本的视图”这些类的用例,你需要了解更多关于底层类如何组合在一起。主要类别是NSTextStorage
,NSTextContainer
,NSLayoutManager
,当然还有NSTextView
(或UITextView
)。
了解TextKit类的工作原理以及它们为您提供的功能将帮助您了解如何实现“部分”概念。以下是您可以采取的几种方式示例:
UITextView
和NSTextView
之间的差异。假设您正在使用NSTextAttachment
子类,那么完成这两项任务应该相当容易,但让我们关注第二项:
你的第一个子类化NSTextStorage
解决方案是好的,但它将存储暴露给许多不需要的逻辑,我的建议是让你的(NS|UI)TextViewDelegate
实现func textDidChange(_ notification: Notification)
,当文本更改时你可以使用文本存储来枚举你的附件并保存稍后要引用的范围列表:
func textDidChange(_ notification: Notification)
{
self.textView.textStorage?.enumerateAttribute(NSAttributedString.Key.attachment, in: NSMakeRange(0, self.textView.textStorage!.length), options: [], using: {
(value:Any?, range:NSRange, stop:UnsafeMutablePointer<ObjCBool>) in
// If the value is one of your attachment save the range in a list
})
}
这样,无论用户在做什么(写,复制/粘贴等等),你都会有一个包含你的分隔符的有效范围列表:)
这只是一个例子,你重新构建列表的逻辑可以更精确,但我希望这可以让你开始。
我会玩NSRange。它可能与您的方法无关,但可以用作建议。
参考你的要点。
视觉上可识别为一个部分(通过在两个后续部分之间添加可视分隔符并可能添加一些垂直空白)可参考。用户应该能够在编辑器中看到所有部分的列表,并通过单击该列表中的项目,文本视图应立即滚动到该部分的开头。
UIButtons可以作为textView中的部分添加。按钮的宽度应该是textView中线条的宽度。
优点: