【问题标题】:Snapshot of MKMapView in iOS7iOS7 中 MKMapView 的快照
【发布时间】:2013-09-17 13:11:17
【问题描述】:

我正在尝试在 iOS7 应用程序中创建 MKMapView 的快照,就像在以前的 iOS 版本中到处推荐的方式一样:

- (UIImage*) renderMapViewToImage
{
   UIGraphicsBeginImageContextWithOptions(mapView.frame.size, NO, 0.0);
   [mapView.layer renderInContext:UIGraphicsGetCurrentContext()];
   UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
   UIGraphicsEndImageContext(); 
   return image;
}

但是,返回的图像是一个黑色矩形,上面有一个蓝色的当前位置点。 我也尝试过使用 mapView 的不同子层,但结果始终相同。

有谁知道如何在 iOS7 中拍摄 MKMapView 快照?

【问题讨论】:

  • 为什么要传递 0.0 作为上下文选项?对于视网膜显示器,它应该是 1.0 或 2.0 ...
  • @Qiqi Zero 很好。正如UIGraphicsBeginImageContextWithOptions 的文档所说,“如果您指定值 0.0,则比例因子将设置为设备主屏幕的比例因子。”

标签: ios ios7 mkmapview mapkit mkmapsnapshotter


【解决方案1】:

您可以使用MKMapSnapshotter 并从生成的MKMapSnapshot 中获取image。请参阅 WWDC 2013 会议视频的讨论,Putting Map Kit in Perspective

例如:

MKMapSnapshotOptions *options = [[MKMapSnapshotOptions alloc] init];
options.region = self.mapView.region;
options.scale = [UIScreen mainScreen].scale;
options.size = self.mapView.frame.size;

MKMapSnapshotter *snapshotter = [[MKMapSnapshotter alloc] initWithOptions:options];
[snapshotter startWithCompletionHandler:^(MKMapSnapshot *snapshot, NSError *error) {
    UIImage *image = snapshot.image;
    NSData *data = UIImagePNGRepresentation(image);
    [data writeToFile:[self snapshotFilename] atomically:YES];
}];

话虽如此,renderInContext 解决方案仍然适用于我。有关于仅在 iOS7 的主队列中执行此操作的说明,但它似乎仍然有效。但是MKMapSnapshotter 似乎是更适合 iOS7 的解决方案。


如果您想在快照中包含一些注释,您必须手动绘制它们 (!)。这在Putting Map Kit in Perspective 视频的末尾有一些详细的讨论。我不得不说这是我见过的 Apple 建议的最不优雅的实现之一。无论如何,在 iOS 中,它可能看起来像:

MKMapSnapshotOptions *options = [[MKMapSnapshotOptions alloc] init];
options.region = self.mapView.region;
options.scale = [UIScreen mainScreen].scale;
options.size = self.mapView.frame.size;

MKMapSnapshotter *snapshotter = [[MKMapSnapshotter alloc] initWithOptions:options];
[snapshotter startWithQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) completionHandler:^(MKMapSnapshot *snapshot, NSError *error) {

    // get the image associated with the snapshot

    UIImage *image = snapshot.image;

    // Get the size of the final image

    CGRect finalImageRect = CGRectMake(0, 0, image.size.width, image.size.height);

    // Get a standard annotation view pin. Clearly, Apple assumes that we'll only want to draw standard annotation pins!

    MKAnnotationView *pin = [[MKPinAnnotationView alloc] initWithAnnotation:nil reuseIdentifier:@""];
    UIImage *pinImage = pin.image;

    // ok, let's start to create our final image

    UIGraphicsBeginImageContextWithOptions(image.size, YES, image.scale);

    // first, draw the image from the snapshotter

    [image drawAtPoint:CGPointMake(0, 0)];

    // now, let's iterate through the annotations and draw them, too

    for (id<MKAnnotation>annotation in self.mapView.annotations)
    {
        CGPoint point = [snapshot pointForCoordinate:annotation.coordinate];
        if (CGRectContainsPoint(finalImageRect, point)) // this is too conservative, but you get the idea
        {
            CGPoint pinCenterOffset = pin.centerOffset;
            point.x -= pin.bounds.size.width / 2.0;
            point.y -= pin.bounds.size.height / 2.0;
            point.x += pinCenterOffset.x;
            point.y += pinCenterOffset.y;

            [pinImage drawAtPoint:point];
        }
    }

    // grab the final image

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

    // and save it

    NSData *data = UIImagePNGRepresentation(finalImage);
    [data writeToFile:[self snapshotFilename] atomically:YES];
}];

