为什么 UITableViewCells 内的 UIButton 会丢失运行时设置的值并默认返回其 Interface Builder 值?

发布于 2025-01-11 10:57:02 字数 3355 浏览 1 评论 0原文

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


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

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

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

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

这是 .xib 文件情节提要的图片,显示默认值。显示 xib 文件和属性检查器的 Xcode 窗口图片。该按钮的默认文本是系统红色的字符串“Buggy”。

输入图片此处描述

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

不再让梦枯萎 2025-01-18 10:57:02

设置按钮值的另一种方法是使用其配置。它使您可以通过使用按钮的状态来更好地控制按钮属性,而不是直接访问 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)

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

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文