【问题标题】:Designable Button does not show images from assets folder可设计按钮不显示资产文件夹中的图像
【发布时间】:2021-10-15 16:12:17
【问题描述】:

我自定义了一个按钮,我想让它在情节提要中可设计。 如果我将背景设置为存储在资产文件夹中的图像,则情节提要不会显示任何内容。我仍然得到一个透明按钮。有什么想法可以解决这个问题吗?

import UIKit

@IBDesignable class AnswerButton: UIButton {
    
    private static let BTN_NORMAL_IMAGE_NAME = "ButtonNormal"
    private static let BTN_ANSWERED_IMAGE_NAME = "ButtonAnswered"

    
    enum AnswerState : Int {
        case normal = 0
        case answered
        case correctAnswered
        case wrongAnswered
    }
    
    var answerState: AnswerState = .normal {
        didSet {
            if answerState == .normal {
                self.answerLabel.textColor = UIColor.white
                self.setBackgroundImage(UIImage(named: AnswerButton.BTN_NORMAL_IMAGE_NAME), for: .normal)
            } else if answerState == .answered {
                self.answerLabel.textColor = UIColor.black
                self.setBackgroundImage(UIImage(named: AnswerButton.BTN_ANSWERED_IMAGE_NAME), for: .normal)
            } else if answerState == .correctAnswered {
                self.answerLabel.textColor = UIColor.blue
                self.setBackgroundImage(UIImage(named: AnswerButton.BTN_ANSWERED_IMAGE_NAME), for: .normal)
            }
        
        }
    }
    
    @IBInspectable var myAnswerState: Int {
        get {
            return self.answerState.rawValue
        }
        set (value) {
            self.setAnswerState(AnswerState(rawValue: value) ?? .normal)
        }
    }
    
    
    @IBInspectable var letter: String = "" {
        didSet {
            self.letterLabel.text = String(letter.first!) + ":"
        }
    }
    
    @IBInspectable var answer: String = "" {
        didSet {
            self.answerLabel.text = answer
        }
    }
    
    public let letterLabel: UILabel = {
        let label = UILabel()
        label.numberOfLines = 1
        label.textAlignment = .left
        label.textColor = UIColor.orange
        label.font = UIFont(name: "Arial Rounded MT Bold", size: 24)
        
        return label
    }()

    public let answerLabel: UILabel = {
        let label = UILabel()
        label.numberOfLines = 1
        label.textAlignment = .left
        label.textColor = UIColor.white
        label.font = UIFont(name: "Arial Rounded MT Bold", size: 22)
        
        return label
    }()
    
        
    override func layoutSubviews() {
        super.layoutSubviews()
        
        self.letterLabel.frame = CGRect(x: 40, y: 0, width: 30, height: self.frame.height)
        self.answerLabel.frame = CGRect(x: 80, y: 0, width: 310, height: self.frame.height)
    }
    
    
    private func setAnswerState(_ state: AnswerState) {
        self.answerState = state
    }
    
    private func setLetter(_ letter: String) {
        self.letter = letter
    }
    
    private func setAnswer(_ answer: String) {
        self.answer = answer
    }
    
    init(letter: String, selectionState: AnswerState, answer: String) {
        super.init(frame: CGRect(x: 0, y: 0, width: 420, height: 47))
        self.sharedInit()
    }
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        self.sharedInit()
    }
    
    @objc
    func touchUpInside(sender: UIButton) {
        self.setAnswerState(.answered)
    }
    
    func sharedInit() {
        self.adjustsImageWhenHighlighted = false
        self.addTarget(self, action: #selector(touchUpInside), for: .touchUpInside)
        
        self.setLetter(letter)
        self.setAnswer(answer)
        self.setAnswerState(.normal)
        
        self.addSubview(self.letterLabel)
        self.addSubview(self.answerLabel)
    }
    
    override func prepareForInterfaceBuilder() {
        self.sharedInit()
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        self.sharedInit()
    }
}

一个名为 ButtonBackground 的图像位于 assets 文件夹中。

【问题讨论】:

  • 请添加一些代码。你是怎么设置的?
  • A UIButton 已经有一个可以在 Storyboard 中设置的 Background Image 属性,因此您完全不清楚您要完成什么。编辑您的帖子,清晰描述您正在做什么,并包含来自您的 @IBDesignable 子类按钮的完整代码。
  • 我知道它有背景图片,但我想通过代码设置它。所以我想给按钮一个状态,状态处理背景图像。

标签: ios swift storyboard


【解决方案1】:

您的代码中有一些问题...

首先,要在设计时加载图像/@IBDesignable,您需要告诉 Interface Builder 从何处获取它们:

let dynamicBundle = Bundle(for: AnswerButton.self)
let img = UIImage(named: AnswerButton.BTN_NORMAL_IMAGE_NAME, in: dynamicBundle, compatibleWith: nil)

除非您对包结构做一些不寻常的事情,否则您可以在设计时和运行时使用它。也就是说,你没有条件代码。

接下来,在你的@IBInspectable var letter / didSet

@IBInspectable var letter: String = "" {
    didSet {
        self.letterLabel.text = String(letter.first!) + ":"
    }
}

确保你的字符串不为空,否则会崩溃:

