0

我有一个UITextView带有格式的工具栏。在我按下“粗体”按钮后,它会更改文本/选择并将操作注册到文本视图的NSUndoManager.

当您尝试撤消时,restoreText:会一遍又一遍地调用,直到您杀死应用程序

一些示例代码:

- (void)bold {
    NSRange range = self.textView.selectedRange;
    NSRange oldRange = range;
    NSString *oldText = self.textView.text;
    NSString *selection = [self.textView.text substringWithRange:range];
    self.textView.scrollEnabled = NO;
    self.textView.text = [self.textView.text stringByReplacingCharactersInRange:range
                                                           withString:[NSString stringWithFormat:@"<b>%@</b>", selection]];
    self.textView.scrollEnabled = YES;
    if (range.length == 0) {                // If nothing was selected
        range.location += 3; // Place selection between tags
    } else {
        range.location += range.length + 7; // Place selection after tag
        range.length = 0;
    }
    self.textView.selectedRange = range;
    [[self.textView.undoManager prepareWithInvocationTarget:self] restoreText:oldText withRange:oldRange];
    [self.textView.undoManager setActionName:@"bold"];
}

- (void)restoreText:(NSString *)text withRange:(NSRange)range {
    NSLog(@"restoreText:%@ %@",text, [NSNumber numberWithBool:[self.textView.undoManager isUndoing]]);
    NSString *oldText = self.textView.text;
    NSRange oldRange = self.textView.selectedRange;
    self.textView.scrollEnabled = NO;
    self.textView.text = text;
    self.textView.scrollEnabled = YES;
    self.textView.selectedRange = range;
    [[self.textView.undoManager prepareWithInvocationTarget:self] restoreText:oldText withRange:oldRange];
}

和控制台:

2012-10-04 18:10:14.207 undotest[8861:c07] restoreText: 1
2012-10-04 18:10:14.633 undotest[8861:c07] restoreText:<b></b> 0
2012-10-04 18:10:15.117 undotest[8861:c07] restoreText: 0
2012-10-04 18:10:15.589 undotest[8861:c07] restoreText:<b></b> 0
2012-10-04 18:10:16.017 undotest[8861:c07] restoreText: 0
2012-10-04 18:10:16.557 undotest[8861:c07] restoreText:<b></b> 0

[self.textView.undoManager isUndoing]在注册调用之前尝试过检查restoreText:withRange:,但是崩溃了:

2012-10-04 18:10:49.297 undotest[8904:c07] restoreText: 1
2012-10-04 18:10:53.412 undotest[8904:c07] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '_endUndoGroupRemovingIfEmpty:: WebThreadSafeUndoManager 0x76551e0 is in invalid state, endUndoGrouping called with no matching begin
'

此代码在 iOS5 上运行,但在 iOS6 上停止运行。我已经向 Apple 提交了一个错误,但我不确定我是否做错了什么

4

2 回答 2

0

Same thing happens to my UITextView instance even for simple-undoing. This must be a bug, and it means that no undo feature can be implemented by yourself to your UITextView anymore. I wonder if anyone have successfully implemented undo features on iOS6 UITextView...

Alternatively, I desided to use replaceRange:withText: (TextInput Protocol) instead to implement my undo feature on UITextView. At least you can Undo replacing text with that method. You don't need to register undo action for that by yourself. That method does it automatically.

于 2012-10-08T15:58:33.833 回答
0

我遇到了同样的问题,发现访问 textView 的文本会导致奇怪的行为,例如重复调用方法并崩溃。

textView.text=@"" //this crashes

这可以replaceRange:withText:通过user1263865的建议使用来解决,如果您想自定义要处理的操作以撤消/重做,您可以注册该操作,但跳过修改文本的部分,因为replaceRange:withText:已经处理它。

例如,如果我想创建一个明文函数,在 textView 以及自定义消息对象中明文,然后在用户撤消操作时恢复它:

-(void) clearEditText {
    //Register undo
    [[textView.undoManager prepareWithInvocationTarget:self] undoClearEditTextWithMessage:[self.message retain]];
    //message is retained because undo manager will not retain, it will be released when user undo.

    // Clear text field
    UITextPosition *beginning = textView.beginningOfDocument;
    UITextPosition *end= textView.endOfDocument;
    UITextRange *textRange = [textView textRangeFromPosition:beginning toPosition:end];
    [textView replaceRange:textRange withText:@""];

    self.message = /*new message*/;
}
-(void) undoClearEditTextWithMessage:(Message*)restoreMessage {
    //Register redo
    [[textView.undoManager prepareWithInvocationTarget:self] undoClearEditTextWithMessage:[self.message retain]];

    self.message = restoreMessage;

    [restoreMessage release];
    //message is released because it is retained when it is added to undo manager.
}
于 2012-12-03T19:47:14.247 回答