/ / NSTextView replaceCharactersInRange: withString: копие на shouldChangeTextInRanges: replacementStrings: - object-c, какао, nstextview

NSTextView replaceCharactersInRange: withString: съответства на shouldChangeTextInRanges: replaceStrings: - цел-c, какао, nstextview

Имам доста сложен подклас 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
}