Question

This is a really strange problem I'm seeing in my app. I have an NSTextField bound to an attribute of an NSManagedObject, but whenever the object is saved the textfield loses focus. I'm continuously updating the value of the binding, so this is far from ideal.

Has anyone seen anything like this before, and (hopefully) found a solution?

Was it helpful?

Solution 3

OK, so thanks to Martin for pointing out that I should read the docs a little more closely. This is expected behaviour, and here's what I did to get around it (use your judgement as to whether this is appropriate for you):

I save my context once every 3 seconds, checking at the start if the context has any changes before I bother executing the actual save: method on my NSManagedObjectContext. I added a simple incrementing/decrementing NSUInteger (_saveDisabler) to my Core Data controller class that is modified via the following methods:

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

- (void)disableSaves {
    _saveDisabler += 1;
}

Then all I do in my custom saveContext method is do a simple check at the top:

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

This prevents the save from occurring, and means that the focus is not stolen from any of my custom textfield subclasses. For completeness, I also subclassed NSTextField and enable/disable saves in my Core Data controller from the following methods:

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

It might be a little messy, but it works for me. I'm keen to hear of cleaner/less convoluted methods if anyone has done this successfully in another way.

OTHER TIPS

I encountered the issue recently and fixed it by changing the way the NSTextField was bound to the NSManagedObject attribute. Instead of binding the value of the text field to the selection.[attribute] key path of the NSArrayController, I bound the arrayController.selection.[attribute] keyPath of the view controller that had a proper outlet pointing to the controller.

For some reason, the NSTextField doesn't loose focus when the NSManagedObjectContext is saved if bound this way.

I want to share my solution. It will work for all fields without modification. I have optimized it for this posting and removed some error checking, logging and thread safety.

- (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;
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top