Here, I have a Table View that contains custom XIB cells. My goal is to update the custom XIB cell content by: (1) saving the "Task Name" UITextField text + (2) updating "Set Time" UIButton title to show, for example "1:30", after the time is set in the pop-up View Controller.
However, I got a "Fatal error: Index out of range: file Swift/ContiguousArrayBuffer.swift" message at "cell.timeButton.setTitle("(savedTaskTime[indexPath.row].hour + ":" + savedTaskTime[indexPath.row].minute[0])", for: .normal) when trying to update the "Set Time" UIButton title to show time.
-press set time->
Firstly, I have two public custom class types:
// Custom class type to set hour/min for task time
public class TimerData {
var hour: String
var minute: [String]
init(hour: String, minute: [String]) {
self.hour = hour
self.minute = minute
}
}
// Custom class type to set task name/time in UITableView
public class TaskData {
var name: String
var time: [String]
init(name: String, time: [String]) {
self.name = name
self.time = time
}
}
This is my code for the View Controller that contains the Table View with the custom XIB cells:
class TaskListViewController: UIViewController {
// MARK: - Outlet Variables
@IBOutlet weak var taskList: SelfSizedTableView!
...
// MARK: - Instance Variables
var taskData = [TaskData]()
var savedTaskName = [String]()
var savedTaskTime = [TimerData]()
...
// MARK: - View Controller Methods
override func viewDidLoad() {
super.viewDidLoad()
// Register TaskListCell.xib file
let taskCellNib = UINib(nibName: "TaskCell", bundle: nil)
taskList.register(taskCellNib, forCellReuseIdentifier: "taskCell")
...
}
// MARK: - Methods
// Segues to selectTime screen pop-up when "set time button" is pressed
@objc func showSelectTime(_ sender: UIButton) {
performSegue(withIdentifier: "selectTime", sender: nil)
}
}
extension TaskListViewController: UITableViewDataSource, UITableViewDelegate {
// Return the number of rows in table view
func tableView(_ tableView: UITableView,
numberOfRowsInSection section: Int) -> Int {
return taskCount
}
// Return the configured cell that is inserted in table view
func tableView(_ tableView: UITableView,
cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "taskCell", for: indexPath) as! TaskListCell
// Configure nameField in cell
cell.nameField.tag = indexPath.row
cell.nameField.delegate = self
cell.nameField.text = savedTaskName[indexPath.row]
// Configure timeButton in cell
cell.timeButton.tag = indexPath.row
cell.timeButton.addTarget(self, action: #selector(showSelectTime(_:)), for: .touchUpInside)
cell.timeButton.setTitle("(savedTaskTime[indexPath.row].hour + ":" + savedTaskTime[indexPath.row].minute[0])", for: .normal)
return cell
}
}
extension TaskListViewController: UITextFieldDelegate {
// Saves textField text input
func textFieldDidEndEditing(_ textField: UITextField) {
if let savedText = textField.text {
savedTaskName.append(savedText)
} else {
savedTaskName.append("")
}
}
// Resigns text field in focus + dismisses upon pressing return
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
}
This is my code for the View Controller that pops-up when the "set time button" is pressed:
class SelectTimeViewController: UIViewController {
// MARK: - Instance Variables
var timeFloat: Float = 0
var hour: String = "0"
var min: String = "00"
var savedTime = [TimerData]()
// MARK: - Outlet Variables
@IBOutlet weak var setTimeLabel: UILabel!
@IBOutlet weak var setTimeSlider: UISlider!
// MARK: - Action Methods
// Updates UISlider and setTimeLabel value
@IBAction func adjustTimeSlider(_ sender: UISlider) {
timeFloat = sender.value
updateTimeLabel()
}
// Save set time + returns to TaskList screen
@IBAction func saveTimeButton(_ sender: UIButton) {
let timeCheck = (hour, min)
if timeCheck != ("0", "00") {
savedTime.append(TimerData(hour: hour, minute: [min]))
dismiss(animated: true, completion: nil)
} else {
...
present(alert, animated: true, completion: nil)
}
}
// MARK: - View Controller Methods
override func viewDidLoad() {
super.viewDidLoad()
updateTimeLabel()
setTimeSlider.value = timeFloat
setTimeSlider.addTarget(self, action: #selector(adjustTimeSlider(_:)), for: .valueChanged)
}
// MARK: - Methods
// Update Time Label with Slider Value
func updateTimeLabel() {
let timeInt = Int(timeFloat)
let timeSet = "(hour + ":" + min)"
// Set hour value
hour = "(timeInt / 60)"
// Check min conditions + set min value
let minCheck = timeInt % 60
switch minCheck {
case 0:
min = "00"
case 1...9:
min = "0" + "(minCheck)"
default:
min = "(minCheck)"
}
// Update setTimeLabel
setTimeLabel.text = timeSet
}
// MARK: - Navigation
// Pass savedTime to savedTaskTime in TaskListViewController
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "savedTime" {
let controller = segue.destination as! TaskListViewController
controller.savedTaskTime = savedTime
}
}
}