UIButton align image left and center text

306 views Asked by At

Introduction:

I have a class, which inherits from UIButton. In this class I want to update properties, like titleEdgeInsets, imageEdgeInsets, contentHorizontalAlignment.

My first approach was to use layoutSubviews:

override func layoutSubviews() {
    super.layoutSubviews()

    // update properties 
}

The layoutSubviews creates an infinity loop, so that I've searched for an alternative method.

My Question:

Is it a common way, to use the willMove method for updating UIButton properties?

override func willMove(toWindow newWindow: UIWindow?) {
    super.willMove(toWindow: newWindow)

    // update properties
}

If not, why?

My goal is to align the imageView of the button left (with padding) and center the text.

UPDATE:

I need the button frame.size and the bounds.width to calculate the position of the text and the image view

1

There are 1 answers

14
Sandeep Bhandari On BEST ANSWER

All the properties you mentioned above can be set in the init of the UIButton there is absolutely no need to set them in layoutSubviews or willMove(toWindow. layoutSubviews will be called multiple times so setting these properties again n agin in here makes no sense. willMove(toWindow will be called when button is added to some view and button is loaded but you dont have to wait till then to set these properties. Because you already have a subclass of button, so I would suggest doing

class SomeButton: UIButton {
    override init(frame: CGRect) {
        super.init(frame: frame)
        self.titleEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
        self.imageEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
        self.contentHorizontalAlignment = .center
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
}

By the way creating a subclass of UIButton is not recommended, so if you wanna simply assign these properties to your button you can rather have a extension to UIButton

extension UIButton {
    func applyStyle() {
        self.titleEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
        self.imageEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
        self.contentHorizontalAlignment = .center
    }
}

EDIT:

Is this what you want??

enter image description here

enter image description here

No matter what the text is, text is always in centre and image is to its left with 10 pixel padding

EDIT 2:

As OP has confirmed that, he wants the button to be styled as showed in images above, posting the code to achieve the same

class SomeButton: UIButton {
    var titleFont: UIFont! = nil
    var textSize: CGFloat = 0
    let imageWidth: CGFloat = 20
    let buttonHeight: CGFloat = 30

    override init(frame: CGRect) {
        super.init(frame: frame)
        self.titleFont = titleLabel!.font
        self.setTitle("here", for: .normal)
        self.setTitleColor(UIColor.red, for: .normal)
        self.setImage(UIImage(named: "hand"), for: .normal)
        self.backgroundColor = UIColor.green
    }

    override func titleRect(forContentRect contentRect: CGRect) -> CGRect {
        if let string = self.title(for: .normal) {
            textSize = string.widthOfString(usingFont: self.titleFont)
            //30 because imageWidth + 10 padding
            return CGRect(origin: CGPoint(x: 30, y: 0), size: CGSize(width: textSize + 30, height: buttonHeight))
        }
        return CGRect.zero
    }

    override func imageRect(forContentRect contentRect: CGRect) -> CGRect {
        return CGRect(origin: CGPoint(x: 0, y: 0), size: CGSize(width: imageWidth, height: buttonHeight))
    }
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }

    override var intrinsicContentSize: CGSize {
        //60 because you need eauql padding on both side 30 + 30 = 60
        return CGSize(width: textSize + 60, height: buttonHeight)
    }
}

extension String {

    func widthOfString(usingFont font: UIFont) -> CGFloat {
        let fontAttributes = [NSAttributedString.Key.font: font]
        let size = self.size(withAttributes: fontAttributes)
        return size.width
    }
}

Hope it helps