在iOS 6中,使用NSUndoManager撤销富文本UITextView中的操作

5
我希望改变一个富文本UITextView(iOS 6)的一些或所有属性文本,并允许用户撤销更改。
阅读了NSUndoManager documentation后,我尝试了第一种方法:
“Simple undo” based on a simple selector with a single object argument.

我希望撤销操作简单明了,只需要:

声明这个方法:

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

通过调用以下方法更改我的UITextView中的文本:
[self setAttributedStringToTextView:mutableAttributedString];

但是这样做后,NSUndoManager说它无法撤销。

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

所以我尝试了第二种方式:
“Invocation-based undo” which uses an NSInvocation object.

声明这个:
- (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];
    }
}

并使用以下文本进行更改:

[self setMyTextViewAttributedString:mutableAttributedString];

之后,NSUndoManager也说它无法撤消。

为什么?

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




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

- (void)replaceRange:(UITextRange *)range withText:(NSString *)text

这里提出的一个技巧是模拟粘贴操作。如果可能的话,我更倾向于避免这样做(目前没有理由,只是感觉这样太不干净了,以后可能会造成麻烦)。

你是否验证了 self.textViewself.textView.attributedTextself.textView.undoManager 是否都不为 nil - Peter Hosey
是的。self.textView.attributedText = newValue 实际上在用户界面中得到更新。textView.undoManager 是 WebThreadSafeUndoManager 类的成员,并且在我的 viewController 的整个生命周期中都是同一个实例。 - Guillaume
1
我注意到了同样的问题。我可以撤销一些操作,但不能撤销attributedString的替换。没有找到解决方法(请参见https://dev59.com/mmjWa4cB1Zd3GeqPsqM8)。 - Denis
1个回答

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接