iosswiftuikituislider

Slider value is not updated


Can anyone help identify my problem here. I am experimenting with a UITable and a Slider, forcing interaction between the two. I want to select a row in the table and have the slider show the value corresponding to a label in that row and then the slider should update the label in the selected row when in changes. It seems to be working well, other than the very first selection when I boot up the test app. The row is selected, the correct value is passed into the slider, but the slider value does not show what is passed to it.

I have an example here with print statements.

enter image description here

When I run the code in simulator and select the first line in the table (first integer is 65), the didSelectRowAt function returns 65 correctly, and slider.value is set to that value. However, the print statement immediately after the setting of slider.value shows 1.0 and the slider on the app has value 1.0.

When I click again on the first row, the function again correctly identifies 65 as the value but this time the slider value is correctly set to 65. If I select any other row then the app functions correctly. Can anyone see what i am doing wrong such that the very first selection does not update the slider value?

My code is here:

import UIKit

class ViewController: UIViewController { 
    @IBOutlet weak var tableView: UITableView!
    @IBOutlet weak var slider: UISlider!{
        didSet{
            slider.transform = CGAffineTransform(rotationAngle: CGFloat(-Double.pi/2))
        }
    }
    
    @IBAction func sliderValueChanged(_ sender: Any) {
        if selectedRowIndexPath == [] {return}//no row selected
        
        let cell = tableView.cellForRow(at: selectedRowIndexPath)
        let pField = cell?.contentView.viewWithTag(1) as? UILabel
        pField?.text = String(format: "%d", Int(slider.value))
        blendSolution[selectedRowIndexPath.row].p = Double(slider.value)
        Swift.print(slider.value)
    }
    
    struct Gas {
        var fO2, fHe, fN2, p, pCum, c, lO2, lHe, lN2 : Double
        var desc = String()
        init(fO2: Double, fHe: Double, p: Double, pCum: Double, c:Double){
            self.fO2 = fO2
            self.fHe = fHe
            self.fN2 = 1 - fO2 - fHe
            self.p = p
            self.pCum = pCum
            self.c = c
            self.lO2 = fO2 * p * c
            self.lHe = fHe * p * c
            self.lN2 = (1 - fO2 - fHe) * p * c
            self.desc = gasDesc(fO2: fO2, fHe: fHe)
        }
        func gasDesc(fO2: Double, fHe: Double) -> String {
            if fO2 == 0.21 {return "Air"}
            if fO2 == 1.0 {return "O2"}
            if fHe == 1.0 {return "He"}
            if fHe == 0 {return "EAN\(Int(round(fO2 * 100)))"}
            return "\(Int(round(fO2 * 100)))/\(Int(round(fHe * 100)))"
        }
    }
    
    var selectedRowIndexPath = IndexPath()
    
    let startGas = Gas(fO2: 0.12, fHe: 0.50, p:45, pCum: 45, c: 3)
    let targetGas = Gas(fO2: 0.16, fHe: 0.36, p:232, pCum: 232, c: 3)
    var deltaGas = (lO2: Double(), lHe: Double(), lN2: Double())
    var blendSolution = [Gas]()
    var c = Double()

    override func viewDidLoad() {
            super.viewDidLoad()
        
        tableView.dataSource = self
        tableView.delegate = self
        tableView.isEditing = true
        
        blendSolution = [
            Gas(fO2: 0.17, fHe: 0.45, p:65.0, pCum: 65.0, c: 3),
            Gas(fO2: 0.09, fHe: 0.65, p:40.0, pCum: 105.0, c: 3),
            Gas(fO2: 0.12, fHe: 0.66, p:20.0, pCum: 125.0, c: 3),
            Gas(fO2: 0.21, fHe: 0, p:30.0, pCum: 155.0, c: 3),
            Gas(fO2: 0.0, fHe: 1.0, p:40.0, pCum: 195.0, c: 3),
            Gas(fO2: 1.0, fHe: 0.0, p:15.0, pCum: 210.0, c: 3),
            Gas(fO2: 0.32, fHe: 0.0, p:22.0, pCum: 232.0, c: 3)
        ]
        
        c = targetGas.c
    }
}

extension ViewController: UITableViewDataSource, UITableViewDelegate {

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return blendSolution.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "blendSolutionCell", for: indexPath)
        let pField = cell.contentView.viewWithTag(1) as! UILabel
        pField.text = String(Int(round(blendSolution[indexPath.row].p)))
        
        let gasField = cell.contentView.viewWithTag(2) as! UILabel
        gasField.text = String(blendSolution[indexPath.row].desc)
        
        let pCum = cell.contentView.viewWithTag(3) as! UILabel
        pCum.text = String(Int(round(blendSolution[indexPath.row].pCum)))
        
        let intGas = cell.contentView.viewWithTag(4) as! UILabel
        intGas.text = "Dummy"
        return cell
    }
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        Swift.print("value \( Float(blendSolution[indexPath.row].p))")
        slider.value = Float(blendSolution[indexPath.row].p)
        Swift.print("slider \(slider.value)")
        slider.maximumValue = 232
        slider.minimumValue = 0
        selectedRowIndexPath = indexPath
    }
    
    func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
        let movedObject = self.blendSolution[sourceIndexPath.row]
        blendSolution.remove(at: sourceIndexPath.row)
        blendSolution.insert(movedObject, at: destinationIndexPath.row)
    }
    
    func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCell.EditingStyle {
        //disallow delete
        return .none
    }
    
    func tableView(_ tableView: UITableView, shouldIndentWhileEditingRowAt indexPath: IndexPath) -> Bool {
        //remove indent when editing
        return false
    }
}

Solution

  • The problem is that initially, the slider's min and max default to 0.0 and 1.0 respectively. You don't change the min and max values of the slider until later in didSelectRowAt. The attempt to set the slider's value to a value at the beginning of didSelectRowAt gets normalized to the current min and max. Since 65 is greater than 1.0, the slider's value is forced to 1.0. You then set a new min and max so future changes to the slider work as expected.

    The simple fix is to move the code that sets the slider's min and max values to your didSet block of the slider property.

    @IBOutlet weak var slider: UISlider!{
        didSet{
            slider.transform = CGAffineTransform(rotationAngle: CGFloat(-Double.pi/2))
            slider.maximumValue = 232
            slider.minimumValue = 0
        }
    }
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        Swift.print("value \( Float(blendSolution[indexPath.row].p))")
        slider.value = Float(blendSolution[indexPath.row].p)
        Swift.print("slider \(slider.value)")
        selectedRowIndexPath = indexPath
    }
    

    Or you can set the min and max values directly on the slider in the storyboard instead of setting them in code.