Home > database >  Why do UIButtons inside UITableViewCells lose their values which were set at runtime and default bac
Why do UIButtons inside UITableViewCells lose their values which were set at runtime and default bac

Time:03-04

EDIT: Using the configuration method from the accepted answer fixed the problem. Setting the title with setTitle would be simpler, but isn't possible since it has to be an attributed string.


In a project I'm working on currently, there is a UITableView which gets its cells from a custom class. Inside each cell is a label and a button. I set default values for the text/fonts of both of these elements in the custom class' .xib file, and then override the values at runtime (the reason for doing this is to scale the sizes and spacing of all UI elements to fit on any size device screen, when the current device isn't known until runtime).

The label works as I intended, and keeps the new value which is assigned programmatically. The problem is that the button doesn't. Its text starts out showing the new value, but then defaults back to the old Storyboard value as soon as it's clicked, and won't go back to the runtime value. I don't want the placeholder value to ever show, only the value I set in the setLabelValues function.

Here is the code which is causing the problem (a minimal reproducible example, not the original program, but it is exhibiting the exact same problem):

CUSTOM TABLE VIEW CELL:

import UIKit

class testCellTableViewCell: UITableViewCell {
    @IBOutlet weak var textLabelLeading: NSLayoutConstraint!
    @IBOutlet weak var textLabelTrailing: NSLayoutConstraint!
    
    @IBOutlet weak var buttonLabelLeading: NSLayoutConstraint!
    @IBOutlet weak var buttonLabelTrailing: NSLayoutConstraint!
    
    @IBOutlet weak var workingCorrectlyLabel: UILabel!
    @IBOutlet weak var buggyButton: UIButton!
    
    override func awakeFromNib() {
        super.awakeFromNib()
    }
    
    func setLabelValues(screenWidth: CGFloat, screenHeight: CGFloat) {
        textLabelLeading.constant = 0.025 * screenWidth
        textLabelTrailing.constant = -0.4 * screenWidth
        
        buttonLabelLeading.constant = 0.6 * screenWidth
        buttonLabelTrailing.constant = -0.025 * screenWidth
        
        workingCorrectlyLabel.font = UIFont.systemFont(ofSize: 0.025*screenHeight, weight: .regular)
        
        buggyButton.titleLabel?.text = "Fixed!"
        buggyButton.titleLabel?.textColor = .green
        buggyButton.titleLabel?.font = UIFont.systemFont(ofSize: 0.025*screenHeight, weight: .medium)
    }
    
    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)
    }
}

VIEW CONTROLLER:

import UIKit

class ViewController: UIViewController {
    @IBOutlet weak var testTable: UITableView!
    
    var labelMessages = ["This is a test", "Second Cell", "Another Label"]
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        testTable.dataSource = self
        testTable.register(UINib(nibName: "testCellTableViewCell", bundle: nil), forCellReuseIdentifier: "testCellID")
        testTable.layoutMargins = UIEdgeInsets.zero
        testTable.separatorInset = UIEdgeInsets.zero
    }
}

extension ViewController: UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        tableView.layer.backgroundColor = CGColor(red: 0.000, green: 0.000, blue: 0.000, alpha: 1.000)
        return labelMessages.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = testTable.dequeueReusableCell(withIdentifier: "testCellID", for: indexPath) as! testCellTableViewCell
        cell.workingCorrectlyLabel.text = labelMessages[indexPath.row]
        cell.layoutMargins = UIEdgeInsets.zero
        cell.setLabelValues(screenWidth: view.safeAreaLayoutGuide.layoutFrame.width, screenHeight: view.safeAreaLayoutGuide.layoutFrame.height)
        return cell
    }
}

And here is a picture of the .xib file storyboard, showing the default values. Picture of Xcode window showing xib file and attributes inspector. The default text for the button is the string "Buggy" in the system red color.

enter image description here

CodePudding user response:

Another way to set button values is by using its configuration. It gives you more control over the button properties by using it's state, instead of accessing the titleLabel directly

Example:

func setLabelValues(screenWidth: CGFloat, screenHeight: CGFloat) {
    textLabelLeading.constant = 0.025 * screenWidth
    textLabelTrailing.constant = -0.4 * screenWidth

    buttonLabelLeading.constant = 0.6 * screenWidth
    buttonLabelTrailing.constant = -0.025 * screenWidth

    workingCorrectlyLabel.font = UIFont.systemFont(ofSize: 0.025*screenHeight, weight: .regular)

    // Original way
    buggyButton.titleLabel?.text = "Fixed!"
    buggyButton.titleLabel?.textColor = .green
    buggyButton.titleLabel?.font = UIFont.systemFont(ofSize: 0.025*screenHeight, weight: .medium)

    // Configuration way
    // this is for the attributed title to set on the button later
    var container = AttributeContainer()
    container.font = UIFont.systemFont(ofSize: 0.025*screenHeight, weight: .medium)

    buggyButton.configurationUpdateHandler = { button in 
    switch button.state {
    // you can switch over the different button states here, but i'll use default to make all button states the same
    default:
        button.configuration = .plain() // or .filled() or .tinted() whatever you prefer
        button.configuration?.attributedTitle = AttributedString("Fixed", attributes: container)
        button.configuration?.baseForegroundColor = .green
}

You can also just set the title of the button using setTitle method of the button:

buggyButton.setTitle("Fixed", for: .normal)

See if this way gives you any issues...

  • Related