NSUndoManager重复调用我的“恢复”方法

时间:2012-10-04 16:13:40

标签: objective-c ios uitextview ios6 nsundomanager

我有一个带有格式工具栏的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提交了一个错误,但我不确定我做错了什么

2 个答案:

答案 0 :(得分:0)

即使是简单撤消,我的UITextView实例也会发生同样的事情。这必定是一个错误,这意味着您自己不能再对UITextView实现撤消功能。 我想知道是否有人在iOS6 UITextView上成功实现了撤消功能...

或者,我决定使用replaceRange:withText :( TextInput Protocol)代替在UITextView上实现我的撤消功能。至少你可以用该方法撤消替换文本。您不需要自己注册撤消操作。该方法自动完成。

答案 1 :(得分:0)

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

textView.text=@"" //this crashes

这可以通过user1283865建议使用replaceRange:withText:来解决, 如果要自定义要处理撤消/重做的操作,可以注册操作,但跳过修改文本的部分,因为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.
}