I want to be able to replace some text in an UITextView programatically, so I wrote this method as an UITextView category:
- (void) replaceCharactersInRange:(NSRange)range withString:(NSString *)newText{
self.scrollEnabled = NO;
NSMutableString *textStorage = [self.text mutableCopy];
[textStorage replaceCharactersInRange:range withString:newText];
//replace text but undo manager is not working well
[[self.undoManager prepareWithInvocationTarget:self] replaceCharactersInRange:NSMakeRange(range.location, newText.length)
withString:[textStorage substringWithRange:range]];
NSLog(@"before replacing: canUndo:%d", [self.undoManager canUndo]); //prints YES
self.text = textStorage;
NSLog(@"after replacing: canUndo:%d", [self.undoManager canUndo]); //prints NO
if (![self.undoManager isUndoing])[self.undoManager setActionName:@"replace characters"];
[textStorage release];
//new range:
range.location = range.location + newText.length;
range.length = 0;
self.selectedRange = range;
self.scrollEnabled = YES;
}
It works but NSUndoManager stops working (it seems to be reset) just after doing self.text=textStorage
I have found a private API: -insertText:(NSString *)
that can do the job but who knows if Apple is going to approve my app if I use it. Is there any way to get text replaced in UITextView with NSUndoManager Support? Or maybe I am missing something here?
After having stumbled over this issue and getting grey hair of it, I finally found a satisfying solution. It's a bit tacky, but it DOES work like a charm. The idea is to use the working copy&paste support that UITextView offers! I think you might be interested:
EDIT: Of course you can customize this code to insert text at any position in the text view. Just set the
selectedRange
before callingpaste
.Shouldn't it be
And you probably can remove the mutable string and just do,
I've been having trouble with this for a long time. I finally realized that you have to make sure you register the undo operation BEFORE doing any changes to your text storage.
There is actually no reason for any hacks or custom categories to accomplish this. You can use the built in UITextInput Protocol method
replaceRange:withText:
. For inserting text you can simply do:This works as of iOS 5.0. Undo works automatically and there are no weird scrolling issues.