如何通过编程更改 NSTextView 的 textValue 来实现撤消/重做?
我创建了一个带有 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];
}
如果我手动更改文本,撤消/重做工作不会出现问题。但是,如果我使用操作方法更改文本,撤消将按预期工作,但重做不再起作用(什么也没有发生),并且撤消管理器似乎被打乱...
好吧 - 为了避免 NSTextView 出现问题,我创建了一个模型类,将 NSTextView 绑定到它并将撤消/重做移动到模型,但这显示了与以前相同的行为 - 我做错了什么 - 这应该很容易,不是吗?
#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
I created a simple demo app with a NSTextView and a button, the provided a NSTextViewDelegate to the textView and added an action:
- (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];
}
Undo/redo works without problems, if I change text by hand. But if I change the text with the action method, undo works as expected, but redo does not work anymore (nothing happens) and the undo manager seems to be scrambled...
OK - to avoid problems with NSTextView I created a model class, bound the NSTextView to it and moved the undo/redo to the model, but this shows the same behavior as before - what the I'm doing wrong - this should be easy, shouldn't it?
#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
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
此处无需添加 NSUndoManager,只需让 NSTextView 完成该工作即可。
您只需要确保仅调用以 insert... 开头的 NSTextView 的更高级别的方法,而不是直接设置 textView 或 textStorage 的文本/字符串:
如果您绝对需要使用 setString 或其他较低级别的方法,然后您只需要添加处理 textDidChange 委托所需的方法: -shouldChangeTextInRange:replacementString 和-didChangeText (这是通过 insert... 方法完成的):
这会自动让 NSTextView 的 undoManager 启动。我认为 undoManager 正在 shouldChangeTextInRange: 中准备撤消分组并调用 didChangeText 中的撤消:.
No need to add the NSUndoManager here, just let the NSTextView do the job.
You just need to make sure you are calling the higher level methods only of NSTextView beginning with insert… and not setting the text/string of the textView or the textStorage directly:
If you absolutely need to use setString or other lower level methods, then you just need to add the required methods handling the textDidChange delegation: -shouldChangeTextInRange:replacementString and -didChangeText (which is done by the insert... methods btw):
This automatically lets the undoManager of NSTextView kick in. I think the undoManager is preparing an undoGrouping in shouldChangeTextInRange: and invoking the undo in didChangeText:.
-setString:
是从NSText
继承的方法。要仅使用 NSTextView 方法来处理此问题以便处理撤消,只需执行以下操作:以这种方式更改文本可以完全避免与撤消管理器混淆。
-setString:
is an inherited method fromNSText
. To handle this usingNSTextView
methods only so that undo is handled, just do this:Making the text change this way avoids mucking about with the undo manager at all.
假设您希望 NSTextView 在用户按下 Enter 键时创建一个新的撤消组(Apple Pages 行为)。然后你可以在 NSTextView 子类中键入以下代码:
Let's say you want your NSTextView to create a new undo group when user hits the Enter key (Apple Pages behavior). Then you might type this code in your NSTextView subclass: