在丰富的UITextView(iOS 6)中使用NSUndoManager撤消操作

Gui*_*ume 5 uitextview nsattributedstring nsundomanager uitextinput ios6

我想要更改丰富的UITextView(iOS 6)的部分或全部属性文本,并允许用户撤消更改.

在阅读NSUndoManager文档后,我尝试了第一种方式:

“Simple undo” based on a simple selector with a single object argument.
Run Code Online (Sandbox Code Playgroud)

我期望撤消操作就像这样简单:
声明这个方法:

- (void)setAttributedStringToTextView:(NSAttributedString *)newAttributedString {

     NSAttributedString *currentAttributedString = self.textView.attributedText;

    if (! [currentAttributedString isEqualToAttributedString:newAttributedString]) {
         [self.textView.undoManager registerUndoWithTarget:self
                                    selector:@selector(setAttributedStringToTextView:)
                                      object:currentAttributedString];
         [self.textView.undoManager setActionName:@"Attributed string change"];
         [self.textView setAttributedText:newAttributedString];
    }
}
Run Code Online (Sandbox Code Playgroud)

通过调用以下命令更改我的UITextView中的文本:

[self setAttributedStringToTextView:mutableAttributedString];
Run Code Online (Sandbox Code Playgroud)

但在这之后,NSUndoManager表示它无法撤消.

NSLog(@"Can undo: %d", [self.textView.undoManager canUndo]);
// Prints: "Can undo: 0"
Run Code Online (Sandbox Code Playgroud)




所以我尝试了第二种方式:

“Invocation-based undo” which uses an NSInvocation object.
Run Code Online (Sandbox Code Playgroud)

声明:

- (void)setMyTextViewAttributedString:(NSAttributedString *)newAttributedString {

        NSAttributedString *currentAttributedString = [self.textView attributedText];
    if (! [currentAttributedString isEqualToAttributedString:newAttributedString]) {
        [[self.textView.undoManager prepareWithInvocationTarget:self]
         setMyTextViewAttributedString:currentAttributedString];
        [self.textView.undoManager setActionName:@"Attributed string change"];
        [self.textView setAttributedText:newAttributedString];
    }
}
Run Code Online (Sandbox Code Playgroud)

并使用以下内容更改文本:

[self setMyTextViewAttributedString:mutableAttributedString];
Run Code Online (Sandbox Code Playgroud)

之后,NSUndoManager也表示它无法撤消.

为什么?

请注意,用户在触发将更改属性文本的代码时正在编辑UITextView.




解决方法是直接通过UITextInput协议方法替换文本.以下方法非常方便,但我没有找到NSAttributedString的等价物.我错过了吗?

- (void)replaceRange:(UITextRange *)range withText:(NSString *)text
Run Code Online (Sandbox Code Playgroud)

这里建议的黑客是模拟粘贴操作.如果可能的话,我宁愿避免这种情况(没有理由,只是觉得太脏了,不能再回来咬我).

Bra*_*oss 2

我仍然有点震惊这有效。我在这里发布了相同的答案UITextView 撤消管理器不适用于替换属性字符串 (iOS 6)

- (void)applyAttributesToSelection:(NSDictionary*)attributes {
    UITextView *textView = self.contentCell.textView;

    NSRange selectedRange = textView.selectedRange;
    UITextRange *selectedTextRange = textView.selectedTextRange;
    NSAttributedString *selectedText = [textView.textStorage attributedSubstringFromRange:selectedRange];

    [textView.undoManager beginUndoGrouping];
    [textView replaceRange:selectedTextRange withText:selectedText.string];
    [textView.textStorage addAttributes:attributes range:selectedRange];
    [textView.undoManager endUndoGrouping];

    [textView setTypingAttributes:attributes];
}
Run Code Online (Sandbox Code Playgroud)