如何在后台使用MKMapSnapshotter?

时间:2016-11-07 21:54:05

标签: ios objective-c multithreading mapkit mkmapsnapshotter

我有一个应用程序应该在后台获取对象并使用它们的位置数据为它们生成地图快照。当然我尝试过MKMapSnapshotter。

事实证明(在对黑色地图快照感到困惑几周之后),这个工具似乎只能在从主线程调用时才能工作:

dispatch_async(dispatch_get_main_queue(), ^{
   MKMapSnapshotter *snapshotter = [[MKMapSnapshotter alloc] initWithOptions:options];
        [snapshotter startWithQueue:dispatch_get_main_queue() completionHandler:^(MKMapSnapshot * _Nullable snapshot, NSError * _Nullable error) {
       //Use image here. Image would be completely black if not for the first line of code specifying main thread.
   }];
});

这是一个框架错误吗?

问题:这只在我的应用程序位于前台时运行。

1 个答案:

答案 0 :(得分:2)

对于我正在处理的应用程序来说这有点复杂,因为有很多调用下载一组多个缩放级别的地图图块,因此下面的代码可能比您需要的更复杂(但是显示队列用于快照)。例如,我需要一个dispatchSemaphore来避免排队数百到数千个并发快照 - 这将它们限制为在线程上捕获的大约25个并发快照。

另外,我在Swift 3中这样做,所以GCD可能会有变化,让我在为你提出问题时这样做。

这里的逻辑是在主队列保持解除阻塞状态时让所有请求在processQueue中启动,以便UI保持活动状态。然后,当任何时候多达25个请求通过信号量门时,它们通过snapshotter.start调用进入snapshotQueue。当一个快照完成时,另一个快照启动,直到processQueue为空。

unowned let myself = self   // Avoid captures in closure

let processQueue = DispatchQueue(label: "processQueue", qos: .userInitiated)
let snapshotQueue = DispatchQueue(label: "snapshotQueue")
var getSnapshotter = DispatchSemaphore(value: 25)

processQueue.async
        {
            var centerpoint = CLLocationCoordinate2D()
            centerpoint.latitude = (topRight.latitude + bottomLeft.latitude) / 2.0
            centerpoint.longitude = (topRight.longitude + bottomLeft.longitude) / 2.0
            let latitudeDelta = abs(topRight.latitude - bottomLeft.latitude)
            let longitudeDelta = abs(topRight.longitude - bottomLeft.longitude)
            let mapSpan = MKCoordinateSpanMake(latitudeDelta, longitudeDelta)

            var mapRegion = MKCoordinateRegion()
            mapRegion.center = centerpoint
            mapRegion.span = mapSpan

            let options = MKMapSnapshotOptions()
            options.region = mapRegion
            options.mapType = .standard               
            options.scale = 1.0
            options.size = CGSize(width: 256, height: 256)

            myself.getSnapshotter.wait()       // Limit the number of concurrent snapshotters since we could invoke very many

            let snapshotter = MKMapSnapshotter(options: options)

            snapshotter.start(with: myself.snapshotQueue, completionHandler: {snapshot, error in
                if error == nil
                {
                    self.saveTile(path: path, tile: snapshot!.image, z: z, x: x, y: y)
                    // saveTile writes the image out to a file in the mapOverlay file scheme
                } else {
                    print("Error Creating Map Tile: ", error!)
                }
                if myself.getSnapshotter.signal() == 0
                {  
                    // show status as completed (though could be up to 20 snapshots finishing, won't take long at this point 
                }
            })
    }

这对我来说可以获得最多5K的快照来构建7级缩放级别的离线地图图像集,而不会阻止用户界面,因此我对代码非常熟悉。