MrSakhr
MrSakhr

Reputation: 1

How to scale the title and image of a UIButton to match the size of the button?

I have a UIButton with both a title and an image. The button's size changes dynamically, but the title text and image inside the button do not resize proportionally to match the button's size. I want to make sure that both the title and the image adjust their sizes automatically based on the button's frame to maintain a balanced appearance.

Storyboard button configuration:

Storyboard button configuration

import UIKit

class ViewController: UIViewController {
    @IBOutlet weak var myBtn: UIButton!
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
}

Button on the simulator:

button on the simulator

I set the title and image for the UIButton, hoping they would resize automatically when the button's frame changes. However, the title and image remained the same size, not scaling proportionally with the button. I expected both to adjust dynamically to fit the button's new size, but this didn’t happen.

Upvotes: -1

Views: 85

Answers (1)

DonMag
DonMag

Reputation: 77423

UI components - including controls such as UIButton - are rarely a "one size fits all" solution.

Buttons do not auto-adjust the font size.

Options are to:

  • subclass UIButton and add customization code
  • set the font and image size on layout of the view
  • create a custom control of your own

Here is a sample custom button control that you may find helpful:

@IBDesignable
class MyCustomButton: UIControl {
    
    @IBInspectable
    public var image: UIImage? {
        didSet {
            imageView.image = image
            setNeedsLayout()
        }
    }

    @IBInspectable
    public var title: String = "Button" {
        didSet {
            titleLabel.text = title
            setNeedsLayout()
        }
    }
    
    private let imageView = UIImageView()
    private let titleLabel = UILabel()

    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    override func prepareForInterfaceBuilder() {
        self.backgroundColor = .systemBlue
    }
    
    private func commonInit() {
        
        backgroundColor = .systemBlue
        layer.cornerRadius = 8
        
        // Configure internal UI elements
        imageView.tintColor = .white
        imageView.contentMode = .scaleAspectFill
        
        titleLabel.textColor = .white
        titleLabel.textAlignment = .center
        
        titleLabel.text = title
        
        // let's put the imageView and titleLabel in a stack view
        let stackView = UIStackView(arrangedSubviews: [imageView, titleLabel])
        // adjust spacing as desired
        stackView.spacing = 0.0
        
        stackView.translatesAutoresizingMaskIntoConstraints = false
        addSubview(stackView)

        // Set up layout constraints
        NSLayoutConstraint.activate([
            stackView.centerXAnchor.constraint(equalTo: centerXAnchor),
            stackView.centerYAnchor.constraint(equalTo: centerYAnchor),
            
            // default imageView is ~75% of the button height
            //  based on your screen shots, you want it to be ~60%
            //  adjust as desired
            imageView.heightAnchor.constraint(equalTo: self.heightAnchor, multiplier: 0.60),
            // use 1:1 ratio for the image view
            imageView.widthAnchor.constraint(equalTo: imageView.heightAnchor),
        ])
        
        // make sure all subviews are not interactive
        //  so they don't consume touches
        for v in [imageView, titleLabel, stackView] {
            v.isUserInteractionEnabled = false
        }
        
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()

        // update font size based on button height
        
        // we want 17-pt font relative to a button height of 45.0
        let fSize: CGFloat = 17.0 * (bounds.height / 45.0)
        titleLabel.font = .systemFont(ofSize: fSize, weight: .regular)

        // hide image view if image is not set
        imageView.isHidden = imageView.image == nil
    }
    
    // MARK: - Touch Handling
    override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
        // Highlight the button on touch down
        alpha = 0.7
        return true
    }
    
    override func endTracking(_ touch: UITouch?, with event: UIEvent?) {
        // Reset appearance on touch up
        alpha = 1.0
    }
    
    override func cancelTracking(with event: UIEvent?) {
        // Reset appearance if the touch is canceled
        alpha = 1.0
    }
}

First, we add a UIImageView and a UILabel in a horizontal UIStackView.

Next, we constrain the size of the image view to 60% of the height of the control view (based on your screen-shots).

On layoutSubviews() we calculate the font size for the label. Again, based on you screen-shots, it looks like you want 17-pt font size (the default font size for a button) when its height is 45, so we set the new font size to 17.0 * (bounds.height / 45.0).

We also define this class as @IBDesignable with @IBInspectable title and image so we can lay it out and configure it in Storyboard.

Add a UIView and set its Custom Class:

class

Set the Image and Title properties:

properties

and we get this at run-time:

runtimeP

runtimeL

This is an example to get you started. See the inline // comments for ways you can customize it as desired.

Upvotes: 0

Related Questions