-1

I have a UIButton subclass intended to show a selected state of a button. The selected state simply places a thick black line at the bottom of the button view and when unselected it hides the black line. However, when using this in a UIButton subclass, the black line view is offset. I have tried playing around with insets, but I don't think that is the problem. Here is my subclass:

class TabButton: UIButton {

    private var height:CGFloat = 5
    private var selectedIndicator:UIView?

    override var isSelected: Bool {
        didSet { 
            selectedIndicator?.isHidden = !isSelected 
        }
    }

    fileprivate func initializeSelector(_ frame: CGRect) {
        selectedIndicator = UIView(frame: CGRect(x: 0, y: frame.size.height - height, width: frame.size.width, height: height))
        selectedIndicator?.backgroundColor = UIColor.black 
        self.addSubview(selectedIndicator!)
    }

    override func awakeFromNib() {
        super.awakeFromNib()
        initializeSelector(self.frame)
    }
}

The desired button should look like this:

enter image description here

But instead it looks like this:

enter image description here

Can anyone help me understand what is happening here and how to fix it? Thanks!

Pheepster
  • 6,045
  • 6
  • 41
  • 75

2 Answers2

1

Try this, in layoutSubviews you get the final frame:

override layoutSubviews() {
    super.layoutSubviews()
    selectedIndicator.frame = CGRect(x: 0, y: frame.size.height - height, width: frame.size.width, height: height)
}
Francesco Deliro
  • 3,899
  • 2
  • 19
  • 24
  • This answer is wrong. By calling `initializeSelector()` inside `layoutSubviews` you'll end up having multiple(as many as `layoutSubviews` is called) black views as the subview of button since `initializeSelector` inserts a new black `UIView` as subview. – Orkhan Alikhanov Nov 06 '17 at 14:51
  • You are right my fault, thank you ;) I was writing from mobile app and I didn’t notice that the method is adding the view. I was sure he was adding the view in awake from nib. – Francesco Deliro Nov 06 '17 at 15:09
1

The frame of the selectedIndicator is set only once when initializeSelector is called. When the button changes its frame, it does not change the frame of subviews, you need to manually update the frame of selectedIndicator.

To do so, you need to override layoutSubviews() method of UIView.

override layoutSubviews() {
    super.layoutSubviews()
    selectedIndicator?.frame = CGRect(x: 0, y: frame.size.height - height, width: frame.size.width, height: height)
}

See this answer to know when layoutSubviews() is called.

Orkhan Alikhanov
  • 9,122
  • 3
  • 39
  • 60