Вместо сна вы должны использовать таймер в вашем случае. Попробуйте что-то вроде следующего:
let frameDuration: TimeInterval = 1.0/60.0 // Using 60 FPS
Timer.scheduledTimer(withTimeInterval: frameDuration, repeats: true) { timer in
guard let sampleBuffer = videoOutput.copyNextSampleBuffer() else {
timer.invalidate()
return
}
print ("sample at time \(CMSampleBufferGetPresentationTimeStamp(sampleBuffer))")
if let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) {
let ciImage = CIImage(cvImageBuffer: imageBuffer)
uiImage = UIImage(ciImage: ciImage)
self.topView.image = uiImage
self.topView.setNeedsDisplay()
}
}
Проблема в вашем случае заключается в том, что вы все еще находитесь в главном потоке, когда делаете это. Поэтому, когда вы спите, вы не позволяете своей основной теме продолжаться. Так что в основном ваш основной поток строит образы и спит, но вы не даете ему времени на фактическое обновление пользовательского интерфейса.
Вы можете сделать аналогичную вещь, используя отдельный поток. Простая диспетчеризация могла бы сделать, например:
DispatchQueue(label: "Updating images").async {
// Now on separate thread
while let sampleBuffer = videoOutput.copyNextSampleBuffer() {
print ("sample at time \(CMSampleBufferGetPresentationTimeStamp(sampleBuffer))")
if let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) {
let ciImage = CIImage(cvImageBuffer: imageBuffer)
uiImage = UIImage(ciImage: ciImage)
// The UI part now needs to go back to main thread
DispatchQueue.main.async {
self.topView.image = uiImage
self.topView.setNeedsDisplay()
}
usleep(useconds_t(24000))
}
}
}
, но важно, чтобы вы по-прежнему обновляли (как минимум) часть UIKit в главном потоке. Возможно, даже часть CI должна быть в основном потоке. Лучше всего просто попробовать.