Computed Property - Как использовать только в основном потоке? - PullRequest
0 голосов
/ 26 июня 2018

Я пытаюсь использовать AVCaptureVideoPreviewLayer для захвата видео, однако, когда я запускаю свое приложение, проверка основного потока вызывает сбой.

В какой момент код должен выполняться в главном потоке - я предполагаю, что это решит проблему, с которой я столкнулся.Было предпринято несколько попыток с использованием DispatchQueue.main.async({}), но безрезультатно.

Произошла ошибка:

Проверка основного потока: API пользовательского интерфейса вызывается в фоновом потоке:

Оригинал:

    var videoPreviewLayer: AVCaptureVideoPreviewLayer {

    let previewlayer = layer as! AVCaptureVideoPreviewLayer

    switch gravity {
      case .resize:
        previewlayer.videoGravity = AVLayerVideoGravity.resize
      case .resizeAspect:
        previewlayer.videoGravity = AVLayerVideoGravity.resizeAspect
      case .resizeAspectFill:
        previewlayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
    }
    return previewlayer
   }

Попытка исправления:

var videoPreviewLayer: AVCaptureVideoPreviewLayer {

  getVideoPreviewLayer { (previewLayer) in
    return previewLayer
  }
}

func getVideoPreviewLayer( completion: @escaping (AVCaptureVideoPreviewLayer) -> ()) {

  DispatchQueue.main.async { [unowned self ] in

    let previewlayer = self.layer as! AVCaptureVideoPreviewLayer

    switch self.gravity {
    case .resize:
      previewlayer.videoGravity = AVLayerVideoGravity.resize
    case .resizeAspect:
      previewlayer.videoGravity = AVLayerVideoGravity.resizeAspect
    case .resizeAspectFill:
      previewlayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
    }

    completion(previewlayer)
  }
}

Оригинал enter image description here Попытка исправления enter image description here

Ответы [ 3 ]

0 голосов
/ 26 июня 2018

Как указано в сообщении об ошибке, ваша попытка исправить не скомпилируется, так как она ожидает возвращаемого значения, в то время как вы оборачиваете этот метод получения в асинхронную диспетчеризацию.

Дело здесь не в том, как вы определяетедобытчик, но как вы его используете. оригинальная часть вашего кода полностью в порядке, если вы помните, что это API, связанный с пользовательским интерфейсом, и вы должны вызывать его только из основного потока.Таким образом, вместо того, чтобы возиться с этой частью вашего кода, вы должны заключить вызов в videoPreviewLayer в вызов DispatchQueue.main.async, что-то вроде этого:

DispatchQueue.main.async {
    let _ = _something_.videoPreviewLayer
}
0 голосов
/ 26 июня 2018

Вы должны использовать DispatchQueue.main.sync, потому что хотите дождаться ответа (вы хотите вернуть полученное значение).Использование async приведет к продолжению работы вашего кода до того, как вы получите значение.

Вам также не нужно иметь дело с отдельной функцией.

Вот простое исправление, которое должно работать:

var videoPreviewLayer: AVCaptureVideoPreviewLayer {
    let capturedLayer = DispatchQueue.main.sync {
        previewlayer = layer as! AVCaptureVideoPreviewLayer

        switch gravity {
            case .resize:
                previewlayer.videoGravity = AVLayerVideoGravity.resize
            case .resizeAspect:
                previewlayer.videoGravity = AVLayerVideoGravity.resizeAspect
            case .resizeAspectFill:
                previewlayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
        }
        return previewlayer
    }
    return capturedLayer
}

Однако вы можете столкнуться с проблемами, пытаясь что-то сделать со слоем, возвращенным из этой функции, потому что вы будете вне основного потока.Вероятно, это хорошая идея, чтобы перейти к основному потоку с DispatchQueue.main.sync где-то еще (где-то в контексте, который получает переменную videoPreviewLayer).

0 голосов
/ 26 июня 2018

Вот одно решение, обеспечивающее получение слоя только в главной очереди, независимо от того, какая очередь используется для доступа videoPreviewLayer:

var videoPreviewLayer: AVCaptureVideoPreviewLayer {
    let previewlayer: AVCaptureVideoPreviewLayer = {
        if Thread.current.isMainThread {
            return layer as! AVCaptureVideoPreviewLayer
        } else {
            DispatchQueue.main.sync {
                return layer as! AVCaptureVideoPreviewLayer
            }
        }
    }()

    switch gravity {
    case .resize:
        previewlayer.videoGravity = AVLayerVideoGravity.resize
    case .resizeAspect:
        previewlayer.videoGravity = AVLayerVideoGravity.resizeAspect
    case .resizeAspectFill:
        previewlayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
    }
    return previewlayer
}
...