views:

27

answers:

2

Hi there,

I have an nstableview in my application containing several columns. When I click on one column header in order to have it sorted ascending, rows with a nil-value in that column are being sorted on top followed by non-nil-valued rows in ascending order. If I reclick on the same column, first the non-nil-valued rows are being shown in descending order followed by nil-valued rows.

What I want to do is having nil-valued rows always sticked to the bottom. So at first non-nil-valued rows being sorted in either ascending or descending way should be presented and nil-valued rows should be kept at the bottom. How can this be done the best way?

Sorting in this context - to my understand - is done by the nsarraycontroller's arrangedObject. So should I subclass nsarraycontroller and use a custom sorting algorithm in arrangedObjects-method, or is there a better way to get the result described above?

Thanks in advance.

Yours,

Marius

A: 

I implemented this in an application of my own by creating a custom subclass of NSSortDescriptor, which is ultimately what NSArrayController uses to perform its sorting. The implementation is below, it's not terribly complicated, but note that it sorts empty strings to the end along with nil strings. Unfortunately there's no way (that I know of, at least) to specify a sort descriptor subclass in Interface Builder, so you'll have to manually create one and call setSortDescriptorPrototype: on the appropriate NSTableColumn in your awakeFromNib method.

#define NULL_OBJECT(a) ((a) == nil || [(a) isEqual:[NSNull null]])

@interface MySortDescriptor : NSSortDescriptor {}
@end

@implementation MySortDescriptor

- (id)copyWithZone:(NSZone*)zone
{
    return [[[self class] alloc] initWithKey:[self key] ascending:[self ascending] selector:[self selector]];
}

- (id)reversedSortDescriptor
{
    return [[[[self class] alloc] initWithKey:[self key] ascending:![self ascending] selector:[self selector]] autorelease];
}

- (NSComparisonResult)compareObject:(id)object1 toObject:(id)object2
{
    if (NULL_OBJECT([object1 valueForKeyPath:[self key]]) || 
        [[object1 valueForKeyPath:[self key]] length] == 0)
        return ([self ascending] ? NSOrderedDescending : NSOrderedAscending);
    if (NULL_OBJECT([object2 valueForKeyPath:[self key]]) ||
        [[object2 valueForKeyPath:[self key]] length] == 0)
        return ([self ascending] ? NSOrderedAscending : NSOrderedDescending);
    return [super compareObject:object1 toObject:object2];
}

@end
Brian Webster
A: 

Thanks a lot. As I mentioned above I always want nil-values sorted to the bottom I used your code with compareObject: in the following way.

- (NSComparisonResult)compareObject:(id)object1 toObject:(id)object2 {
    if (NULL_OBJECT([object1 valueForKeyPath:[self key]]) && NULL_OBJECT([object2 valueForKeyPath:[self key]]))
        return NSOrderedSame;
    if (NULL_OBJECT([object1 valueForKeyPath:[self key]]))
        return NSOrderedDescending;
    if (NULL_OBJECT([object2 valueForKeyPath:[self key]]))
        return NSOrderedAscending;
    return [super compareObject:object1 toObject:object2];
}
mheidenstecker