5

Sto cercando un modo per implementare qualcosa come celle riutilizzabili per UI/NSTableView ma per NSScrollView. Fondamentalmente voglio la stessa cosa del video WWDC 2011 "Session 104 - Advanced Scroll View Techniques" ma per Mac.NSScrollView scroll infinito/infinito | Riutilizzo secondario riutilizzo

Ho diversi problemi a rendermene conto. Il primo: NSScrollView non ha -layoutSubviews. Ho provato ad usare -adjustScroll invece, ma non riescono a impostare un diverso contentOffset:

- (NSRect)adjustScroll:(NSRect)proposedVisibleRect { 
    if (proposedVisibleRect.origin.x > 600) { 
     // non of them work properly 
     // proposedVisibleRect.origin.x = 0; 
     // [self setBoundsOrigin:NSZeroPoint]; 
     // [self setFrameOrigin:NSZeroPoint]; 
     // [[parentScrollView contentView] scrollPoint:NSZeroPoint]; 
     // [[parentScrollView contentView] setBoundsOrigin:NSZeroPoint]; 
    } 
    return proposedVisibleRect; 
} 

La prossima cosa che ho provato è stato quello di impostare una davvero enorme visualizzare il contenuto con un width di milioni di pixel (che funziona in realtà, rispetto a iOS!) ma ora la domanda è, come installare un pool di riutilizzo?
È meglio spostare le visualizzazioni secondarie mentre si scorre in una nuova posizione o rimuovere tutte le sottoview e inserirle di nuovo? e come e dove dovrei farlo?

risposta

2

Come meglio posso dire, -adjustScroll: non è dove si desidera toccare gli eventi di scorrimento perché non viene chiamato universalmente. Penso che -reflectScrolledClipView: sia probabilmente un punto di collegamento migliore.

Ho preparato il seguente esempio che dovrebbe raggiungere i punti più alti di un modo per eseguire una visualizzazione di riutilizzo della vista. Per semplicità, ho impostato le dimensioni della DocumentView di scrollView su "enorme", come suggerito, piuttosto che cercare di "falsificare" il comportamento di scorrimento per sembrare infinito. Ovviamente il disegno delle viste delle tessere costituenti è reale. (In questo esempio ho creato una vista fittizio che appena si riempie di rosso con un contorno blu di convincermi che tutto funzionava.) E 'uscito in questo modo:

// For the header file 
@interface SOReuseScrollView : NSScrollView 
@end 

// For the implementation file 
@interface SOReuseScrollView() // Private 

- (void)p_updateTiles; 
@property (nonatomic, readonly, retain) NSMutableArray* p_reusableViews; 

@end 

// Just a small diagnosting view to convince myself that this works. 
@interface SODiagnosticView : NSView 
@end 

@implementation SOReuseScrollView 

@synthesize p_reusableViews = mReusableViews; 

- (void)dealloc 
{ 
    [mReusableViews release]; 
    [super dealloc]; 
} 

- (NSMutableArray*)p_reusableViews 
{ 
    if (nil == mReusableViews) 
    { 
     mReusableViews = [[NSMutableArray alloc] init]; 
    } 
    return mReusableViews; 
} 

- (void)reflectScrolledClipView:(NSClipView *)cView 
{ 
    [super reflectScrolledClipView: cView]; 
    [self p_updateTiles]; 
} 

- (void)p_updateTiles 
{ 
    // The size of a tile... 
    static const NSSize gGranuleSize = {250.0, 250.0}; 

    NSMutableArray* reusableViews = self.p_reusableViews; 
    NSRect documentVisibleRect = self.documentVisibleRect; 

    // Determine the needed tiles for coverage 
    const CGFloat xMin = floor(NSMinX(documentVisibleRect)/gGranuleSize.width) * gGranuleSize.width; 
    const CGFloat xMax = xMin + (ceil((NSMaxX(documentVisibleRect) - xMin)/gGranuleSize.width) * gGranuleSize.width); 
    const CGFloat yMin = floor(NSMinY(documentVisibleRect)/gGranuleSize.height) * gGranuleSize.height; 
    const CGFloat yMax = ceil((NSMaxY(documentVisibleRect) - yMin)/gGranuleSize.height) * gGranuleSize.height; 

    // Figure out the tile frames we would need to get full coverage 
    NSMutableSet* neededTileFrames = [NSMutableSet set]; 
    for (CGFloat x = xMin; x < xMax; x += gGranuleSize.width) 
    { 
     for (CGFloat y = yMin; y < yMax; y += gGranuleSize.height) 
     { 
      NSRect rect = NSMakeRect(x, y, gGranuleSize.width, gGranuleSize.height); 
      [neededTileFrames addObject: [NSValue valueWithRect: rect]]; 
     } 
    } 

    // See if we already have subviews that cover these needed frames. 
    for (NSView* subview in [[[self.documentView subviews] copy] autorelease]) 
    { 
     NSValue* frameRectVal = [NSValue valueWithRect: subview.frame]; 

     // If we don't need this one any more... 
     if (![neededTileFrames containsObject: frameRectVal]) 
     { 
      // Then recycle it... 
      [reusableViews addObject: subview]; 
      [subview removeFromSuperview]; 
     } 
     else 
     { 
      // Take this frame rect off the To-do list. 
      [neededTileFrames removeObject: frameRectVal]; 
     } 
    } 

    // Add needed tiles from the to-do list 
    for (NSValue* neededFrame in neededTileFrames) 
    { 
     NSView* view = [[[reusableViews lastObject] retain] autorelease]; 
     [reusableViews removeLastObject]; 

     if (nil == view) 
     { 
      // Create one if we didnt find a reusable one. 
      view = [[[SODiagnosticView alloc] initWithFrame: NSZeroRect] autorelease]; 
      NSLog(@"Created a view."); 
     } 
     else 
     { 
      NSLog(@"Reused a view."); 
     } 

     // Place it and install it. 
     view.frame = [neededFrame rectValue]; 
     [view setNeedsDisplay: YES];   
     [self.documentView addSubview: view]; 
    } 
} 

@end 

@implementation SODiagnosticView 

- (void)drawRect:(NSRect)dirtyRect 
{ 
    // Draw a red tile with a blue border. 
    [[NSColor blueColor] set]; 
    NSRectFill(self.bounds); 

    [[NSColor redColor] setFill]; 
    NSRectFill(NSInsetRect(self.bounds, 2,2));  
} 

@end 

Questo ha funzionato abbastanza bene come meglio ho potuto dire . Di nuovo, disegnare qualcosa di significativo nelle viste riutilizzate è dove il vero lavoro è qui.

Spero che questo aiuti.

+0

funziona come un incanto, grazie. Scaverà più in profondità nei prossimi giorni per supportare l'autoresizing e altre personalizzazioni che ho fatto =) –