I'm creating an audio voice recorder and after each upload, the name appends an int to the recording (e.g. 1, 2, 3, etc.) in a table view (note: regular view controller with a UITableView vs. Table View Controller).
I'm having trouble deleting each row, and I'm not sure if it is because 'numberOfRecords.remove(at: indexPath.row)' only accepts strings.
I get the error: "Value of type 'Int' has no member 'remove.'"
class ViewController2: UIViewController, RecordButtonDelegate, AVAudioRecorderDelegate, UITableViewDelegate, UITableViewDataSource {
@IBOutlet weak var myTableView: UITableView!
var numberOfRecords : Int = 0
// Setting up Table View
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return numberOfRecords
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = String(indexPath.row + 1)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let path = getDirectory().appendingPathComponent("\(indexPath.row + 1).m4a")
do {
audioPlayer = try AVAudioPlayer(contentsOf: path)
audioPlayer.play()
}
catch {
}
}
// Delete rows
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == UITableViewCellEditingStyle.delete{
numberOfRecords.remove(at: indexPath.row)
tableView.beginUpdates()
tableView.deleteRows(at: [indexPath], with: .automatic)
tableView.endUpdates()
}
}
// Audio Player
var audioPlayer : AVAudioPlayer!
var recordingSession : AVAudioSession!
var audioRecorder : AVAudioRecorder!
var recordButton: RecordButton?
@IBOutlet weak var buttonLabel2: RecordButton!
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
buttonLabel2.delegate = self
}
func tapButton(isRecording: Bool) {
// Check if we have an active recorder
if audioRecorder == nil {
numberOfRecords += 1
let filename = getDirectory().appendingPathComponent("\(numberOfRecords).m4a")
let settings = [AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
AVSampleRateKey: 12000,
AVNumberOfChannelsKey: 1,
AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue]
// Start audio recording
do {
audioRecorder = try AVAudioRecorder(url: filename, settings: settings)
audioRecorder.delegate = self
audioRecorder.record()
}
catch {
displayAlert(title: "Oops!", message: "Recording failed")
}
// Play speaker instead of earpiece
let audioSession = AVAudioSession.sharedInstance()
do {
try audioSession.overrideOutputAudioPort(AVAudioSessionPortOverride.speaker)
} catch let error as NSError {
print("Audio Session error: \(error.localizedDescription)")
}
}
else {
// Stop audio recording
audioRecorder.stop()
audioRecorder = nil
UserDefaults.standard.set(numberOfRecords, forKey: "myNumber")
myTableView.reloadData()
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Setting up Recording session
recordingSession = AVAudioSession.sharedInstance()
if let number : Int = UserDefaults.standard.object(forKey: "myNumber") as? Int {
numberOfRecords = number
}
AVAudioSession.sharedInstance().requestRecordPermission { (hasPermission) in
if hasPermission {
print ("Accepted")
}
}
The problem that you're experiencing is due you calling remove(at:)
on an Int. An Int has no function called remove(at:)
.
You're declaring var numberOfRecords: Int
to track your indices, then in
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath)
you're calling
// this is the line that's causing your problem
numberOfRecords.remove(at: indexPath.row)
If you want to continue using an int to track your cells you should subtract from numberOfRecords
numberOfRecords-=1
Or you could track your records using an array something like this:
// declare an array of Strings to hold your filenames
var records: [String] = []
then where you're saving your files add the new filename to your array for the tableview
// Stop audio recording
audioRecorder.stop()
audioRecorder = nil
// add your filename to your array of records for the tableview
records.append(filename)
// update your total number of records if desired
UserDefaults.standard.set(numberOfRecords, forKey: "myNumber")
myTableView.reloadData()
Then your delegate functions might look something like this
// Setting up Table View
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// return the total count of filenames in your array
return records.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
// set the filename in your text label
cell.textLabel?.text = records[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// get the filename for this row
let filename = records[indexPath.row]
// use your filename in your path
let path = getDirectory().appendingPathComponent("\(filename)")
do {
audioPlayer = try AVAudioPlayer(contentsOf: path)
audioPlayer.play()
}
catch {
}
}
and you could update your remove(at:)
call to run on the records array instead
records.remove(at: indexPath.row)
EDIT:
When you add or remove filenames from your records array update user defaults with your updated records:
// save array to user defaults when you create a new record or when you delete a record
UserDefaults.standard.setValue(records, forKey: "storedRecords")
To retrieve your saved array of filenames pull them from user defaults and update your records array with the stored names.
Replace these lines in your viewDidLoad
function:
if let number : Int = UserDefaults.standard.object(forKey: "myNumber") as? Int {
numberOfRecords = number
}
with this:
// load stored records from user defaults and verify it's what you expect to receive
if let stored = UserDefaults.standard.value(forKey: "storedRecords") as? [String] {
// update your records array with the stored values
records = stored
}