Search code examples
cocoanstableviewcocoa-bindingsnsarraycontrollernssortdescriptor

Setting and maintaining the first SortDescriptor for an NSTableView


I have an NSTableView with columns bound to an NSArrayController.

The table view shows a list of email messages:

  • Flag if unread
  • Subject
  • Attachment size

The user can click on the Attachment Size column to sort the list, but I would like the table to always be sorted by the "unread" flag first so that the unread messages always remain at the top of the list.

I did not bind the Array Controller's sort descriptors to anything, yet table sorting works magically by clicking on the table columns (why?). Is there some way I can intercept setting the Array Controller's sort descriptors and insert the "Unread" sort descriptor first?

Example of a table sorted by attachment size:

UNREAD▼ SUBJECT ATTACHMENT SIZE▼
------  ------- ------------------
  yes   Hello.. 110kb
  yes   Test...  90kb

  no    Foobar  200kb
  no    Hey     100kb
  no    Test2    10kb

Solution

  • Well, the reason it "just works" is because the table columns call setSortDescriptors: on their bound NSArrayController.

    Assuming you want the table to remain sortable, but you always want to sort by "unread", this is how I would go about it:

    First, subclass NSArrayController and override arrangeObjects:

    - (NSArray *)arrangeObjects:(NSArray *)objects {
    
        NSMutableArray *oldSorted = [[super arrangeObjects:objects] mutableCopy];
        NSMutableArray *newSorted = [NSMutableArray arrayWithCapacity:[oldSorted count]];
    
        for (id anObject in oldSorted)
            if ([[anObject valueForKey:@"isUnread"] boolValue])
                [newSorted addObject:anObject];
    
        [oldSorted removeObjectsInArray:newSorted];
        [newSorted addObjectsFromArray:oldSorted];
        [oldSorted release];
    
        return newSorted;
    }
    

    This puts unread messages at the "top" (beginning of array). I'm not sure this is the most efficient sorting algorithm, but I believe it's the correct way to go about it.