Displaying row index in an NSTableView bound to NSArrayController

后端 未结 3 1341
一生所求
一生所求 2021-02-09 13:18

I have an NSTableView which is bound to an NSArrayController. I would like to have one of the table columns showing the index of the table row. This is easy enough to do when yo

3条回答
  •  陌清茗
    陌清茗 (楼主)
    2021-02-09 14:16

    I recently implemented this using an NSRuler subclass that draws the line numbers next to each line in the TableView. I based the code on something similar that I found here.

    You can add this to your tableview using:

    NSScrollView *scrollView = [tableView enclosingScrollView];
    TableLineNumberRulerView *lineNumberView = [[TableLineNumberRulerView alloc] initWithTableView:tableView
                                                                               usingArrayController:arrayController];
    
    [scrollView setVerticalRulerView:lineNumberView];
    [scrollView setHasVerticalRuler:YES];
    [scrollView setRulersVisible:YES];
    

    Here's the interface file:

    //
    //  TableLineNumberRulerView
    //  Line View Test
    //
    //  Created by Ben Golding, Object Craft Pty Ltd on 7 May 2014.
    //  Based on code by Paul Kim on 9/28/08.
    
    
    #import 
    
    @interface TableLineNumberRulerView : NSRulerView
    
    @property (strong) NSArrayController *arrayController;
    
    @property (strong) NSFont       *font;
    @property (strong) NSColor  *textColor;
    @property (strong) NSColor  *alternateTextColor;
    @property (strong) NSColor  *backgroundColor;
    @property (strong) NSDictionary *textAttributes;
    @property (assign) NSUInteger   rowCount;
    
    - (id)initWithTableView:(NSTableView *)tableView  usingArrayController:(NSArrayController *)arrayController;
    
    @end
    

    Here's the implementation:

    //
    //  TableLineNumberRulerView.m
    //  Line View Test
    //
    //  Created by Ben Golding, Object Craft Pty Ltd on 7 May 2014.
    //  Based on code by Paul Kim on 9/28/08.
    
    #import "TableLineNumberRulerView.h"
    
    #define DEFAULT_THICKNESS   22.0
    #define RULER_MARGIN        5.0
    
    @implementation TableLineNumberRulerView
    
    @synthesize font;
    @synthesize textColor;
    @synthesize alternateTextColor;
    @synthesize backgroundColor;
    @synthesize textAttributes;
    @synthesize rowCount;
    
    
    - (id)initWithTableView:(NSTableView *)tableView usingArrayController:(NSArrayController *)arrayController
    {
        NSScrollView *scrollView = [tableView enclosingScrollView];
    
        if ((self = [super initWithScrollView:scrollView orientation:NSVerticalRuler]) == nil)
            return nil;
    
        [self setClientView:tableView];
    
        self.arrayController = arrayController;
        [arrayController addObserver:self forKeyPath:@"arrangedObjects" options:NSKeyValueObservingOptionNew context:nil];
    
        self.font = [NSFont labelFontOfSize:[NSFont systemFontSizeForControlSize:NSMiniControlSize]];
        self.textColor = [NSColor colorWithCalibratedWhite:0.42 alpha:1.0];
        self.alternateTextColor = [NSColor whiteColor];
        self.textAttributes = @{
            NSFontAttributeName: [self font],
            NSForegroundColorAttributeName: [self textColor]
        };
    
        self.rowCount = [[arrayController arrangedObjects] count];
    
        return self;
    }
    
    - (void)awakeFromNib
    {
        [self setClientView:[[self scrollView] documentView]];      // this will be an NSTableView instance
    }
    
    - (void)finalize
    {
        [self.arrayController removeObserver:self forKeyPath:@"arrangedObjects"];
    }
    
    #pragma mark -
    #pragma mark Key-Value observing of changes to array controller
    
    /*
     * This picks up changes to the arrayController's arrangedObjects using KVO.
     * We check the size of the old and new rowCounts and compare them to see if the number
     * digits has changed, and if so, we adjust the ruler width.
     */
    
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
    {
        if ([keyPath isEqualToString:@"arrangedObjects"]) {
            NSUInteger newRowCount = [[self.arrayController arrangedObjects] count];
    
            if ((int)log10(self.rowCount) != (int)log10(newRowCount))
                [self setRuleThickness:[self requiredThickness]];
            self.rowCount = newRowCount;
            // we need to redisplay because line numbers may change or disappear in view
            [self setNeedsDisplay:YES];
        }
    }
    
    
    - (CGFloat)requiredThickness
    {
        NSUInteger      lineCount = [[self.arrayController arrangedObjects] count],
                        digits = (unsigned)log10((lineCount < 1) ? 1: lineCount) + 1;
        NSMutableString *sampleString = [NSMutableString string];
        NSSize          stringSize;
    
        for (NSUInteger i = 0; i < digits; i++) {
            // Use "8" since it is one of the fatter numbers. Anything but "1"
            // will probably be ok here. I could be pedantic and actually find the fattest
            // number for the current font but nah.
            [sampleString appendString:@"8"];
        }
    
        stringSize = [sampleString sizeWithAttributes:[self textAttributes]];
    
        // Round up the value. There is a bug on 10.4 where the display gets all wonky when scrolling if you don't
        // return an integral value here.
        return ceil(MAX(DEFAULT_THICKNESS, stringSize.width + RULER_MARGIN * 2));
    }
    
    - (void)drawHashMarksAndLabelsInRect:(NSRect)aRect
    {
        NSTableView *tableView = (NSTableView *)[self clientView];
        NSRect bounds = [self bounds];
        NSRect visibleRect = [[tableView enclosingScrollView] documentVisibleRect];
        NSRange visibleRowRange = [tableView rowsInRect:visibleRect];
        CGFloat yinset = NSHeight([[tableView headerView] bounds]);
    
        if (backgroundColor != nil) {
            [backgroundColor set];
            NSRectFill(bounds);
    
            [[NSColor colorWithCalibratedWhite:0.58 alpha:1.0] set];
            [NSBezierPath strokeLineFromPoint:NSMakePoint(NSMaxX(bounds) - 0/5, NSMinY(bounds))
                                      toPoint:NSMakePoint(NSMaxX(bounds) - 0.5, NSMaxY(bounds))];
        }
    
    //    NSLog(@"drawHashMarksAndLabelsInRect: bounds %@, ruleThickness %lf", NSStringFromRect(bounds), [self ruleThickness]);
    
        for (NSUInteger row = visibleRowRange.location; NSLocationInRange(row, visibleRowRange); row++) {
            // Line numbers are internally stored starting at 0
            NSString *labelText = [NSString stringWithFormat:@"%lu", row + 1];
            NSSize stringSize = [labelText sizeWithAttributes:self.textAttributes];
            NSRect rowRect = [tableView rectOfRow:row];
            CGFloat ypos = yinset + NSMinY(rowRect) - NSMinY(visibleRect);
    
            [labelText drawInRect:NSMakeRect(NSWidth(bounds) - stringSize.width - RULER_MARGIN,
                                             ypos + (NSHeight(rowRect) - stringSize.height) / 2.0,
                                             NSWidth(bounds) - RULER_MARGIN * 2.0, NSHeight(rowRect))
                   withAttributes:self.textAttributes];
        }
    }
    
    #pragma mark -
    #pragma mark NSCoding methods
    
    #define FONT_CODING_KEY         @"font"
    #define TEXT_COLOR_CODING_KEY       @"textColor"
    #define ALT_TEXT_COLOR_CODING_KEY   @"alternateTextColor"
    #define BACKGROUND_COLOR_CODING_KEY @"backgroundColor"
    
    - (id)initWithCoder:(NSCoder *)decoder
    {
        if ((self = [super initWithCoder:decoder]) != nil) {
            if ([decoder allowsKeyedCoding]) {
                font = [decoder decodeObjectForKey:FONT_CODING_KEY];
                textColor = [decoder decodeObjectForKey:TEXT_COLOR_CODING_KEY];
                alternateTextColor = [decoder decodeObjectForKey:ALT_TEXT_COLOR_CODING_KEY];
                backgroundColor = [decoder decodeObjectForKey:BACKGROUND_COLOR_CODING_KEY];
            } else {
                font = [decoder decodeObject];
                textColor = [decoder decodeObject];
                alternateTextColor = [decoder decodeObject];
                backgroundColor = [decoder decodeObject];
            }
        }
        return self;
    }
    
    - (void)encodeWithCoder:(NSCoder *)encoder
    {
        [super encodeWithCoder:encoder];
    
        if ([encoder allowsKeyedCoding]) {
            [encoder encodeObject:font forKey:FONT_CODING_KEY];
            [encoder encodeObject:textColor forKey:TEXT_COLOR_CODING_KEY];
            [encoder encodeObject:alternateTextColor forKey:ALT_TEXT_COLOR_CODING_KEY];
            [encoder encodeObject:backgroundColor forKey:BACKGROUND_COLOR_CODING_KEY];
        } else {
            [encoder encodeObject:font];
            [encoder encodeObject:textColor];
            [encoder encodeObject:alternateTextColor];
            [encoder encodeObject:backgroundColor];
        }
    }
    
    @end
    

提交回复
热议问题