2

我正在编写一个 iPhone 应用程序,其中我使用 MKOverlayView 在 MKMapView 上放置一个大的 PNG 图像(1936 × 2967)。我对如何在 MKOverlayView 中适当地实现 drawMapRect: 函数有点困惑——我应该在绘制图像之前手动分割我的图像吗?或者我应该让 MKOverlayView 的机制来处理所有这些吗?

我从其他帖子中得到的印象是,在 MKOverlayView 可用之前,您应该为此类任务自己分割图像,并使用 CATiledLayer。我想也许 MKOverlayView 处理了所有的脏活。

我问的真正原因是,当我使用分配工具通过 Instruments 运行我的应用程序时,我发现我的应用程序使用的实时字节数随着地图上自定义图像的引入而稳步增加。现在我没有分割我的图像,但我也没有在 Instruments 的泄漏工具中看到内存泄漏的记录。这是我的 drawMapRect: 函数:

- (void)drawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale inContext:(CGContextRef)context{
    // Load image from applicaiton bundle
    NSString* imageFileName = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"map.png"];
    CGDataProviderRef provider = CGDataProviderCreateWithFilename([imageFileName UTF8String]);
    CGImageRef image = CGImageCreateWithPNGDataProvider(provider, NULL, true, kCGRenderingIntentDefault);
    CGDataProviderRelease(provider);

    MKMapRect overlayMapRect = [self.overlay boundingMapRect];
    CGRect overlayRect = [self rectForMapRect:overlayMapRect];

    // draw image
    CGContextSaveGState(context);
    CGContextDrawImage(context, overlayRect, image);
    CGContextRestoreGState(context);

    CGImageRelease(image);

}

如果我的 drawMapRect: 函数不是这些内存问题的原因,有人知道它可能是什么吗?我通过调试知道我的 mapView 的 viewForOverlay: 函数只为每个叠加层调用一次,所以并不是内存泄漏或其他原因。

欢迎任何建议!

谢谢,-马特

编辑:所以事实证明内存问题实际上是由 MKMapView 引起的 - 每次我移动地图时,内存使用量都会非常稳定地上升并且永远不会下降 - 这似乎不太好:(

4

2 回答 2

1

答案有点晚了,如果将来有人遇到同样的问题,就把它留在这里。这里的缺陷是试图渲染整个图像,而文档清楚地表明 -

此外,您应该避免在每次调用此方法时绘制覆盖的全部内容。相反,请始终考虑 mapRect 参数并避免在该矩形之外绘制内容。

因此,您只需在 mapRect 定义的区域中绘制图像的一部分

更新:记住这里的drawRect可以比mapRect大,需要相应地调整绘制和剪切区域

let overlayMapRect = overlay.boundingMapRect
let overlayDrawRect = self.rect(for: overlayMapRect)

// watch out for draw rect adjustment here --
let drawRect = self.rect(for: mapRect).intersection(overlayDrawRect)
let scaleX = CGFloat(image.width) / overlayRect.width
let scaleY = CGFloat(image.height) / overlayRect.height
let transform = CGAffineTransform.init(scaleX: scaleX, y: scaleY)
let imageCut = drawRect.applying(transform)

// omitting optionals checks, you should not
let cutImage = image.cropping(to: imageCut)

// the usual vertical flip issue with image.draw
context.translateBy(x: 0, y: drawRect.maxY + drawRect.origin.y)
context.scaleBy(x: 1, y: -1)
context.draw(image, in: drawRect, byTiling: false)
于 2018-07-31T13:32:15.333 回答
0

这是基于 epolyakov 回答的 objc 版本。它工作得很好,但只是没有任何旋转。

- (void) drawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale inContext:(CGContextRef)context
{
    CGImageRef overlayImage = <your_uiimage>.CGImage;

    CGRect overlayRect = [self rectForMapRect:[self.overlay boundingMapRect]];
    CGRect drawRect = [self rectForMapRect:mapRect];

    CGRect rectPortion = CGRectIntersection(overlayRect, drawRect);
    CGFloat scaleX = rotatedImage.size.width / overlayRect.size.width;
    CGFloat scaleY = rotatedImage.size.height / overlayRect.size.height;
    CGAffineTransform transform = CGAffineTransformMakeScale(scaleX, scaleY);
    CGRect imagePortion = CGRectApplyAffineTransform(rectPortion, transform);

    CGImageRef cutImage = CGImageCreateWithImageInRect(overlayImage, imagePortion);

    CGRect finalRect = rectPortion;

    CGContextTranslateCTM(context, 0, finalRect.origin.y + CGRectGetMaxY(finalRect));
    CGContextScaleCTM(context, 1.0, -1.0);

    CGContextSetAlpha(context, self.alpha);
    CGContextDrawImage(context, finalRect, cutImage);
}

如果您还需要管理图像的旋转,我发现了一个使用原始图像的旋转版本的技巧(这是因为地图渲染总是绘制垂直矩形并且在这种方法中旋转图像会剪切它)。因此,使用原始图像的旋转版本可以按照地图的预期使用垂直矩形进行渲染

    UIImage* rotatedImage = [self rotatedImage:<your_uiimage> withAngle:<angle_of_image>];

    CGImageRef overlayImage = rotatedImage.CGImage;

这是在边界矩形中产生旋转图像的方法

- (UIImage*) rotatedImage:(UIImage*)image withAngle:(CGFloat)angle
{
    float radians = degreesToRadians(angle);

    CGAffineTransform xfrm = CGAffineTransformMakeRotation(radians);
    CGRect imageRect = CGRectMake(0, 0, image.size.width, image.size.height);
    CGRect rotatedImageBoundingRect = CGRectApplyAffineTransform (imageRect, xfrm);


    UIGraphicsBeginImageContext(rotatedImageBoundingRect.size);
    CGContextRef ctx = UIGraphicsGetCurrentContext();

    CGContextTranslateCTM (ctx, rotatedImageBoundingRect.size.width/2., rotatedImageBoundingRect.size.height/2.);
    CGContextScaleCTM(ctx, 1.0, -1.0);
    CGContextRotateCTM (ctx, radians);
    CGContextDrawImage (ctx, CGRectMake(-image.size.width / 2, -image.size.height / 2, image.size.width, image.size.height), image.CGImage);

    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return newImage;
}
于 2018-08-20T16:54:07.103 回答