リッチ 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"
だから私は2番目の方法を試しました:
“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
ここで提案されているハックは、貼り付け操作をシミュレートすることです。可能であれば、これを避けたいと思います(理由はまだありません。後で噛まれないようにするには、あまりにも汚いと感じているだけです)。