Search code examples
iosuikit

UILabel Not Updating Consistently


I have a very simple update method, where I've included the debugging lines.

@IBOutlet weak var meterLabel: UILabel!

func updateMeter(string: String)
{
    if Thread.isMainThread {
        meterLabel.text = string
    } else {
        DispatchQueue.main.sync {
            meterLabel.text = string
        }
    }
    print(string)
}

Obviously string is never nil. The function updateMeter is called about 3 times a second, however currently in the simulator I do not see the UILabel change (it does change during calls to this same updateMeter elsewhere). Is there any reason why changing a UILabel's text would not have a visible result on the main thread?

Called here:

public func startRecording()
{
    let recordingPeriod = TimeInterval(Float(Constants.windowSize)/Float(Constants.sampleFrequency))
    var index = 0
    Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { timer in
        let audioRecorder = self.AudioRecorders[index]!
        audioRecorder.deleteRecording()
        audioRecorder.record()
        DispatchQueue.main.asyncAfter(deadline: .now() + recordingPeriod)
        {
            if let pitch = self.finishSampling(audioRecorder: audioRecorder, index: self.AudioRecorders.index(of: audioRecorder))
            {
                self.meterViewController?.updateMeter(string: String(pitch))
            }
        }
        index = index + 1
        if index == 4 { index = 0 }
        if !(self.keepRecording ?? false) { timer.invalidate() }
    }
}

Other methods called:

private func finishSampling(audioRecorder: AVAudioRecorder?, index: Int?) -> Float?
{
    audioRecorder?.stop()
    if let index = index, var (data, _, _) = loadAudioSignal(audioURL: getDirectory(for: index))
    {
        let pitch = getPitch(&data, Int32(data.count), Int32(Constants.windowSize), Int32(Constants.sampleFrequency))
        return Float(pitch)
    }
    return nil
}

private func loadAudioSignal(audioURL: URL) -> (signal: [Float], rate: Double, frameCount: Int)?
{
    guard
        let file = try? AVAudioFile(forReading: audioURL),
        let format = AVAudioFormat(commonFormat: .pcmFormatFloat32, sampleRate: file.fileFormat.sampleRate, channels: file.fileFormat.channelCount, interleaved: false),
        let buf = AVAudioPCMBuffer(pcmFormat: format, frameCapacity: UInt32(file.length))
        else
    {
        return nil
    }
    try? file.read(into: buf)
    let floatArray = Array(UnsafeBufferPointer(start: buf.floatChannelData?[0], count:Int(buf.frameLength)))
    return (signal: floatArray, rate: file.fileFormat.sampleRate, frameCount: Int(file.length))
}

Where getPitch does some simple processing and runs relatively quick.


Solution

  • By calling usleep you are blocking the main thread. The main thread is the thread that updates the UI. Since it is blocked, it cannot do that.

    You should use an alternate approach, such as a Timer to periodically update the label.