2016-11-07 2 views
0

У меня есть приложение, которое должно извлекать объекты в фоновом режиме и использовать их данные о местоположении для создания моментального снимка карты для них. Естественно, я попробовал 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

Это было немного сложно для приложения, над которым я работаю, поскольку есть много вызовов для загрузки набора плиток карт для нескольких уровней масштабирования, поэтому приведенный ниже код может быть немного сложнее, чем вам нужно (но показывает что очереди работают для моментальной съемки). Например, мне понадобился файл dispatchSemaphore, чтобы избежать очередей сотен или тысяч одновременных снимков - это ограничивает их примерно до 25 одновременных снимков, захваченных в потоке.

Кроме того, я делаю это в Swift 3, поэтому в GCD могут быть изменения, которые позволяют мне делать это, представляя проблемы для вас.

Логика здесь заключается в том, чтобы получить все запросы, запущенные в processQueue, в то время как основная очередь остается разблокированной, поэтому пользовательский интерфейс остается активным. Затем, как только до 25 запросов проходят через ворота семафора в любой момент времени, они вводят snapshotQueue через вызов snapshotter.start. Когда один снимок заканчивается, другой запускается до тех пор, пока 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-масштабирования карты уровня автономного образа в набор без блокировки пользовательского интерфейса, так что я очень комфортно с кодом.

+0

Что касается необходимости находиться на переднем плане, это правильно, поэтому я использую команду: [UIApplication.shared.isIdleTimerDisabled = true]. Просто убедитесь, что он установлен на false, как только вы закончите. –

+0

Ничего себе, выглядит впечатляюще. Я не очень хорошо знаком с продвинутой очередью. Может потребоваться некоторое время, чтобы попробовать какую-то форму этого решения. – TealShift

+0

Это разочаровывает передний план, так как я вытягиваю данные тренировки из Apple Watch, и единственная остановка bg-обработки - эта немая функция. :/ – TealShift

Смежные вопросы