问题
I am trying to read through a string one character a time delaying .1 seconds before moving on to the next character.
I have tried implementing the delay function inside the for loop but it has two problems. 1. The delay is inconsistent and does not take the same amount of time when moving between characters. 2. It disrupts the main thread which I think is the cause of the camera view freezing. However, I think it is also possible that activating the flashlight while the camera is on freezes the signal, causing the glitch.
func delay(_ delay:Double, closure:@escaping ()->()) {
DispatchQueue.main.asyncAfter(
deadline: DispatchTime.now () + Double(Int64(delay *
Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC), execute:
closure)
}
func scriptReader(){
let str = "10101000001111110000110"
for i in 0..<str.count {
delay(Double(i) * 0.5) {
let index = str.index(str.startIndex, offsetBy: i)
self.flash(number: str[index])
}
}
}
func flash(number: Character){
guard let device = AVCaptureDevice.default(for:
AVMediaType.video) else { return }
guard device.hasTorch else { return }
if number == "0" {
print("off")
do {
try device.lockForConfiguration()
if (device.torchMode == AVCaptureDevice.TorchMode.on) {
device.torchMode = AVCaptureDevice.TorchMode.off
}
device.unlockForConfiguration()
} catch {
print(error)
}
}
if number == "1"{
print("on")
do {
try device.lockForConfiguration()
if (device.torchMode == AVCaptureDevice.TorchMode.off) {
do {
try device.setTorchModeOn(level: 1.0)
} catch {
print(error)
}
}
device.unlockForConfiguration()
} catch {
print(error)
}
}
}
回答1:
Re your first concern, the use of a series of asyncAfter
are going to suffer from “timer coalescing”, where the OS will group future, individually scheduled timers to fire all at once, to make best use of the device battery (the less often the OS needs to wake up the device, the better the battery life). The further out the scheduled timers are, the more coalescing the OS will do.
One can avoid this by using a repeating Timer
:
func handle(string: String) {
guard !string.isEmpty else { return }
var index = string.startIndex
Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { timer in
if string[index] == "0" {
// handle "0"
} else {
// handle not "0"
}
index = string.index(after: index)
if index == string.endIndex { timer.invalidate() }
}
}
回答2:
Something as simple as this may also work:
let threadName = "FlashThread"
let string = "10101000001111110000110"
//Create a custom thread
DispatchQueue(label: threadName).async {
print("Thread Start")
//Start loop
string.forEach { (c) in
print(c)
//Do something on main thread
DispatchQueue.main.async {
self.flash(number: c)
}
//Put your thread to sleep for a second
sleep(1)
}
print("Thread END")
}
回答3:
I have fixed the issue now. The first problem was solved with the timer code provided by Rob. This stopped the freezing of the main thread and now the code iterates through the String with consistent timing between.
The second problem was fixed by eliminating multiple instances of AVCaptureDevice
, once that was fixed the code worked.
Thanks for the help!
回答4:
I think camera freezes because you call its property every time in for loop, do it once, pass its property to obtain in your func
try this code to fix your glitch
func scriptReader(){
let str = "10101000001111110000110"
guard let device = AVCaptureDevice.default(for: .video) else { return }
guard device.hasTorch else { return }
for i in 0..<str.count {
DispatchQueue.main.asyncAfter(deadline: .now() + Double(i) * 0.5) {
let index = str.index(str.startIndex, offsetBy: i)
self.flash(number: str[index])
}
}
}
func flash(device: AVCaptureDevice, isOn: Bool) {
do {
try device.lockForConfiguration()
device.torchMode = isOn ? AVCaptureDevice.TorchMode.off : device.setTorchModeOn(level: 1.0)
device.unlockForConfiguration()
} catch {
print(error)
}
}
来源:https://stackoverflow.com/questions/57213255/how-do-i-delay-a-for-loop-in-swift-without-interrupting-the-main-thread