NSTextView 撤消/重做属性更改(当不是第一响应者时)

Posted

技术标签:

【中文标题】NSTextView 撤消/重做属性更改(当不是第一响应者时)【英文标题】:NSTextView undo/redo attribute changes (when not first responder) 【发布时间】:2015-10-13 15:12:26 【问题描述】:

我正在构建一个带有自定义控件的基本文本编辑器。对于我的文本对齐控件,我需要涵盖两个用户场景:

    文本视图是第一响应者 - 将段落属性更改为textView.rangesForUserParagraphAttributeChange

    文本视图不是第一响应者 - 将段落属性更改为全文范围。

方法如下:

- (IBAction)changedTextAlignment:(NSSegmentedControl *)sender

    NSTextAlignment align;
    // ....

    NSRange fullRange = NSMakeRange(0, self.textView.textStorage.length);
    NSArray *changeRanges = [self.textView rangesForUserParagraphAttributeChange];

    if (![self.mainWindow.firstResponder isEqual:self.textView])
    
        changeRanges = @[[NSValue valueWithRange:fullRange]];
    

    [self.textView shouldChangeTextInRanges:changeRanges replacementStrings:nil];
    [self.textView.textStorage beginEditing];

    for (NSValue *r in changeRanges)
    
        @try 
            NSDictionary *attrs = [self.textView.textStorage attributesAtIndex:r.rangeValue.location effectiveRange:NULL];
            NSMutableParagraphStyle *pStyle =  [attrs[NSParagraphStyleAttributeName] mutableCopy];
            if (!pStyle)
                pStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];

            [pStyle setAlignment:align];
            [self.textView.textStorage addAttributes:@NSParagraphStyleAttributeName: pStyle
                                             range:r.rangeValue];
        
        @catch (NSException *exception) 
            NSLog(@"%@", exception);
        
    

    [self.textView.textStorage endEditing];
    [self.textView didChangeText];

    // ....

    NSMutableDictionary *typingAttrs = [self.textView.typingAttributes mutableCopy];
    NSMutableParagraphStyle *pStyle =  typingAttrs[NSParagraphStyleAttributeName];
    if (!pStyle)
        pStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
    [pStyle setAlignment:align];
    [typingAttrs setObject:NSParagraphStyleAttributeName forKey:pStyle];
    self.textView.typingAttributes = typingAttrs;


所以这两种情况都可以正常工作...但是当更改应用于“非第一响应者”情况时,撤消/重做不起作用。撤消管理器将某些内容推入其堆栈(即撤消在编辑菜单中可用),但调用撤消不会更改文本。它所做的只是明显地选择整个文本范围。

我如何适当地更改文本视图属性,以便无论视图是否是第一个响应者,撤消/重做都能正常工作?

提前谢谢你!

【问题讨论】:

【参考方案1】:

我不确定,但我有两个建议。一,检查来自shouldChangeTextInRanges:... 的返回值,因为文本系统可能拒绝您提出的更改;无论如何都是个好主意。第二,我会尝试让非第一响应者案例更像是第一响应者案例,以便让它发挥作用;特别是,您可以从选择整个范围开始,这样rangesForUserParagraphAttributeChange 实际上就是您更改属性的范围。朝着这个方向迈出的进一步一步是在您的更改期间实际上暂时使 textview 成为第一响应者。在那种情况下,我认为这两种情况应该是完全相同的。您可以在完成后立即恢复第一响应者。不是最优的,但似乎 AppKit 在幕后做出了一些假设,您可能只需要解决这个问题。无需尝试重​​现问题并解决它,这是我能提供的最好的......

【讨论】:

感谢@bhaller。我尝试了您的两个建议,但结果相同。最终,之后更改typingAttributes 上的对齐似乎是一个问题(请参阅我的回答)。非常感谢您的洞察力! 有趣。输入属性——我没有预见到这一点。 :-> Lolz,我刚刚发现这是我的代码中的一个错字。不是错误!【参考方案2】:

问题是我在之后更新typingAttributes 的代码中的拼写错误。看这里:

//...

NSMutableParagraphStyle *pStyle =  typingAttrs[NSParagraphStyleAttributeName];

// ...

哇!需要真正可变...

//...

NSMutableParagraphStyle *pStyle =  [typingAttrs[NSParagraphStyleAttributeName] mutableCopy];

// ...

【讨论】:

以上是关于NSTextView 撤消/重做属性更改(当不是第一响应者时)的主要内容,如果未能解决你的问题,请参考以下文章

在 CoreData 应用程序上摇动撤消工作,但没有显示撤消/重做提示

Flex ApplyFormatOperation 中断 Spark TextArea 中的撤消/重做

协作文档/代码编辑、处理撤消和冲突?

如何将Tkinter Text小部件的撤消/重做历史记录复制到另一个小部件中

UITextView 和 NSUndoManager 同步

检测 iOS 13 撤消和重做