2014-01-12 7 views
5

Это привело меня в сумасшествие. У меня большое изображение, и мне нужно иметь вид, который можно масштабировать и прокручивать (в идеале он также должен быть способен вращаться, но я отказался на этой части). Поскольку изображение очень велико, я планирую использовать CATiledLayer, но я просто не могу заставить его работать. Мои требования:CATiledLayers on OS X

  • Мне нужно, чтобы иметь возможность увеличить (по центру мыши) и панорамирование

  • Изображение не должно изменять его ширину: соотношение высоты (не следует изменять размеры, только масштаб) ,

  • Это должно работать на Mac OS 10.9 (НЕ IOS!)

  • использование памяти не должно быть огромным (хотя до нравится 100 МБ должно быть в порядке).

У меня есть необходимое изображение, полное в одном файле, а также разбитое на многие (даже для разных уровней масштабирования). Я предпочитаю использовать плитки, поскольку это должно быть проще в памяти, но доступны оба варианта.

Большинство примеров в Интернете относятся к iOS и, таким образом, используют UIScrollView для масштабирования/панорамирования, но я не могу скопировать это поведение для NSScrollView. Единственным примером для Mac OS XI является this, но его масштабирование всегда идет в левом нижнем углу, а не в середине, и когда я адаптирую код для использования файлов png вместо pdf, использование памяти составляет около 400 МБ ...

Это мой лучший попробовать до сих пор:

@implementation MyView{ 
    CATiledLayer *tiledLayer; 
} 
-(void)awakeFromNib{ 
    NSLog(@"Es geht los"); 
    tiledLayer = [CATiledLayer layer]; 

    // set up this view & its layer 
    self.wantsLayer = YES; 
    self.layer = [CALayer layer]; 
    self.layer.masksToBounds = YES; 
    self.layer.backgroundColor = CGColorGetConstantColor(kCGColorWhite); 

    // set up the tiled layer 
    tiledLayer.delegate = self; 
    tiledLayer.levelsOfDetail = 4; 
    tiledLayer.levelsOfDetailBias = 5; 
    tiledLayer.anchorPoint = CGPointZero; 
    tiledLayer.bounds = CGRectMake(0.0f, 0.0f, 41*256, 22*256); 
    tiledLayer.autoresizingMask = kCALayerNotSizable; 
    tiledLayer.tileSize = CGSizeMake(256, 256); 
    self.frame = CGRectMake(0.0f, 0.0f, 41*256, 22*256); 
    self.layer = tiledLayer; 
    //[self.layer addSublayer:tiledLayer]; 
    [tiledLayer setNeedsDisplay]; 

} 

-(void)drawRect:(NSRect)dirtyRect{ 
    CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort]; 


    CGFloat scale = CGContextGetCTM(context).a; 

    CGSize tileSize = tiledLayer.tileSize; 


    tileSize.width /= scale; 
    tileSize.height /= scale; 

    // calculate the rows and columns of tiles that intersect the rect we have been asked to draw 
    int firstCol = floorf(CGRectGetMinX(dirtyRect)/tileSize.width); 
    int lastCol = floorf((CGRectGetMaxX(dirtyRect)-1)/tileSize.width); 
    int firstRow = floorf(CGRectGetMinY(dirtyRect)/tileSize.height); 
    int lastRow = floorf((CGRectGetMaxY(dirtyRect)-1)/tileSize.height); 

    for (int row = firstRow; row <= lastRow; row++) { 
     for (int col = firstCol; col <= lastCol; col++) { 
      NSImage *tile = [self tileForScale:scale row:row col:col]; 
      CGRect tileRect = CGRectMake(tileSize.width * col, tileSize.height * row, 
             tileSize.width, tileSize.height); 

      // if the tile would stick outside of our bounds, we need to truncate it so as 
      // to avoid stretching out the partial tiles at the right and bottom edges 
      tileRect = CGRectIntersection(self.bounds, tileRect); 

      [tile drawInRect:tileRect]; 
     } 
    } 
} 

-(BOOL)isFlipped{ 
    return YES; 
} 

но это деформирует изображение, а не увеличить или сковороду правильно (но, по крайней мере, выбор плитки работает) ...

I не могу поверить, что это так сложно, любая помощь будет очень признательна. Спасибо :)

ответ

3

После много исследований и попыток, я, наконец, смог заставить это работать, используя пример this. Решил опубликовать его для дальнейшего использования. Откройте ZIP> CoreAnimationLayers> TiledLayers, там есть хороший пример. Вот как работает CATiledLayer с OS X, и поскольку пример не очень хорошо справляется с масштабированием, я оставляю здесь код масштабирования

-(void)magnifyWithEvent:(NSEvent *)event{ 
    [super magnifyWithEvent:event]; 

    if (!isZooming) { 
     isZooming = YES; 

     BOOL zoomOut = (event.magnification > 0) ? NO : YES; 

     if (zoomOut) { 
      [self zoomOutFromPoint:event.locationInWindow]; 

     } else { 
      [self zoomInFromPoint:event.locationInWindow];; 
     } 

    } 

} 

-(void)zoomInFromPoint:(CGPoint)mouseLocationInWindow{ 
    if(zoomLevel < pow(2, tiledLayer.levelsOfDetailBias)) { 
     zoomLevel *= 2.0f; 

     tiledLayer.transform = CATransform3DMakeScale(zoomLevel, zoomLevel, 1.0f); 
     tiledLayer.position = CGPointMake((tiledLayer.position.x*2) - mouseLocationInWindow.x, (tiledLayer.position.y*2) - mouseLocationInWindow.y); 

    } 

} 

-(void)zoomOutFromPoint:(CGPoint)mouseLocationInWindow{ 
    NSInteger power = tiledLayer.levelsOfDetail - tiledLayer.levelsOfDetailBias; 
    if(zoomLevel > pow(2, -power)) { 
     zoomLevel *= 0.5f; 

     tiledLayer.transform = CATransform3DMakeScale(zoomLevel, zoomLevel, 1.0f); 
     tiledLayer.position = CGPointMake((tiledLayer.position.x + mouseLocationInWindow.x)/2, (tiledLayer.position.y + mouseLocationInWindow.y)/2); 

    } 
}