5

Tôi đang tìm cách triển khai một cái gì đó giống như các tế bào tái sử dụng cho UI/NSTableView nhưng đối với NSScrollView. Về cơ bản tôi muốn giống như các video WWDC 2011 "Phiên 104 - Advanced Scroll View Kỹ thuật" nhưng đối với Mac.NSScrollXem cuộn vô hạn/vô hạn | subview reuse

Tôi gặp một số sự cố khi nhận ra điều này. Đầu tiên: NSScrollView không có -layoutSubviews. Tôi cố gắng để sử dụng -adjustScroll thay nhưng thất bại trong việc thiết lập một khác nhau 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; 
} 

Điều tiếp theo tôi đã cố gắng là để thiết lập một cái nhìn nội dung thực sự khổng lồ với một width triệu pixel (mà thực sự hoạt động so với iOS!) nhưng bây giờ câu hỏi là, làm thế nào để cài đặt một hồ bơi tái sử dụng?
Bạn có nên di chuyển các bản xem trước trong khi cuộn đến vị trí mới hoặc xóa tất cả các bản xem lại và chèn lại chúng không? và tôi nên làm như thế nào và ở đâu?

Trả lời

2

Tốt nhất tôi có thể biết, -adjustScroll: không phải là nơi bạn muốn nhấn vào các sự kiện cuộn vì nó không được gọi chung. Tôi nghĩ rằng -reflectScrolledClipView: có lẽ là một điểm hookup tốt hơn.

Tôi đã nấu một ví dụ sau đây để đạt được điểm cao của một cách để thực hiện chế độ xem cuộn xem lại. Để đơn giản, tôi đặt kích thước của documentView của scrollView thành "to", như bạn đề xuất, thay vì cố gắng "giả mạo" hành vi cuộn để nhìn vô hạn. Rõ ràng là việc vẽ các chế độ xem dạng thành phần cho thực tế là tùy thuộc vào bạn. (Trong ví dụ này tôi tạo ra một cái nhìn giả mà chỉ lấp đầy chính nó với màu đỏ với một phác thảo màu xanh để thuyết phục bản thân mình rằng mọi thứ đã được làm việc.) Nó xuất hiện như thế này:

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

này làm việc khá tốt là tốt nhất tôi có thể nói . Một lần nữa, vẽ một cái gì đó có ý nghĩa trong quan điểm tái sử dụng là nơi mà công việc thực sự là ở đây.

Hy vọng điều đó sẽ hữu ích.

+0

hoạt động như một sự quyến rũ, cảm ơn bạn. Sẽ đào sâu hơn vào những ngày tiếp theo để hỗ trợ tự động hóa và các tùy chỉnh khác mà tôi đã thực hiện =) –

Các vấn đề liên quan