Objective c NSTextView ReplaceCharactersRange:withString:shouldChangeTextInRanges:ReplacementString的对应项:

Objective c NSTextView ReplaceCharactersRange:withString:shouldChangeTextInRanges:ReplacementString的对应项:,objective-c,cocoa,nstextview,Objective C,Cocoa,Nstextview,我的一个项目中有一个相当复杂的NSTextView子类。我目前正在努力使find/replace与内联查找栏(例如Safari、Xcode)一起工作,并希望正确支持replace操作的undo/redo 我希望Replace All命令作为单个命令支持undo(即,如果要在文本视图中进行8次替换,它也应该同时撤消这8次替换) 我想知道是否有一个对应的shouldChangeTextInRanges:replaceStrings:,我可以在检查后调用它进行替换。我原以为会有一个替换字符sinran

我的一个项目中有一个相当复杂的NSTextView子类。我目前正在努力使find/replace与内联查找栏(例如Safari、Xcode)一起工作,并希望正确支持replace操作的undo/redo

我希望Replace All命令作为单个命令支持undo(即,如果要在文本视图中进行8次替换,它也应该同时撤消这8次替换)

我想知道是否有一个对应的
shouldChangeTextInRanges:replaceStrings:
,我可以在检查后调用它进行替换。我原以为会有一个
替换字符sinranges:with字符串:
或类似的东西,但似乎没有

目前我唯一能想到的方法是先调用
shouldChangeTextInRanges:replaceStrings:
,然后调用
replaceCharactersRange:withString:
,将文本视图的整个范围和新字符串(进行了替换)作为第二个参数


这似乎没有必要,如果不需要的话,我真的不想替换整个字符串。有什么想法吗?

我很惊讶撤销没有自动分组。但是,您可以手动撤消分组;你必须自己设置反向动作。希望这将为您指明正确的方向:

- (BOOL)shouldChangeTextInRanges:(NSArray *)affectedRanges replacementStrings:(NSArray *)replacementStrings {

    NSUndoManager * undoMan = [self undoManager];
    [undoMan beginUndoGrouping];
    NSEnumerator stringEnumerator = [replacementStrings objectEnumerator];
    for( NSRange thisRange in affectedRanges ){
        NSString * thisString = [stringEnumerator nextObject];
        NSTextStorage * textStore = [self textStorage];
        [[undoMan prepareWithInvocationTarget:textStore] 
                 replaceCharactersInRange:thisRange 
                               withString:[textStore attributedSubstringFromRange:thisRange]];

        [textStore replaceCharactersInRange:thisRange withString:thisString];
    }
    [undoMan endUndoGrouping];
    [undoMan setActionName:@"Replace All"];

    return NO; // because we just did it by hand
}

经过一些修补,我想我已经弄明白了。乔希,我用你的建议开始了。我不确定你是否编辑了你的建议或者只是删除了它,但是它已经消失了,所以我不能在我的回答中引用它

无论如何,在每次调用
replaceCharactersRange:with字符串:
之后,必须移动要替换的范围,否则,当范围不匹配时,就会发生不好的事情。以下是我的结论:

// array of NSValue objects storing an NSRange
NSArray *replaceRanges = [self replaceRanges];
NSString *replaceString = [self replaceString];
// array of NSString objects you are going to use for the replace operation, just replaceString repeated [replaceRanges count] times
NSArray *replaceStrings = [self replaceStrings];
NSTextView *textView = [self textView];
// the amount we have to shift subequent replace ranges after each call to replaceCharactersInRange:withString:
NSInteger locationShift = 0;

// check to makes sure the replace can occur
if ([textView shouldChangeTextInRanges:replaceRanges replacementStrings:replaceStrings]) {
    // we want to treat all these replacements as a single replacement for undo purposes
    [[textView textStorage] beginEditing];

    for (NSValue *rangeValue in replaceRanges) {
        NSRange range = [rangeValue rangeValue];

        // replace the range shifted by locationShift with our replaceString
        [[textView textStorage] replaceCharactersInRange:NSMakeRange(range.location + locationShift, range.length) withString:replaceString];

        // update the shift amount, which is the difference between our replaced string length and the original match length
        locationShift += [replaceString length] - range.length;
    }
    // end the grouping operation
    [[textView textStorage] endEditing];
}
这非常有效,并支持按预期撤消,撤消此操作会导致所有替换都立即撤消


无论如何都要感谢Josh,因为他的回答让我找到了正确的方向。

我删除了它,因为我没有尝试就把它打印出来了;然后,当我尝试编译/运行它时,我无法让它做任何有用的事情。很高兴这有帮助!为了子孙后代,我取消了它。顺便说一句,这是一个很好的问题,也是一个很好的解决方案!对于未来的历史学家来说:这段代码甚至都不会编译!我只是从删除箱中检索到它,因为willbur1984表示它很有用。