对于 MacOS 实现,请参阅该视频了解更多信息,但技术基本相同(创建图像的机制略有不同)。

【讨论】:

  • 感谢您的出色回答!现在可以了。 renderInContext 仍然对我不起作用,但那是另一回事了。
  • 有什么方法可以用 MKMapSnapshotter 渲染 pin 注释?
  • @DZenBot 在我在回答中提到的那个视频的结尾,Apple 会准确地引导您完成这项技术。虽然很丑。我很惊讶他们认为这是一个可以接受的实现。无论如何,我在这里包含了一个演绎,并相应地更新了我的答案。如果不使用 pin 注释,如果您有 MKUserAnnotation 等,则需要对其进行修改,但这应该为您指明正确的方向。
  • 很好的答案!直到 Apple 的视频结束,我才走到最后,我完全同意:这根本不优雅。这个很酷的新 API 的整个想法是不依赖于图像上下文渲染和使用 MapKit 附加 API。但它有效,看起来像一个魅力。这是截图:dl.dropboxusercontent.com/u/2452151/Permalink/…
  • @jeraldo 没错,不需要地图视图。显然,您必须以其他方式设置MKMapSnapshotOptionscameraregion(上面仅使用地图视图的region),但是是的,MKMapSnapshotter 在没有地图视图。事实上,正是因为这个原因,当您不想要地图视图的开销并且可以接受静态呈现时,它经常被使用。
【解决方案2】:

对于 iOS 10 及更高版本,您可以使用 UIGraphicsImageRenderer 类将任何视图渲染为图像(以防万一您不想使用 MKMapSnapshotter,因为我使用的是 @987654325 @)。

let render = UIGraphicsImageRenderer(size: self.mapView.bounds.size)
let image = render.image { ctx in
  self.mapView.drawHierarchy(in: self.mapView.bounds, afterScreenUpdates: true)
}

结果

【讨论】:

    【解决方案3】:

    对于 Swift 3

    这是我从这篇文章中修改的 swift 3 版本: Render a Map as an Image using MapKit

    以下代码允许您基于点(1 个坐标)和折线(多个坐标)对区域进行快照

    func takeSnapShot() {
        let mapSnapshotOptions = MKMapSnapshotOptions()
    
        // Set the region of the map that is rendered. (by one specified coordinate)
        // let location = CLLocationCoordinate2DMake(24.78423, 121.01836) // Apple HQ
        // let region = MKCoordinateRegionMakeWithDistance(location, 1000, 1000)
    
        // Set the region of the map that is rendered. (by polyline)
        // var yourCoordinates = [CLLocationCoordinate2D]()  <- initinal this array with your polyline coordinates
        let polyLine = MKPolyline(coordinates: &yourCoordinates, count: yourCoordinates.count)
        let region = MKCoordinateRegionForMapRect(polyLine.boundingMapRect)
    
        mapSnapshotOptions.region = region
    
        // Set the scale of the image. We'll just use the scale of the current device, which is 2x scale on Retina screens.
        mapSnapshotOptions.scale = UIScreen.main.scale
    
        // Set the size of the image output.
        mapSnapshotOptions.size = CGSize(width: IMAGE_VIEW_WIDTH, height: IMAGE_VIEW_HEIGHT)
    
        // Show buildings and Points of Interest on the snapshot
        mapSnapshotOptions.showsBuildings = true
        mapSnapshotOptions.showsPointsOfInterest = true
    
        let snapShotter = MKMapSnapshotter(options: mapSnapshotOptions)
    
        snapShotter.start() { snapshot, error in
            guard let snapshot = snapshot else {
                return
            }
            self.imageView.image = snapshot.image
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-09-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-06-17
      • 1970-01-01
      相关资源
      最近更新 更多