我正在尝试实现一个非常简单的文本编辑器,该编辑器应既可以在macOS上与NSTextView
一起在iOS上也可以与UITextView
一起使用。该文本编辑器具有工具栏按钮“ 分节符”,该按钮每次单击时都会在当前光标位置插入一个新节。一节应为:
视觉上可识别的作为一个部分(通过在两个后续部分之间添加视觉分隔符并可能添加一些垂直空白),
可引用。用户应该能够在编辑器中看到所有部分的列表,然后单击该列表中的一个项目,文本视图应立即滚动到该部分的开头。
在another question中,我问如何解决第一个问题,不幸的是,我还没有找到答案(即使 是solution that works on macOS only)
但是,这个问题集中在 second 方面:
如何在文本视图中维护所有部分的列表,其中每个部分都能准确引用相关文本?
此任务的复杂性在于用户可以随时复制和粘贴或简单地编辑文本的任何部分。因此,我无法保留简单的段落编号数组或类似的内容。
我曾经有一个想法是将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)
第一个方法仅传递不带任何属性的纯字符串,第二个方法仅获取属性。这意味着在第一种方法中,我无法确定替换字符是否实际上应该是分节符,因此我无法确定此时在内部文本存储数组中的哪里创建新元素。
在第二种方法中,我不知道用户是否实际添加了新文本(在这种情况下,我将不得不在我的文本存储数组中为每个分节符属性添加一个新元素)还是用户只是简单地更改了一些内容现有文本的属性(在这种情况下,先前已经创建了新的数组元素)。
我还考虑过在表视图或堆栈视图中使用多个文本视图的想法。但是,这不起作用,因为它将使用户无法选择(和删除)多个后续部分中的文本。
最后,我尝试了对NSLayoutManager
进行子类化,但是关于它的文档确实很薄,对我来说似乎是错误的地方。 (毕竟,布局经理的职责是文本的布局,而不是跟踪其结构。)
答案 0 :(得分:2)
如何在我的文本视图中维护所有部分的列表,其中每个部分都能准确引用相关文本?
首先确定是什么。直观地,这似乎是一块连续的文本,其中的一些文本串在一起形成一个完整的文档。但是,尝试做得比这更深入:一节中的所有文本是否都具有相同的样式?利润率一样吗?您是否真的需要完全跟踪部分,还是只跟踪部分中断也可以吗?
接下来,由于您已经决定通过使用NSTextView
和UITextView
来购买TextKit,并且由于您的需求超出了最常见的“可编辑视图这些类的用例,您需要了解更多有关基础类如何组合的信息。主要类别为NSTextStorage
,NSTextContainer
,NSLayoutManager
,当然还有NSTextView
(或UITextView
)。
了解TextKit类的工作方式以及它们为您提供的便利将帮助您弄清楚如何实现“节”的概念。这是您可以使用的几种示例:
构建自己的文本视图。文本视图所做的大部分工作实际上是由其他类完成的:文本容器,布局管理器和文本存储。您可以构建自己的包含一系列文本容器的视图,以便每个部分由不同的容器表示,每个容器都有自己的布局管理器和存储对象。显然,这是最繁琐的工作,但是它将对部分的显示方式提供很多控制。另外,如果您构建了在两种平台上均可使用的视图,则不必担心UITextView
和NSTextView
之间的差异。
让文本视图完成所有工作。部分的轻量级实现可能涉及为文本视图或文本存储对象设置委托。编辑发生时,这两个方法都会调用委托方法,因此,如果您的委托人维护代表断面的索引或范围列表,则它可以使用这些方法来更新其断面边界数据。您仍然需要弄清楚如何以视觉方式显示截面边界,但这可以像每个截面边界处的inserting an image一样简单。
答案 1 :(得分:2)
假设您使用的是add_31
Reshape_165
concat_52/axis
concat_52
concat_53/axis
concat_53
子类,那么完成这两项任务应该很容易,但是让我们关注第二个:
您的第一个子类化NSTextAttachment
的解决方案是好的,但是它使存储暴露于很多不需要的逻辑,我的建议是让您的NSTextStorage
来实现(NS|UI)TextViewDelegate
更改文本后,您可以使用文本存储来枚举附件并保存范围列表供以后参考:
func textDidChange(_ notification: Notification)
这种方式与用户的操作(书写,复制/粘贴等)无关紧要,您将始终获得包含分隔符的有效范围列表:)
这只是一个例子,您重新构建列表的逻辑可以更加完善,但是我希望这可以帮助您入门。
答案 2 :(得分:-1)
我会和NSRange一起玩。它可能与您的方法无关,但可以用作建议。
参考要点。
在视觉上可识别为一个部分(通过在两个后续部分之间添加视觉分隔符并可能添加一些垂直空白) 可以。 用户应该能够在编辑器中看到所有部分的列表,并且通过单击列表中的项目,文本视图应立即滚动到该部分的开头。
UIButton可以作为部分添加到textView中。按钮的宽度应为textView内部行的宽度。
优点: