1

我正在尝试创建一个 NSTextView,它的行为有点像一系列打字机页面——每个页面都有固定数量的可用行和列。

底层表示与显示完美匹配,而显示又与文件格式匹配。页面以换页符结尾'\f'。例如,两个 4x4 页面在保存时会如下所示(字符之间有空格以便于阅读):

T  H  I  S  \n
P  A  G  E  \n
I  S  \n
F  U  L  L  \n
\f
N  O  T  \n
T  H  I  S  \n
O  N  E  \n
\n
\f

需要注意的几点:

  • 换行符和换页符不计入行/列总数
  • 短于列数的行不会用尾随空格填充(尽管如果编辑器在覆盖模式下工作,它们可能是)
  • 比行数短的页面换行符填充

我尝试了几种方法,例如将文本转换NSArray为一行并在每个通知上拆分长行textDidChange,然后将文本设置为连接的行,但它确实效率低下,并且如果键入发生在除末尾之外的任何地方,则会丢失光标位置文档。

最后,我想我希望它表现为一个填充有空格的大页面,并且键入只发生在覆盖模式下,但在行尾换行。我不确定从哪里开始使用这种方法。有什么建议么?

4

1 回答 1

0

我找到了一个可接受的(如果不是完美的)通过子类化包装的解决方案NSTextStorage

在将文本添加到文档末尾时,无论是粘贴还是键入,它的行为都完全符合预期。它在删除或替换文本时也会起作用。唯一有点奇怪的地方是在已经满行的中间插入文本时。这将在插入的文本之前导致换行。它工作正常,但这意味着继续在该位置键入会创建许多短行。

例如,下面的文本在 20 列处换行。原始文本是x,插入文本是o。插入的文本从第二行的第 8 个字符开始。

xxxxxxxxxxxxxxxxxxxx
xxxxxxx
ooooooo
ooooooo
oooooooxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxx

下面的代码:

- (void)breakLinesInArray:(NSMutableArray *)lines atLength:(NSUInteger)length
charsPreceding:(NSUInteger)preceding charsFollowing:(NSUInteger)following
{
    NSUInteger line = 0;
    while (line < [lines count]) {
        NSUInteger maxLineLength = length;
        if (line == 0) {
            maxLineLength = length - preceding;
        } else if (line == [lines count] - 1) {
            maxLineLength = length - following;
        }
        NSString *original = [lines objectAtIndex:line];
        if (maxLineLength > 0 && [original length] > maxLineLength) {
            NSString *first = [original substringToIndex:maxLineLength];
            NSString *second = [original substringFromIndex:maxLineLength];
            [lines replaceObjectAtIndex:line withObject:first];
            [lines insertObject:second atIndex:line + 1];
        }
        ++line;
    }
}

- (void)replaceCharactersInRange:(NSRange)range withString:(NSString *)str
{
    static const NSUInteger kMaxLineLength = 20;
    NSString *text = [text_ string];
    NSUInteger firstLineStart = 0;
    NSUInteger lastLineEnd = 0;
    NSMutableArray *lines = [NSMutableArray arrayWithArray:[str componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]]];
    [text getLineStart:&firstLineStart end:nil contentsEnd:&lastLineEnd forRange:range];
    NSUInteger firstLineStartFragmentLength = range.location - firstLineStart;
    NSUInteger lastLineEndFragmentLength = lastLineEnd - (range.location + range.length);
    [self breakLinesInArray:lines atLength:kMaxLineLength charsPreceding:firstLineStartFragmentLength charsFollowing:lastLineEndFragmentLength];
    NSString *newString = [lines componentsJoinedByString:@"\n"];
    NSInteger changeInLength = [newString length] - range.length;
    NSInteger newLastLineLength = firstLineStartFragmentLength + lastLineEndFragmentLength + changeInLength;
    if ((firstLineStartFragmentLength || lastLineEndFragmentLength) && newLastLineLength > (NSInteger)kMaxLineLength) {
        newString = [@"\n" stringByAppendingString:newString];
        ++changeInLength;
    }
    [text_ replaceCharactersInRange:range withString:newString];
    [self edited:NSTextStorageEditedCharacters range:range changeInLength:changeInLength];
}
于 2012-06-16T01:02:46.397 回答