2010-01-02 10 views
5

Possiedo un NSTableView associato a un controller NSArray. Mi piacerebbe avere una delle colonne della tabella che mostra l'indice della riga della tabella. Questo è abbastanza facile da fare quando si implementa NSTableDataSource da soli, ma non riesco a capirlo con una vista tabella associata. Suppongo che sto cercando qui qualcosa come il percorso chiave @count che mi dà il conteggio di arrangedObjects (che è @index), ma questo è ovviamente mancante.Visualizzazione dell'indice di riga in un NSTableView associato a NSArrayController

Due precisazioni:

  1. L'indice che viene mostrata in ciascuna riga è l'indice di quella riga e non correlati affatto al modo in cui i dati sono effettivamente disposto nel controller modello o matrice. Ad esempio, se l'intero dato è di 10000 elementi, gli indici dovrebbero andare da 1 a 10000, se l'utente inserisce un termine di ricerca e la tabella mostra solo 4 degli elementi, allora i numeri dovrebbero andare da 1 a 4, anche se il gli oggetti mostrati sono in realtà da tutto l'array originale.
  2. Ho bisogno di questo perché mi è stato chiesto di farlo dal cliente :-). Il cliente ha bisogno di un modo per essere in grado di stimare il numero di righe prima o dopo una determinata riga. Ad esempio, potrebbe voler sapere quante righe sono state aggiunte.
+0

Qual è il motivo? L'unica app che riesco a pensare che funzioni oggi è iTunes e non ho mai trovato uno scopo per quella funzione. –

risposta

3

Come ho capito, è possibile scegliere di non associare quella colonna di tabella e utilizzare invece un'origine dati. Ricordo che NSTableView supporta questo tipo di operazione "dual mode", ma non riesce a trovare alcun documento per confermarlo.

+0

Grazie! la soluzione era abbastanza semplice. Ho aggiunto una colonna che non era vincolata a nulla, quindi ho impostato l'origine dataSource dell'intera vista tabella al proprietario del file e poi implementato NSTableDataSource restituendo l'indice della colonna quando richiesto per il contenuto della colonna. –

+0

Ho bisogno di fare questo, ma il mio metodo di datasource solo implementato viene chiamato per altre visualizzazioni di tabella e colonne. Il mio delegato/origine dati è stato originariamente contrassegnato per le tabelle che supportano il trascinamento della selezione. Non dovrei ricevere una sola callback per questa colonna in una singola tabella? Tutti gli altri colori tra le altre visualizzazioni di tabella hanno associazioni! – slashlos

+0

btw, il comportamento differisce notevolmente se la vista tabella è basata su cella (cronologia) o vista (nuova a partire da 10.6?). Apparentemente non è affatto fattibile con quest'ultimo, ma il comportamento delle celle a drag-n-drop (necessità di riorganizzazione dell'utente) è diverso. Sì, come iTunes è una sorta di playlist bisogno di dove l'ordine è la chiave. – slashlos

1

Supponendo che si miri a replicare il comportamento di iTunes, la colonna deve solo visualizzare 1 su (numero di righe visibili). In realtà non ha bisogno di relazionarsi con il tuo modello.

Quindi, assegnare al controller una proprietà di matrice generata dinamicamente e renderlo depend sulla matrice di oggetti del modello corrente.

Per questa proprietà, implementare array accessor methods, con il getter indicizzato, semplicemente calcolando idx + 1, box e recuperando tale oggetto. Potrebbe anche essere necessario implementare il getter dell'intero array per soddisfare KVC.

Assicurarsi che la colonna sia impostata come non modificabile e che l'insieme di rilegature non sia impostato in modo condizionale enabled. Altrimenti, riceverai un'eccezione (per non avere setter per questa proprietà) quando l'utente prova a inserire un nuovo indice per una riga.

Una nota di cautela: ciò potrebbe causare problemi poiché NSTableView non prevede che le sue colonne vengano associate a array discreti; si aspetta che tutte le sue colonne siano associate a diverse proprietà degli oggetti del modello in un singolo array. Potrebbe essere necessario associare lo content della vista tabella stessa alla serie di oggetti del modello, oltre a legare i collegamenti del contenuto delle colonne e I've had trouble with that before.

+0

Sto cercando di implementarlo, ma il link ora si interrompe e la ricerca di Apple su "Model Objects" non mostra nulla che sembri utile. –

+0

@boyfarrell: Grazie per l'heads-up. La ricerca che ho usato per trovare il nuovo collegamento era "metodi di accesso agli oggetti del modello". Ho aggiornato i collegamenti alla documentazione della mia risposta. –

2

Recentemente ho implementato questo utilizzando una sottoclasse NSRuler che disegna i numeri di riga accanto a ogni riga nel TableView. Ho basato il codice su qualcosa di simile che ho trovato here.

È possibile aggiungere questo al tuo Tableview utilizzando:

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

[scrollView setVerticalRulerView:lineNumberView]; 
[scrollView setHasVerticalRuler:YES]; 
[scrollView setRulersVisible:YES]; 

Ecco il file di interfaccia:

// 
// 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 <Cocoa/Cocoa.h> 

@interface TableLineNumberRulerView : NSRulerView<NSCoding> 

@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 

Ecco l'attuazione:

// 
// 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