2

I am trying to create a configuration screen so that a user can set up the number of workouts he or she wants to do. I managed to add a tableview to a blank view controller but there were some stylistic limitation - such as expanding and collapsing the tableview according to the number of cells.

So I figured I would set up the screen in a tableviewcontroller. Now it's all going well, until I get to the point where I want my Stepper in section 0, cell 1, to increase and decrease the number of rows in section 1. It works to the extent that the data array (workouts[]) updates the view controller with the latest value (in the cellForRowAt:indexPath method) which in turn updates the numberOfRowsInSection with the (supposedly) latest workouts.count.

However, it turns out that workouts.count holds the count before the array was updated by the stepper last.

Now, I have tried a number of workaround but I still can't get the numberOfRowsInSection method to update properly - it seems to me that the Workouts array isn't updated in the View Controller with the latest value, rather the previous set value.

This is my view controller:

class AViewController: UITableViewController {

//MARK: Properties
@IBOutlet weak var aTableView: UITableView!

var sections: [String] = ["", "Set Up Circuits"]
var workouts: [Exercise] = []

//MARK: Initialisation
override func viewDidLoad() {
    super.viewDidLoad()
    workouts = [Exercise(name: "Circuit 1", timeMinutes: 2)]
    self.tableView.delegate = self
    self.tableView.dataSource = self
}

//MARK: Table Config
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
    return self.sections[section]
}

override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
    switch section {
    case 0:
        return 0.1
    default:
        return 32.0
    }
}

override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    switch indexPath.section {
    case 0:
        return 60
    default:
        return 44
    }
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

    var numberOfRowsInSection: Int = 0

    switch section {
    case 0:
        numberOfRowsInSection = 1
    default:
        numberOfRowsInSection = workouts.count
        //numberOfRowsInSection = 1
    }
    print(numberOfRowsInSection, "number of rows in section", section)
    return numberOfRowsInSection
}

//MARK: Table Protocols
override func numberOfSections(in tableView: UITableView) -> Int {
    return sections.count
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    switch indexPath.section {

    //Setting layout for circuit number config
    case 0:
        let cell = aTableView.dequeueReusableCell(withIdentifier: "ACircuitTableViewCell") as! ACircuitTableViewCell
        cell.tableView = self.aTableView
        self.workouts = cell.workouts
        print(workouts.count, " VC workouts array count")
        return cell

    //Setting layout for circuit cells
    default:
        let cell = aTableView.dequeueReusableCell(withIdentifier: "ATableViewCell") as! ATableViewCell
        cell.aLabel.text = workouts[indexPath.row].getName()
        cell.aDetail.text = "Tap here to configure"
        return cell

    }
}

This is my ACircuitTableViewCell:

class ACircuitTableViewCell: UITableViewCell {

//MARK: Properties
@IBOutlet weak var circuitNumber: UILabel!
@IBOutlet weak var circuitLabel: UILabel!
@IBOutlet weak var circStepper: UIStepper!


var tableView: UITableView!
// var updateCallback : ((_ updateList: Bool)-> Void)?

//var exercises: [Exercise]?
var anArray: [Exercise] = []
var workouts: [Exercise] = [Exercise(name: "Circuit 1", timeMinutes: 2)]

var workoutsCount: Int = 1
var indexPath: NSIndexPath = NSIndexPath(row: 0, section: 1)
//MARK: Original Functions
override func awakeFromNib() {
    super.awakeFromNib()
    // Initialization code

    circuitNumber.text = String(workouts.count)
}

override func setSelected(_ selected: Bool, animated: Bool) {
    super.setSelected(selected, animated: animated)
}

@IBAction func valueChanged(_ sender: AnyObject) {
    print("Method .valueChanged activated")

    //Update circuitNumber label
    circuitNumber.text = String(Int(circStepper.value))

    //View pre-array-update status of variables
    print(workouts.count, "anArray before appending")
    print(circStepper.value, "stepper value before appending")
    print(circuitNumber.text, "circuitNumber")

    //Update workouts array
    if workouts.count < Int(circStepper.value) {
        workouts.append(Exercise(name: "Circuit \(Int(circStepper.value))", timeMinutes: 2))
        print(workouts.count, "after appending")

        tableView.reloadData()

       print(workouts.count, "new workout.count")
    }


    print(workouts.count, "workouts.count")
    print("Method .valueChanged completed")

}

This is the output to the console from when the app loads, to three taps on the stepper + button.

1 number of rows in section 1
1 number of rows in section 0
1 number of rows in section 1
1 number of rows in section 0
1 number of rows in section 1
1 number of rows in section 0
1  VC workouts array count
Method .valueChanged activated
1 anArray before appending
2.0 stepper value before appending
Optional("2") circuitNumber
2 after appending
1 number of rows in section 1
1 number of rows in section 0
2 new workout.count
2 workouts.count
Method .valueChanged completed
2  VC workouts array count
Method .valueChanged activated
2 anArray before appending
3.0 stepper value before appending
Optional("3") circuitNumber
3 after appending
2 number of rows in section 1
1 number of rows in section 0
3 new workout.count
3 workouts.count
Method .valueChanged completed
3  VC workouts array count
Method .valueChanged activated
3 anArray before appending
4.0 stepper value before appending
Optional("4") circuitNumber
4 after appending
3 number of rows in section 1
1 number of rows in section 0
4 new workout.count
4 workouts.count
Method .valueChanged completed
4  VC workouts array count
Method .valueChanged activated
4 anArray before appending
5.0 stepper value before appending
Optional("5") circuitNumber
5 after appending
4 number of rows in section 1
1 number of rows in section 0
5 new workout.count
5 workouts.count
Method .valueChanged completed
5  VC workouts array count
Bista
  • 7,869
  • 3
  • 27
  • 55
Sonera
  • 23
  • 3
  • Please don't store data source in your cells (or any other views). It breaks MVC pattern. What you should do is to handle stepper callback in the controller and change viewController's dataSource there. – alexburtnik Oct 18 '16 at 10:30
  • @alexburtnik thank you! Once i figured out how to handle the stepper callback in the controller, the table functioned as i wanted it to. – Sonera Oct 19 '16 at 13:39
  • You're welcome. Just posted two common approaches for handling view events in the viewController. You've probably used one of them – alexburtnik Oct 19 '16 at 13:53

1 Answers1

1

You should handle stepper callback in the controller and change viewController's dataSource there.

  1. The most common way to handle cell events in a view controller is by using delegates, like I've described here: https://stackoverflow.com/a/40111943/1689376

  2. Alternatively you can add action with self target right in cellForRow method:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as! TableViewCell
    cell.stepper?.tag = indexPath.row
    cell.stepper?.addTarget(self, action: #selector(stepperValueChanged(_:)), for: .valueChanged)
    return cell
}

func stepperValueChanged(_ stepper: UIStepper) {
    //use stepper.tag as index to fetch an object from your dataSource
    let workout = dataSource[stepper.tag]
}
Community
  • 1
  • 1
alexburtnik
  • 7,661
  • 4
  • 32
  • 70