@IBInspectable var letter: String = "" {
    didSet {
        if !letter.isEmpty {
            self.letterLabel.text = String(letter.first!) + ":"
        }
    }
}

接下来,当您更改 @IBInspectable 值时,您的 sharedInit() 函数将被调用,所以不要这样调用:

self.setAnswerState(.normal)

否则您的按钮将永远不会反映任何其他价值。

而且,您为标签设置框架的方式看起来有问题 - 如果按钮宽度小于 390 点,answerLabel 将不适合。

最后,我建议在初始化时加载您的两个图像,而不是每次状态更改时都重新加载。

以下是您班级的最新动态:

@IBDesignable class AnswerButton: UIButton {
    
    private static let BTN_NORMAL_IMAGE_NAME = "ButtonNormal"
    private static let BTN_ANSWERED_IMAGE_NAME = "ButtonAnswered"
    
    private var normalImage: UIImage!
    private var answeredImage: UIImage!

    enum AnswerState : Int {
        case normal
        case answered
        case correctAnswered
        case wrongAnswered
    }
    
    var answerState: AnswerState = .normal {
        didSet {
            switch answerState {
            case .answered:
                self.answerLabel.textColor = UIColor.black
                self.setBackgroundImage(answeredImage, for: .normal)

            case .correctAnswered:
                self.answerLabel.textColor = UIColor.blue
                self.setBackgroundImage(answeredImage, for: .normal)
                
            case .wrongAnswered:
                self.answerLabel.textColor = UIColor.red
                self.setBackgroundImage(answeredImage, for: .normal)
                
            default:
                self.answerLabel.textColor = UIColor.white
                self.setBackgroundImage(normalImage, for: .normal)
                
            }
        }
    }
    
    @IBInspectable var myAnswerState: Int {
        get {
            return self.answerState.rawValue
        }
        set (value) {
            if let t: AnswerState = AnswerState(rawValue: value) {
                self.setAnswerState(t)
            } else {
                self.setAnswerState(.normal)
            }
        }
    }
    
    
    @IBInspectable var letter: String = "" {
        didSet {
            if !letter.isEmpty {
                self.letterLabel.text = String(letter.first!) + ":"
            }
        }
    }
    
    @IBInspectable var answer: String = "" {
        didSet {
            self.answerLabel.text = answer
        }
    }
    
    public let letterLabel: UILabel = {
        let label = UILabel()
        label.numberOfLines = 1
        label.textAlignment = .left
        label.textColor = UIColor.orange
        label.font = UIFont(name: "Arial Rounded MT Bold", size: 24)
        label.backgroundColor = UIColor.red.withAlphaComponent(0.25)
        return label
    }()
    
    public let answerLabel: UILabel = {
        let label = UILabel()
        label.numberOfLines = 1
        label.textAlignment = .left
        label.textColor = UIColor.white
        label.font = UIFont(name: "Arial Rounded MT Bold", size: 22)
        label.backgroundColor = UIColor.green.withAlphaComponent(0.25)
        return label
    }()
    
    
    override func layoutSubviews() {
        super.layoutSubviews()
        
        self.letterLabel.frame = CGRect(x: 40, y: 0, width: 30, height: self.frame.height)
        self.answerLabel.frame = CGRect(x: 80, y: 0, width: 310, height: self.frame.height)
    }
    
    
    private func setAnswerState(_ state: AnswerState) {
        self.answerState = state
    }
    
    private func setLetter(_ letter: String) {
        self.letter = letter
    }
    
    private func setAnswer(_ answer: String) {
        self.answer = answer
    }
    
    init(letter: String, selectionState: AnswerState, answer: String) {
        super.init(frame: CGRect(x: 0, y: 0, width: 420, height: 47))
        setAnswer(answer)
        self.sharedInit()
    }
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        self.sharedInit()
    }
    
    @objc
    func touchUpInside(sender: UIButton) {
        self.setAnswerState(.answered)
    }
    
    func sharedInit() {
        self.adjustsImageWhenHighlighted = false
        self.addTarget(self, action: #selector(touchUpInside), for: .touchUpInside)
        
        self.setLetter(letter)
        self.setAnswer(answer)
        
        // don't call this here!
        //self.setAnswerState(.normal)

        self.addSubview(self.letterLabel)
        self.addSubview(self.answerLabel)
        
        // load normal / answered images once
        let dynamicBundle = Bundle(for: AnswerButton.self)

        if let img = UIImage(named: AnswerButton.BTN_NORMAL_IMAGE_NAME, in: dynamicBundle, compatibleWith: nil) {
            self.normalImage = img
        }
        if let img = UIImage(named: AnswerButton.BTN_ANSWERED_IMAGE_NAME, in: dynamicBundle, compatibleWith: nil) {
            self.answeredImage = img
        }
    }
    
    override func prepareForInterfaceBuilder() {
        super.prepareForInterfaceBuilder()
        self.sharedInit()
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        self.sharedInit()
    }
}

【讨论】:

  • 非常感谢。仍然有很多测试代码初始化,但你解决了我的问题。我用动态捆绑包试过一次,但没有用。现在它起作用了。我正在为 ipad pro 10.5 开发一款游戏。所以我保持简单,在里面放了很多硬编码的尺寸。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多