创建适用于iOS 7
文本编辑器我需要了解如何TextKit工作,我怎么可以用它来建立一个文本编辑器。我需要弄清楚如何仅绘制最终用户与可见文本进行交互,或确定如何仅将可用文本应用于可见文本而不将属性应用于processEditing方法中整个已更改的文本范围。
背景
的iOS 7推出了TextKit。我有一个完全实现TextKit的标记器和代码(参考Apple的TextKitDemo项目 - 下面提供了一个链接)...并且它可以工作。但是,它真的很慢。当文本被解析时,NSTextStorage会要求您在processEditing方法中在同一个线程上着色编辑文本的整个范围。将作业卸载到线程无济于事。这太简单了。我已经到了只能重新修改范围的地步,但是如果范围太大,这个过程会很慢。在某些情况下,整个文档可能会在更改后失效。
以下是我的一些想法。请让我知道,如果任何这些将工作或可能推动我在正确的方向。
1)多NSTextContainers
阅读看来我可以在NSLayoutManager内添加多个NSTextContainers的文档。我假设通过这样做,我应该能够定义不仅可以在NSTextContainer中绘制的行数,而且还应该能够知道哪个NSTextContainer对最终用户可见。我知道,如果我走这条路,我将需要投入很多时间才能看出这是否可行。初始测试表明您只需要一个NSTextContainer。所以我必须继承NSLayout的子类,或者在布局管理器确定哪些文本进入哪个文本容器时创建一个包装器。呸。另外,我不知道TextKit如何让我知道是时候绘制一个特定的NSTextContainer了......也许这不是它的工作原理!
2)无效范围瓦特/ NSLayoutManager
使用invalidateLayoutForCharacterRange无效LayoutManager的:actualCharacterRange :.但是,这实际上做了什么,它将如何减少文本归因阶段?它什么时候让我知道一个特定的文本需要突出显示?另外,我看到NSLayoutManager将懒洋洋地画出字形......怎么样?什么时候?这对我有什么帮助?我如何利用此调用,以便在实际布置文本之前将背景字符串归属?
3)覆盖NSLayoutManager drawGlyphsForGlyphRange:atPoint:方法。
我真的不想这样做。也就是说,在Mac OS X中,NSAttributedStrings具有临时属性的概念,其中样式信息仅用于演示。这加快了突出显示的过程!问题是,它不存在于iOS 7 TextKit框架中(或者它在那里,我只是不知道它)。我相信通过使用这种方法,它可以给我使用临时属性的相同类型的速度......因为我可以用这种方法回答所有布局,颜色和格式问题,而无需触及NSTextStorage归因串。唯一的问题是,我不知道该方法如何与NSLayoutManager类中提供的其他方法相关。它是否保持宽度和高度的状态? NSTextContainer太小时会修改它的大小吗?另外,它只为在文本缓冲区中添加的字符绘制字形。它不会重新绘制整个屏幕。只有它的一小部分...而且非常好。我对如何使用这个工具有一些想法......但我真的没有想要布置字形的愿望。这太方便了,我还没有找到一个很好的例子。
我将不胜感激任何帮助,你必须提供。
作为感谢,我列出了所有我已经使用在过去的几年中帮助我去,我现在在哪里,它们对您有帮助的希望的框架和参考。
语法高亮框架:
- http://colorer.sourceforge.net/
- https://github.com/MikeJ1971/Glint(不支持字符串,注释等)
- https://projects.gnome.org/gtksourceview/features.html(提供用于令牌生成一个体面的lib用GTK工作但也可能是。重新编写了不同的布局管理器)
- http://parsekit.com/(良好解析器。但是,你必须来包装API来创建一个状态机需要维修范围时)
- http://svn.gna.org/viewcvs/etoile/trunk/Etoile/Languages/LanguageKit/
- https://github.com/CodaFi/IDEKit(令人惊叹的工作。在这个代码中有很多好点子。我的问题是,我完全不知道它如何修理范围,或者即使它)
- http://www.crimsoneditor.com/(旧的Windows代码编辑器,它有一个很好的分词器 - 。虽然有点难以阅读它不。使用正则表达式的表达式话虽这么说,我根据我的令牌逻辑关闭的这段代码,这是远远超过上面列出的任何框架)
资源的速度更快:
- http://cocoafactory.com/blog/2012/10/29/how-to-use-custom-nsattributedstring-attributes/
- https://github.com/objcio/issue-5-textkit
- http://alexgorbatchev.com/SyntaxHighlighter/
- http://docs.xamarin.com/samples/TextKitDemo/(苹果的演示)
- http://cocoadev.com/ImplementSyntaxHighlighting(请务必仔细阅读所有子文章。伟大的东西)
大部分框架都是一样的。它们或者不考虑上下文切换(或者你必须编写包装来提供上下文)范围,或者它们不修改上下文范围,因为用户修改了文本(如字符串,多行注释等)。最后一项要求非常重要。因为如果令牌生成器无法确定哪些范围受到更改的影响,您最终必须再次解析并重新命名整个字符串。唯一的例外是Crimson编辑。这个标记器的问题是它在标记时不保存状态。在绘制时间,算法使用标记来确定绘图的状态。它从文档的顶部开始,一直到文本的可见范围。不用说,我通过在文档的某些部分缓存文档的状态来优化这一点。
另一个问题是,框架不遵循Apple所做的相同的MVC模式 - 这是可以预料的。具有完整工作编辑器的框架全部使用由它们构建的API(即GTK,Windows等)提供的钩子,为它们提供了何时以及何时绘制到屏幕的哪一部分的信息。在我的情况下,TextKit似乎要求您在processEditing中对整个更改的范围进行归因。
也许我的观察是错误的。 (我希望他们是!!)也许,例如ParseKit会为我需要它做的工作,我根本不知道如何使用它。如果是这样,请让我知道!并再次感谢!
我想通了。我没有使用上述任何建议。这就是说,我现在得到的表现简直令人难以置信。请记住YMMV。标记和缓存关于字符串的元数据的方式可能与我不同。但是,我能够输入1400行的PHP文件,并且只需0.015秒即可完成任何一项更改。简直难以置信。
这里是我采取的方法:
我的UIViewController是一个委托UITextViewDelegate和UIScrollViewDelegate。
当调用UITextViewDelegate.textViewDidChange:时,我确定当前最终用户可以看到哪个范围的文本。我这样做是利用我现有的子类的UITextView并添加这个方法吧:
- (NSRange)visibleRangeOfText
{
CGRect bounds = self.bounds;
UITextPosition *start = [self characterRangeAtPoint:bounds.origin].start;
UITextPosition *end = [self characterRangeAtPoint:CGPointMake(CGRectGetMaxX(bounds), CGRectGetMaxY(bounds))].end;
return NSMakeRange([self offsetFromPosition:self.beginningOfDocument toPosition:start],
[self offsetFromPosition:start toPosition:end]);
}
在那之后,我传递的范围内,以一个子类NSTextStorage对象,它会再执行魔术以确定哪些线路需要突出显示。
UIScollViewDelegate方法调用也是如此。根据视图的哪一部分被查看,我将可见范围传递给我的子类NSTextStorage调用,并确定这些行是否已被归因等。
我知道我要留给读者很多东西。我最终使用了我现在的东西,并对它进行了一些修改,以适应上述实现。
我想分享一些我的发现,我发现有趣而实现这一点:
1)如果你试图突出高于目前的线路,将光标显示静止的任何文字,你可能会看到光标在视图内“跳”起来,然后回到最初的位置。我几乎肯定这是由NSTextStorage.processEditing方法调用引起的。我能够得到它的系统只突出显示修改过的行......所以这个问题现在已经消失了。
2)本来我这样做是为了防止光标从跳来跳去:
NSRange selectedRange = [textView selectedTextRange];
[textView setScrollEnabled:NO];
NSRange visibleRange = [textView visibleRangeOfText];
[textStorage applyAttributesToRange:visibleRange];
[textView setScrollEnabled:YES];
它的工作...但[TextView的setScrollEnabled:NO]呼叫作出了巨大打击性能。这个命令仅用了将近3/4秒就完成了1400行文件。我不确定是什么导致它变慢,但我认为这值得一提。
如果有人试图实现上述建议,你会让我知道它是怎么回事?我很想听听你的发现!谢谢! – PeqNP
我忘了补充一件事。我也试着覆盖NSLayoutManager attributesAtIndex:effectiveRange :.我假设这返回一个NSDictionary与键/值对组成的NSForegroundColorAttributeName/UIColor等,但是,每次我试图返回一个_basic_字典与此键/值对,程序挂起...但是,这可能成为我可以潜在地提供属性的地方......这是假设属性被懒惰地查询。我只能猜测,我需要使已更改的文本范围无效,以使其正常工作。 – PeqNP
我想我可能会拥有它;如果我可以确定用户可见的文本范围,我可以将属性仅应用于processEditing中的该范围,并在可见文本之前和之后标记无效范围,并使用自定义属性(或NSRanges列表, )。然后,我会在一个单独的过程中卸载剩余的工作,仅用于无效的文本。 – PeqNP