5

Я ищу способ реализовать что-то вроде многоразовых ячеек для UI/NSTableView, но для NSScrollView. В принципе, я хочу того же, что и видео WWDC 2011 «Session 104 - Advanced Scroll View Techniques», но для Mac.NSScrollView бесконечный/бесконечный свиток | subview reuse

У меня есть несколько проблем, связанных с этим. Первое: NSScrollView не имеет -layoutSubviews. Я пытался использовать -adjustScroll вместо но не в определении другого 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; 
} 

Следующая вещь, которую я попытался было установить действительно огромное содержание представление с width миллионов пикселя (который на самом деле работает, по сравнению с прошивкой!), но теперь вопрос заключается в том, как установить пул повторного использования?
Лучше ли перемещать подзоны во время прокрутки в новое положение или удалить все подпункты и вставить их снова? и как и где я должен это делать?

ответ

2

Насколько я могу судить, -adjustScroll: не там, где вы хотите использовать события прокрутки, потому что он не называется универсальным. Я думаю, что -reflectScrolledClipView:, вероятно, лучший пункт подключения.

Я приготовил следующий пример, который должен ударить по высоким точкам одним способом, чтобы просмотреть вид прокрутки. Для простоты я устанавливаю размеры документаViewrollViewView «огромным», как вы предлагаете, вместо того, чтобы пытаться «подделать» поведение прокрутки, чтобы выглядеть бесконечно. Очевидно, что рисование составных фрагментов по реальности зависит от вас. (В этом примере я создал фиктивную точку зрения, что только заполнится красный с синим контуром, чтобы убедить себя, что все работает.) Получилось так:

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

Это работало довольно хорошо, насколько я мог сказать, , Опять же, рисование чего-то значимого в повторных представлениях - вот где настоящая работа здесь.

Надеюсь, что это поможет.

+0

работает как очарование, спасибо. Будут углубляться в следующие дни, чтобы поддерживать автоматизацию и другие настройки, которые я сделал =) –