图表实时更新图表-数据记录在一个VC上并在另一个VC上绘制

时间:2019-05-30 17:57:18

标签: ios swift ios-charts ios-mvc

我有一个选项卡式应用程序,该应用程序在一个选项卡上开始录音,并在另一个选项卡上绘制麦克风电平。

在第一个VC中,我正在收集麦克风电平并将其存储在模型中的数组中。我在模型中使用另一种方法来更新数据,并且在第二个VC中调用它以更新视图。

我要做的是从第一个视图控制器(其中用于在模型中存储数据的逻辑)更新第二个视图控制器中的图表


型号:

Chart.swift

import Charts

class Chart {
    static let sharedInstance = Chart()
    var lineChartView: LineChartView!

    func setChartValues() {
        let entries = (0..<GraphData.sharedInstance.array.count).map { (i) -> ChartDataEntry in
            let val = GraphData.sharedInstance.array[i]
            print(ChartDataEntry(x: Double(i), y: val))
            return ChartDataEntry(x: Double(i), y: val)
        }
        let set1 = LineChartDataSet(values: entries, label: "DataSet 1")
        let data = LineChartData(dataSet: set1)
        lineChartView.data = data
    }
}

GraphData.swift

class GraphData {
    static let sharedInstance = GraphData()
    var array = [Double]()
}

视图控制器:

第一个VC :(每个注释的完整代码)

导入UIKit 导入AVFoundation

class SoundController: UIViewController, AVAudioRecorderDelegate {

    var recordingSession: AVAudioSession!
    var audioRecorder: AVAudioRecorder!
    var timer = Timer()
    @IBOutlet weak var errorLbl: UILabel!
    @IBOutlet weak var recordBtn: UIButton!

    @IBAction func recordButton(_ sender: UIButton) {
        if audioRecorder == nil {
            startRecording()
        } else {
            finishRecording(success: true)
        }
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(false)
        errorLbl.text = ""
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        recordPermission()
    }

    func recordPermission() {
        recordingSession = AVAudioSession.sharedInstance()
        do {
            try recordingSession.setCategory(.playAndRecord, mode: .default)
            try recordingSession.setActive(true)
            recordingSession.requestRecordPermission() {  allowed in
                DispatchQueue.main.async {
                    if allowed {
                        print("recording allowed")
                    } else {
                        self.errorLbl.text = "Recording Permission was Denied. Please open settings and allow Cry It Out to access the microphone."
                    }
                }
            }
        } catch {
            self.errorLbl.text = "Recording Permission was Denied. Please open settings and allow the app to access the microphone."
        }
    }

    func getDocumentsDirectory() -> URL {
        let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
        return paths[0]
    }

    func startRecording() {
        if recordBtn.titleLabel?.text == "Tap to Re-record" {
            //reset values array
            GraphData.sharedInstance.array = []
        }
        let audioFilename = getDocumentsDirectory().appendingPathComponent("baby.m4a")

        let settings = [
            AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
            AVSampleRateKey: 12000,
            AVNumberOfChannelsKey: 1,
            AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue
        ]

        do {
            audioRecorder = try AVAudioRecorder(url: audioFilename, settings: settings)
            audioRecorder.delegate = self
            audioRecorder.isMeteringEnabled = true
            runTimer()
            audioRecorder.record()
            runTimer()
            recordBtn.setTitle("Tap to Stop", for: .normal)
        } catch {
            finishRecording(success: false)
        }
    }

    func levelTimerCallback() -> Float {
        if audioRecorder != nil {
            audioRecorder.updateMeters()
            //If we are beyond a threshold value (-15)
            if audioRecorder.averagePower(forChannel: 0) > -15 {
                return audioRecorder.averagePower(forChannel: 0)
            }
        }
        return 0
    }

    func finishRecording(success: Bool) {
        //stop recording and reset recorder to nil for other checks
        audioRecorder.stop()
        audioRecorder = nil

        if success {
            recordBtn.setTitle("Tap to Re-record", for: .normal)
            if timer.isValid {
                timer.invalidate()
            }
        } else {
            //Recording Failed
            recordBtn.setTitle("Tap to Record", for: .normal)
            //disable timer if running (might be running or might not)
            if timer.isValid {
                timer.invalidate()
            }
        }
    }

    func audioRecorderDidFinishRecording(_ recorder: AVAudioRecorder, successfully flag: Bool) {
        if !flag {
            finishRecording(success: false)
        }
    }

    //MARK: Timers

    @objc func updateTimer() {
        if levelTimerCallback() != 0 {
            let date = Date()
            let calendar = Calendar.current
            let month = calendar.component(.month, from: date)
            let day = calendar.component(.day, from: date)
            let hour = calendar.component(.hour, from: date)
            let minutes = calendar.component(.minute, from: date)
            let seconds = calendar.component(.second, from: date)
            let prettyDate = "\(month)/\(day) \(hour):\(minutes) and \(seconds) seconds"
            print(prettyDate)
            GraphData.sharedInstance.array.append(Double(levelTimerCallback()))
            //does this run the method? It should
            GraphController.sharedInstance.lineChartView?.data = Chart.sharedInstance.setChartValues()

        }
    }


    func runTimer() {
        timer = Timer.scheduledTimer(timeInterval: 1, target: self,   selector: (#selector(SoundController.updateTimer)), userInfo: nil, repeats: true)
    }

    func stopTimer() {
        timer.invalidate()
    }

}

第二个VC:

import UIKit
import Charts

class GraphController: UIViewController {
    static let sharedInstance = GraphController()
    @IBOutlet weak var lineChartView: LineChartView!


    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(true)
        self.lineChartView.data = Chart.sharedInstance.setChartValues()
    }

}

1 个答案:

答案 0 :(得分:1)

Try this solution without lambda functions. You don't need use static values.

1. Prepare your GraphController to have a function to receive data

class GraphController: UIViewController {

    ...

    func dataReceived ( gData : GraphData ) {
        DispatchQueue.main.async {
            // Update your chart with gData
        }
    }

}

2. Get the reference of GraphController and use the function of step 1 to make your updates.

Please, get the reference of your GraphController from tab and use this reference to call a function to make your chart updates. I don't know exactely your situation, but if you have problems to make it, please look this: https://stackoverflow.com/a/39499751/5140756

class SoundController: UIViewController, AVAudioRecorderDelegate { 

    var graphController : GraphController?

    ...

    override func viewDidLoad() {
        super.viewDidLoad()
        ...
        // get graph controller reference from tabbar.
        self.graphController = self.tabBarController.viewControllers![INDEX_OF_VIEW_CONTROLLER] as! GraphController
    }

    // finally on your function call the function's graph controller receive data
    @objc func updateTimer() {
        if levelTimerCallback() != 0 {
            let date = Date()
            let calendar = Calendar.current
            let month = calendar.component(.month, from: date)
            let day = calendar.component(.day, from: date)
            let hour = calendar.component(.hour, from: date)
            let minutes = calendar.component(.minute, from: date)
            let seconds = calendar.component(.second, from: date)
            let prettyDate = "\(month)/\(day) \(hour):\(minutes) and \(seconds) seconds"
            print(prettyDate)
            GraphData.sharedInstance.array.append(Double(levelTimerCallback()))
            //does this run the method? It should
            //GraphController.sharedInstance.lineChartView?.data = Chart.sharedInstance.setChartValues()

             if graphController != nil {
                 self.graphController!.dataReceived( gData: GraphData.sharedInstance )
             } 


        }
    }

}

Please, look the code, and make some changes that you need, I tried automate the max that I can.