Custom UIButton subclass not displaying title

2019-06-09 22:34发布

问题:

Let me start off by saying I am aware that this is a frequently asked question, I just can't seem to find someone with the same scenario/problem as me.

I am writing a music application, and I came up with a UI that I liked. It required a button with special functionality (past what can be achieved by the Custom Button type) so I decided to make a UIButton subclass. I used the following code in my subclass:

required init(coder aDecoder: NSCoder) {
    self.activeState = false
    self.activeAccidental = UIImageView()
    super.init(coder: aDecoder)
    self.layer.borderColor = UIColor(white:221/255, alpha: 1.0).CGColor
    self.layer.borderWidth = 2.0
    self.backgroundColor = UIColor.blackColor()
    self.setTitleColor(UIColor.whiteColor(), forState: .Normal)
    self.setTitle("Hello", forState: .Normal)
}

override func layoutSubviews() {
    println(self.frame.origin.x)
    self.activeAccidental = UIImageView(frame: CGRectMake(self.bounds.origin.x, self.bounds.origin.y, 20, 20))
    self.activeAccidental.image = UIImage(named: "BMICalcIcon.png")
    self.addSubview(activeAccidental)
}

However, when I add a button to my storyboard (and enter the custom class name in the field) my title, regardless if set in the initializer as shown, or in the attributes inspector in the storyboard, is not visible. This is my first major project in swift, so I am not sure entirely what the problem is.

Code when ImageView moved to initWithCoder

required init(coder aDecoder: NSCoder) {
    self.activeState = false
    self.activeAccidental = UIImageView()
    super.init(coder: aDecoder)
    self.activeAccidental = UIImageView(frame: CGRectMake(self.bounds.origin.x, self.bounds.origin.y, 20, 20))
    self.activeAccidental.image = UIImage(named: "BMICalcIcon.png")
    self.layer.borderColor = UIColor(white:221/255, alpha: 1.0).CGColor
    self.layer.borderWidth = 2.0
    self.backgroundColor = UIColor.blackColor()
    self.setTitleColor(UIColor.whiteColor(), forState: .Normal)
    self.setTitle("Hello", forState: .Normal)
    self.addSubview(activeAccidental)
}

回答1:

I'm not sure why, but the problem is putting the code for adding the image view in layoutSubviews -- that's not a good place anyway, since it can be called multiple times, which will cause you to add multiple image views. If you move that code into your initWithCoder method, it will work properly. Here's the test class I made,

import UIKit

class RDButton: UIButton {

    var activeState: Bool
    var activeAccidental: UIImageView

    required init(coder aDecoder: NSCoder) {
        self.activeState = false
        self.activeAccidental = UIImageView()
        super.init(coder: aDecoder)
        self.activeAccidental = UIImageView(frame: CGRectMake(self.bounds.origin.x, self.bounds.origin.y, 20, 20))
        self.activeAccidental.image = UIImage(named: "img.jpg")
        self.layer.borderColor = UIColor(white:221/255, alpha: 1.0).CGColor
        self.layer.borderWidth = 2.0
        self.backgroundColor = UIColor.blackColor()
        self.setTitleColor(UIColor.whiteColor(), forState: .Normal)
        self.setTitle("Hello", forState: .Normal)
        self.addSubview(activeAccidental)

    }
}


回答2:

Maybe add [super layoutSubviews]; I had the same problem because I forgot to add this piece of line. After that you can add the code you want.



回答3:

This also happens when the current view controller (containing the custom button) was started with performSegue(withIdentifier:sender:) from a queue other than the main. To fix this, just call the method within main thread. For example:

class MyViewController: ViewController {
    ...
    func doTask() {
        let myTask = MyTask()
        myTask.perform( {
            Dispatch.main.async {
                self.performSegue(withIdentifier: "second", sender: nil)
            }
        })
    }
}