在窗口调整大小时调整CALayer帧大小的方法?

时间:2014-06-07 18:33:48

标签: objective-c cocoa core-animation

我向各个CALayer子图层绘制一系列图像,然后将这些子图层添加到superlayer

- (void)renderImagesFromArray:(NSArray *)array {
    CALayer *superLayer = [CALayer layer];
    for (id object in array) {
        CALayer* subLayer = [CALayer layer];
        // Disregard...
        NSURL *path = [NSURL fileURLWithPathComponents:@[NSHomeDirectory(), @"Desktop", object]];
        NSImage *image = [[NSImage alloc] initWithContentsOfURL:path];
        [self positionImage:image layer:subLayer];
        subLayer.contents = image;
        subLayer.hidden = YES;
        [superLayer addSublayer:subLayer];
    }
    [self.view setLayer:superLayer];
    [self.view setWantsLayer:YES];
    // Show top layer
    CALayer *top = superLayer.sublayers[0];
    top.hidden = NO;
}

然后我调用[self positionImage: layer:]CALayer拉伸到它的最大边界(essentially using the algorithm for the CSS cover property),并将其放在窗口的中心位置:

- (void)positionImage:(NSImage *)image layer:(CALayer *)layer{
    float imageWidth = image.size.width;
    float imageHeight = image.size.height;
    float frameWidth = self.view.frame.size.width;
    float frameHeight = self.view.frame.size.height;
    float aspectRatioFrame = frameWidth/frameHeight;
    float aspectRatioImage = imageWidth/imageHeight;
    float computedImageWidth;
    float computedImageHeight;
    float verticalSpace;
    float horizontalSpace;
    if (aspectRatioImage <= aspectRatioFrame){
        computedImageWidth = frameHeight * aspectRatioImage;
        computedImageHeight = frameHeight;
        verticalSpace = 0;
        horizontalSpace = (frameWidth - computedImageWidth)/2;
    } else {
        computedImageWidth = frameWidth;
        computedImageHeight = frameWidth / aspectRatioImage;
        horizontalSpace = 0;
        verticalSpace = (frameHeight - computedImageHeight)/2;
    }
    [CATransaction flush];
    [CATransaction begin];
    CATransaction.disableActions = YES;
    layer.frame = CGRectMake(horizontalSpace, verticalSpace, computedImageWidth, computedImageHeight);
    [CATransaction commit];
}

除非调整窗口大小,否则一切正常。我通过子类化NSView解决了这个问题(以一种非常丑陋的方式),然后实现了在调整窗口大小时实际调用的唯一方法viewWillDraw:

- (void)viewWillDraw{
    [super viewWillDraw];
    [self redraw];
}

- (void)redraw{
    AppDelegate *appDelegate = (AppDelegate *)[[NSApplication sharedApplication] delegate];
    CALayer *superLayer = self.layer;
    NSArray *sublayers = superLayer.sublayers;
    NSImage *image;
    CALayer *current;
    for (CALayer *view in sublayers){
        if (!view.isHidden){
            current = view;
            image = view.contents;
        }
    }
    [appDelegate positionImage:image layer:current];
}

那么......这样做的正确方法是什么? viewWillDraw:被调用太多次,这意味着我必须进行不必要的冗余计算,而且我不能使用viewWillStartLiveResize:,因为我需要不断地将图像保持在正确的位置。我在俯瞰什么?

1 个答案:

答案 0 :(得分:3)

彼得霍西是对的;我原来的方法很笨重,我不应该压倒setNeedsDisplayInRect:。我首先确保我在我的应用程序中使用了自动布局,然后实现了以下内容:

subLayer.layoutManager  = [CAConstraintLayoutManager layoutManager];
subLayer.autoresizingMask = kCALayerHeightSizable | kCALayerWidthSizable;
subLayer.contentsGravity = kCAGravityResizeAspect;

基本上,我将子图层autoResizingMask设置为水平和垂直拉伸,然后设置contentsGravity以保留纵横比。

我偶然发现的最后一个变量,但值得注意的是,您可以only use a few contentsGravity constants if,就像我的情况一样,您将NSImage设置为图层&#39 ; s contents

  

该方法创建的图像适合用作图层的内容,并支持所有图层的重力模式。相比之下,NSImage类仅支持kCAGravityResize,kCAGravityResizeAspect和kCAGravityResizeAspectFill模式。

当复杂的解决方案可以简化为3行代码时,总是很有趣。