NSManagedObjectContext 保存导致 NSTextField 失去焦点

Posted

技术标签:

【中文标题】NSManagedObjectContext 保存导致 NSTextField 失去焦点【英文标题】:NSManagedObjectContext save causes NSTextField to lose focus 【发布时间】:2010-09-30 07:53:14 【问题描述】:

这是我在我的应用程序中看到的一个非常奇怪的问题。我有一个 NSTextField 绑定到 NSManagedObject 的属性,但是每当保存对象时,文本字段就会失去焦点。我不断更新绑定的值,所以这远非理想。

以前有没有人见过这样的事情,并且(希望)找到了解决方案?

【问题讨论】:

您是否保存每次更改的上下文? Textfield 失去焦点是 NSManagedObjectContext 保存的正常行为。检查 NSEditor 协议以获取更多信息 不是每次更改都设置为保存时间延迟,并且仅当上下文发生更改时。我不知道 NSEditor 协议 - 我得重新考虑一下 - 谢谢! 我将 10.7 附带的自动保存功能用于基于 NSPersistentDocument 的应用程序。现在保存操作是自动处理的,我的文本字段在输入两个字符后总是失去焦点。也许我遗漏了一些东西,但我认为应该有一个简单的解决方法。有什么建议吗? 【参考方案1】:

我最近遇到了这个问题,并通过更改 NSTextField 绑定到 NSManagedObject 属性的方式来修复它。我没有将文本字段的值绑定到 NSArrayController 的 selection.[attribute] 键路径,而是绑定了视图控制器的 arrayController.selection.[attribute] keyPath,该视图控制器具有指向控制器的正确出口。

由于某种原因,如果以这种方式绑定保存 NSManagedObjectContext 时,NSTextField 不会失去焦点。

【讨论】:

它也解决了我的问题。我过去已经使用过那些将绑定解耦的技巧。我想知道是否有人在这里看到了真正的问题。在我看来这应该没问题(显然,如果只有一个 UI 可以更改 TextField 的底层核心数据属性)! 当文本字段位于表格视图中的文本字段单元格内时,你会怎么做? 我遇到了与@Sbhklr 相同的问题,我的文本字段位于基于视图的 NSTableView 的视图内。【参考方案2】:

我想分享我的解决方案。它适用于所有领域,无需修改。 我已经针对这篇文章进行了优化,并删除了一些错误检查、日志记录和线程安全。

- (BOOL)saveChanges:(NSError **)outError 
  BOOL result = YES;
  @try 
    NSError *error = nil; 
    if ([self hasChanges])  

    // Get field editor
    NSResponder *responder = [[NSApp keyWindow] firstResponder];
    NSText *editor = [[NSApp keyWindow] fieldEditor: NO forObject: nil];
    id editingObject = [editor delegate];
    BOOL isEditing = (responder == editor);
    NSRange range;
    NSInteger editedRow, editedColumn;

   // End editing to commit the last changes
   if (isEditing) 

     // Special case for tables
     if ([editingObject isKindOfClass: [NSTableView class]]) 
       editedRow = [editingObject editedRow];
       editedColumn = [editingObject editedColumn];
     

     range = [editor selectedRange];
     [[NSApp keyWindow] endEditingFor: nil];
   

   // The actual save operation
   if (![self save: &error]) 
     if (outError != nil)
        *outError = error;
      result = NO;
     else 
      result = YES;
    

    // Now restore the field editor, if any.
    if (isEditing) 
      [[NSApp keyWindow] makeFirstResponder: editingObject];
      if ([editingObject isKindOfClass: [NSTableView class]])
        [editingObject editColumn: editedColumn row: editedRow withEvent: nil select: NO];
        [editor setSelectedRange: range];
      
    
   @catch (id exception) 
    result = NO;
  
  return result;

【讨论】:

当然编辑点可能不在末尾(或者甚至可能有一个选择)。只需在结束编辑之前捕获 [editor selectedRange] NSRange,然后使用保存的 NSRange [editor setSelectedRange:]。 你是对的。我已经改进了代码并添加了对 NSTableView 的支持。我现在已经编辑了我的答案与你分享。请注意,这是我实际代码的精简版,其中还包括线程安全和错误处理。【参考方案3】:

好的,感谢 Martin 指出我应该更仔细地阅读文档。这是预期的行为,这就是我为解决它所做的(根据您的判断这是否适合您):

我每 3 秒保存一次上下文,在开始对我的 NSManagedObjectContext 执行实际的 save: 方法之前检查上下文是否有任何变化。我在通过以下方法修改的 Core Data 控制器类中添加了一个简单的递增/递减 NSUInteger (_saveDisabler):

- (void)enableSaves 
    if (_saveDisabler > 0) 
        _saveDisabler -= 1;
       


- (void)disableSaves 
    _saveDisabler += 1;

然后我在我的自定义 saveContext 方法中所做的就是在顶部做一个简单的检查:

if (([moc hasChanges] == NO) || (_saveDisabler > 0)) 
    return YES;

这可以防止保存发生,并意味着不会从我的任何自定义文本字段子类中窃取焦点。为了完整起见,我还继承了 NSTextField 并通过以下方法在我的核心数据控制器中启用/禁用保存:

- (void)textDidBeginEditing:(NSNotification *)notification;
- (void)textDidEndEditing:(NSNotification *)notification;

这可能有点乱,但它对我有用。如果有人以另一种方式成功地做到了这一点,我很想听到更清洁/不那么复杂的方法。

【讨论】:

这样做的缺点是,如果用户习惯于始终将光标留在文本字段中,您将永远无法保存。我通常采用拥有所有文本字段/令牌字段/等的方法。绑定设置为不断更新,然后类似于 @cocoafan 来恢复 firstResponder 和选择。

以上是关于NSManagedObjectContext 保存导致 NSTextField 失去焦点的主要内容,如果未能解决你的问题,请参考以下文章

CoreData 多 NSManagedObjectContext 保存通知说明

私有队列 NSManagedObjectContext 在无限循环中保存结束

用于保存的 NSManagedObjectContext 进度表?

NSManagedObjectContext 保存方法抛出异常

保存时的 NSManagedObjectContext 层次结构

iOS - 核心数据 - NSManagedObjectContext - 不确定是不是保存