我有一个选项卡式应用程序,该应用程序在一个选项卡上开始录音,并在另一个选项卡上绘制麦克风电平。
在第一个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()
}
}
答案 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.