我创建了一个带有NSTextView和按钮的简单演示应用程序,为textView提供了一个NSTextViewDelegate并添加了一个动作:
- (IBAction)actionButtonClicked:(id)sender {
NSString *oldText = [[[self.textView textStorage] string] copy];
NSString *newText = @"And... ACTION!";
[[self.textView undoManager] registerUndoWithTarget:self.textView
selector:@selector(setString:)
object:oldText];
[[self.textView undoManager] setActionName:@"ACTION"];
[self.textView setString:newText];
}
如果我手动更改文本,撤消/重做没有问题。但是,如果我使用action方法更改文本,则undo会按预期工作,但redo不再起作用(没有任何反应),并且撤消管理器似乎被扰乱了......
好的 - 为了避免NSTextView的问题我创建了一个模型类,将NSTextView绑定到它并将undo / redo移动到模型,但这显示了与以前相同的行为 - 我做错了什么 - 这应该很容易,不应该吗?
#import "GFTextStore.h"
@implementation GFTextStore
@synthesize textVal = textVal_;
-(void)changeText{
if (!undoMan_) {
undoMan_ = [[[NSApplication sharedApplication] mainWindow] undoManager];
}
NSAttributedString *oldText = [self.textVal copy];
NSString *tempStr = [[oldText string] stringByAppendingFormat:@"\n%@",[[NSCalendarDate date]description]];
NSAttributedString *newText = [[NSAttributedString alloc] initWithString:tempStr];
[self setTextVal:newText];
[undoMan_ registerUndoWithTarget:self
selector:@selector(setTextVal:)
object:oldText];
[undoMan_ setActionName:@"ACTION"];
}
@end
答案 0 :(得分:11)
无需在此处添加NSUndoManager,只需让NSTextView完成工作。
您只需要确保从插入... 开始调用NSTextView的更高级别方法,而不是设置textView或textStorage的文本/字符串直接:
[self.textView insertText:newString];
如果你绝对需要使用setString或其他低级方法,那么你只需要添加处理textDidChange委派所需的方法: -shouldChangeTextInRange:replacementString 和 -didChangeText (由插入...方法btw完成):
if( [self.textView shouldChangeTextInRange:editedRange replacementString:editedString]) {
// do some fancy stuff here…
[self.textView.textStorage replaceCharactersInRange:editedRange
withAttributedString:myFancyNewString];
// … and finish the editing with
[self.textView didChangeText];
}
这会自动让NSTextView的undoManager启动。我认为undoManager正在shouldChangeTextInRange中准备一个undoGrouping:并在didChangeText中调用undo:。
答案 1 :(得分:5)
-setString:
是来自NSText
的继承方法。要仅使用NSTextView
方法处理此问题以便处理撤消,请执行以下操作:
[self.textView setSelectedRange:NSMakeRange(0, [[self.textView textStorage] length])];
[self.textView insertText:@"And… ACTION!"];
以这种方式改变文本可以避免使用撤消管理器。
答案 2 :(得分:1)
假设您希望NSTextView在用户点击Enter键(Apple页面行为)时创建新的撤消组。然后,您可以在NSTextView子类中键入此代码:
override func shouldChangeTextInRange(affectedCharRange: NSRange, replacementString: String?) -> Bool {
super.shouldChangeTextInRange(affectedCharRange, replacementString: replacementString)
guard replacementString != nil else { return true }
let newLineSet = NSCharacterSet.newlineCharacterSet()
if let newLineRange = replacementString!.rangeOfCharacterFromSet(newLineSet) {
// check whether it's a single character (user hit Return key)
let singleCharRange = (replacementString!.startIndex)! ..< (replacementString!.startIndex.successor())!
if newLineRange == singleCharRange {
self.breakUndoCoalescing()
}
}
return true
}