How can I replace text in UITextView with NSUndoManager support?

杀马特。学长 韩版系。学妹 提交于 2019-12-03 12:31:44

问题


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?


回答1:


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.




回答2:


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.




回答3:


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



回答4:


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.



来源:https://stackoverflow.com/questions/6419823/how-can-i-replace-text-in-uitextview-with-nsundomanager-support

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!