【问题标题】:Proper use of MKOverlayView正确使用 MKOverlayView
【发布时间】:2011-05-09 16:33:10
【问题描述】:

我正在编写一个 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 引起的 - 每次我移动地图时,内存使用量都会非常稳定地上升并且永远不会下降 - 这似乎不太好:(

【问题讨论】:

  • 在这种情况下,MKMapView 无法解决驻留图像数据的最小子集的繁琐工作。您的图像占用接近 23MB 的空间,除了最新的硬件外,其他所有硬件都会受到压力,因此平铺它可能是在模拟器之外使此性能发挥作用的唯一方法。
  • 如果这是真的,那么MKOverlayView的意义何在?或者更确切地说,它解决了 CATiledLayer 没有解决的哪些问题?

标签: iphone mapkit mkmapview overlay cgimage


【解决方案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)

【讨论】:

    【解决方案2】:

    这是基于 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;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多