Имам доста сложен подклас NSTextViewв един от моите проекти. Понастоящем работя за намиране / замяна, за да работя с вградена лента за намиране (например Safari, Xcode) и искам да поддържам правилно undo / redo за операциите за замяна.
Искам командата Replace All (Подмяна на всички) да поддържа отмяна като една команда (т.е. ако има 8 замени, които трябва да бъдат направени в текстовия изглед, то трябва да отмени и осемте замествания наведнъж).
Чудя се дали има друга страна shouldChangeTextInRanges:replaceStrings:
че мога да се обадя след проверка, за да направя замяна. Очаквах, че ще има replaceCharactersInRanges:withStrings:
или нещо подобно, но изглежда, че няма.
Единственият начин, по който мога да направя това в момента, е да проверя с обаждане shouldChangeTextInRanges:replaceStrings:
първо, след това се обадете replaceCharactersInRange:withString:
с целия диапазон на текстовия изглед и новия низ (с направените замени) като втори аргумент.
Това просто изглежда ненужно, аз наистина не искам да заменя целия низ, ако не трябва. Някакви идеи?
Отговори:
4 за отговор № 1Смятам, че след известно бъркотие имам товаразбрах. Джош, използвах предложението ти да започна. Аз не съм сигурен дали сте редактирали предложението си или просто сте го изтрили, но е изчезнало, така че не мога да го цитирам в отговора си.
Както и да е, трябва да смените диапазоните, които ще замените след всяко извикване на 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 за отговор № 2
Изненадан съм, че отмената за това не е групиранаавтоматично. Въпреки това, можете да направите групово връщане назад; ще трябва сами да настроите обратните действия. Надяваме се, че това ще ви насочи в правилната посока:
- (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
}