2011-04-06 22 views
4

我在我的一個項目中有一個相當複雜的NSTextView子類。我目前正在努力讓查找/替換能夠使用內聯查找欄(例如Safari,Xcode),並且想要正確支持撤銷/重做替換操作。NSTextView replaceCharactersInRange:withString:對應於shouldChangeTextInRanges:replacementStrings:

我希望全部替換命令支持撤消作爲單個命令(即如果在文本視圖中有8個替換,它應該同時撤消這8個替換)。

我想知道是否有shouldChangeTextInRanges:replaceStrings:的對應件,我可以在檢查替換件後調用。我預計會有一個replaceCharactersInRanges:withStrings:或類似的東西,但似乎沒有。

我能想到此刻要做到這一點的唯一方法是通過調用檢查shouldChangeTextInRanges:replaceStrings:第一,然後調用replaceCharactersInRange:withString:與文本視圖的整個範圍和新的字符串(與所做的替換)作爲第二論據。

這似乎沒有必要,如果我不需要,我真的不想替換整個字符串。有任何想法嗎?

回答

4

經過一些修補後,我想我已經弄明白了。喬希,我用你的建議開始。我不確定你是否編輯了你的建議或者只是刪除了它,但沒有了,所以我不能在我的答案中引用它。

無論如何,在每次調用replaceCharactersInRange:withString:之後,您必須改變要替換的範圍,否則在範圍不匹配時會發生不好的事情。以下是我結束了:

// 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]; 
} 

這個偉大的工程和支持撤消預期,撤消所有替代而復一次這種操作的結果。

無論如何感謝喬希,因爲他的回答讓我指出了正確的方向。

+0

我把它刪除了,因爲我沒有嘗試過;那麼當我嘗試編譯/運行它時,我無法讓它做任何有用的事情。很高興它是有幫助的!爲了後代的緣故,我已經放棄了它。 – 2011-04-07 03:47:04

+0

偉大的問題,BTW,和很好的解決方案! – 2011-04-07 03:49:47

0

我很驚訝,撤消沒有自動分組。但是,您可以執行手動撤消分組;你必須自己設置相反的動作。希望這會指向你正確的方向:

- (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 
} 
+0

對於未來的歷史學家:這段代碼甚至沒有編譯!我只從刪除文件夾中檢索它,因爲willbur1984表示它很有幫助。 – 2011-04-07 03:48:30