Графики обновляются в режиме реального времени — данные записываются в одном VC, а отображаются в другом

У меня есть приложение с вкладками, которое начинает запись на одной вкладке и отображает уровни микрофона на другой вкладке.

В первом VC я собираю уровни микрофонов и сохраняю их в виде массива в модели. Я использую другой метод в модели для обновления данных и вызываю его во втором VC, чтобы обновить представление.

Что я хочу сделать, так это обновить диаграмму во втором контроллере представления из первого контроллера представления (где находится логика хранения данных в модели)


Model:

Диаграмма.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()
    }

}

Второй ВК:

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()
    }

}

в вашем случае я бы использовал лямбда-функции

Augusto 30.05.2019 20:08

@Augusto, спасибо, не могли бы вы привести пример?

froggomad 30.05.2019 20:32

Я могу попробовать, пожалуйста, покажите мне ваш полный код ваших двух классов здесь или снаружи (так).

Augusto 30.05.2019 20:33

@Augusto большое спасибо. Я обновил код - вот и все приложение

froggomad 31.05.2019 06:51
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
4
351
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Попробуйте это решение без лямбда-функций. Вам не нужно использовать значения static.

1. Подготовьте свой GraphController к функции приема данных

class GraphController: UIViewController {

    ...

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

}

2. Получите ссылку на GraphController и используйте функцию шага 1 для обновления.

Пожалуйста, получите ссылку на вкладку GraphController from и используйте эту ссылку для вызова функции для обновления диаграммы. Я не знаю точно вашу ситуацию, но если у вас есть проблемы с этим, пожалуйста, посмотрите это: 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 )
             } 


        }
    }

}

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

Другие вопросы по теме