为什么 UITableViewCells 中的 UIButtons 会丢失它们在运行时设置的值并默认返回到它们的 Interface Builder 值?

Why do UIButtons inside UITableViewCells lose their values which were set at runtime and default back to their Interface Builder values?

编辑:使用已接受答案中的配置方法解决了问题。使用 setTitle 设置标题会更简单,但不可能,因为它必须是属性字符串。


在我目前正在处理的项目中,有一个 UITableView 从自定义 class 获取其单元格。每个单元格内都有一个标签和一个按钮。我在自定义 class' .xib 文件中为这两个元素的 text/fonts 设置默认值,然后在运行时覆盖这些值(这样做的原因是为了缩放尺寸和间距所有 UI 元素都适合任何尺寸的设备屏幕,当前设备直到运行时才知道)。

标签按我的预期工作,并保留以编程方式分配的新值。问题是按钮没有。它的文本开始显示新值,但一旦单击它就会默认返回到旧的 Storyboard 值,并且不会返回到运行时值。我不想显示占位符值,只显示我在 setLabelValues 函数中设置的值。

这是导致问题的代码(一个最小的可重现示例,不是原始程序,但它表现出完全相同的问题):

自定义 TABLE 查看单元格:

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)
    }
}

查看控制器:

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
    }
}

这里是 .xib 文件故事板的图片,显示了默认值。 Xcode window 的图片显示了 xib 文件和属性检查器。按钮的默认文本是系统红色的字符串“Buggy”。

另一种设置按钮值的方法是使用它的配置。它使您可以通过使用它的状态来更好地控制按钮属性,而不是直接访问 titleLabel

示例:

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
}

您也可以使用按钮的 setTitle 方法只设置按钮的标题:

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

看看这种方式是否会给您带来任何问题...