Maintaining NSTextView Insertion Point After Core Data Save

November 21st, 2016

Filed under: Cocoa, Mac Development | 1 comment

I am working on a Mac journaling application that uses Core Data. The user interface consists of a table view and a text view. The table view contains a list of dates. Selecting a date fills the text view with what was written on that date. Use the text view to write.

One problem I had was saving the text. Core Data is set up initially to save a text view or text field’s contents only when leaving the text view or text field. This behavior works fine for a contact form, but not for a journaling application. Core Data would save the text only when I selected a new date in the table view. With this behavior there’s potential for data loss.

I wrote some code to autosave the text view’s contents periodically. The saving worked, but every time I saved, the text view lost focus. When the text view lost focus, I had to click in the text view to resume typing. Autosaving provided a miserable typing experience. How could I autosave while allowing someone to keep typing? I had to perform the following steps:

  1. Call the NSTextView function selectedRange to save where the person was typing during the autosave.
  2. Call the NSWindow function makeFirstResponder to make the text view the first responder so the text view didn’t lose focus.
  3. Call the NSTextView function setSelectedRange to set the typing position back to where it was before the autosave.
  4. Call the NSText function scrollRangeToVisible to make the text view scroll to the point where the person was typing.

Here’s some Swift 3 code.

func updateTextViewAfterAutosave() {
    let insertionRange = textView.selectedRange()

Make sure you don’t autosave too often. When I used this code to autosave after every change to the text view’s contents, the text view scrolling was visibly jumpy. Autosaving every 30 seconds worked well.


One thought on “Maintaining NSTextView Insertion Point After Core Data Save

  1. Bernard Fischli says:

    You can also override saveDocument like this:

    – (void) saveDocument:(id)sender
    NSWindow *myWindow = self.windowControllers.firstObject.window;
    NSResponder *responder = myWindow.firstResponder;
    if ([responder isKindOfClass:[NSTextView class]])
    [myWindow makeFirstResponder:nil];
    [super saveDocument:sender];
    [myWindow makeFirstResponder:responder];
    [super saveDocument:sender];

Leave a Reply

Your email address will not be published. Required fields are marked *