How can I replace text in UITextView with NSUndoManager support?
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?
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:
[textView replaceRange:textView.selectedTextRange withText:replacementText];
This works as of iOS 5.0. Undo works automatically and there are no weird scrolling issues.
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:
- (void)insertText:(NSString *)insert
{
UIPasteboard *pboard = [UIPasteboard generalPasteboard];
// Clear the current pasteboard
NSArray *oldItems = [pboard.items copy];
pboard.items = nil;
// Set the new content to copy
pboard.string = insert;
// Paste
[self paste: nil];
// Restore pasteboard
pboard.items = oldItems;
[oldItems release];
}
EDIT: Of course you can customize this code to insert text at any position in the text view. Just set the selectedRange
before calling paste
.
Shouldn't it be
[[self.undoManager prepareWithInvocationTarget:self] replaceCharactersInRange:NSMakeRange(range.location, newText.length)
withString:[self.text substringWithRange:range]];
And you probably can remove the mutable string and just do,
self.text = [self.text stringByReplacingCharactersInRange:range withString:newText];
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.
精彩评论