CATiledLayers на OS X

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

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

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

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

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

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

Большинство примеров в Интернете относятся к iOS и, таким образом, используют UIScrollView для масштабирования / панорамирования, но я не могу скопировать это поведение для NSScrollView. Единственный найденный пример для Mac OS X I - это, но его масштабирование всегда идет в левый нижний угол, а не в середину, и когда я адаптирую код для использования файлов 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;
}

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

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

1 ответ

Решение

После долгих исследований и попыток мне наконец удалось заставить это работать на этом примере. Решил опубликовать его для дальнейшего использования. Откройте 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);

    }
}
Другие вопросы по тегам