Search code examples
iosswiftuibuttonuiimageviewuiimage

UIButton Image with PDF has full width image view


I'm trying to build a radio-style like button (a circle with a dot on the left, and text next to it on the right), pretty much like this:

enter image description here

I have 2 PDFs (link to one of them) containing images for selected and unselected radios. My code for the button is as follows:

let radioBtn = UIButton()
radioBtn.setImage(UIImage(named: "radio", in: .module, compatibleWith: nil), for: .normal)
radioBtn.setImage(UIImage(named: "radio_ticked", in: .module, compatibleWith: nil), for: .selected)
radioBtn.contentHorizontalAlignment = .leading
radioBtn.titleLabel?.adjustsFontSizeToFitWidth = true
radioBtn.titleEdgeInsets = UIEdgeInsets(top: 0, left: 5, bottom: 0, right: 0)
radioBtn.contentEdgeInsets = UIEdgeInsets(top: 8, left: 0, bottom: 8, right: 0)
radioBtn.heightAnchor.constraint(equalToConstant: 40).isActive = true
radioBtn.contentVerticalAlignment = .center
radioBtn.imageView?.contentMode = .scaleAspectFit

The problem is that the UIImage stretches over the whole width (the blue part) and there is no space for thee text to show:

((UIImageView *)0x157019080)

What I want to accomplish is the radio completely on the left, and then the text next to it with some inset. How can this be achieved?


Solution

  • First, your code is not setting a Width for your button. In that case, the button will set the width of its imageView to the size of the image -- with your pdf, that ends up being 510 pts wide.

    So, couple options...

    1. Use some scaling code to resize your image. If you're setting the button Height to 40, with 8-pts top and bottom insets, you need a 24x24 image.

    2. Give your button a Width constraint, and calculate the imageView insets "on-the-fly."

    Probably the easiest way to do that is with a UIButton subclass, such as this:

    class RadioButton: UIButton {
    
        override init(frame: CGRect) {
            super.init(frame: frame)
            commonInit()
        }
        required init?(coder: NSCoder) {
            super.init(coder: coder)
            commonInit()
        }
        func commonInit() -> Void {
    
            contentHorizontalAlignment = .leading
    
            // 8-pts inset "padding" on all 4 sides
            contentEdgeInsets = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8)
    
            // set title inset Left to content inset Left
            var insets: UIEdgeInsets = titleEdgeInsets
            insets.left = contentEdgeInsets.left
            titleEdgeInsets = insets
    
            // set images for .normal and .selected
            if let img = UIImage(named: "radio") {
                setImage(img, for: .normal)
            }
            
            if let img = UIImage(named: "radio_ticked") {
                setImage(img, for: .selected)
            }
            
        }
    
        override func layoutSubviews() {
            super.layoutSubviews()
    
            // make sure an image was set (otherwise, there is no imageView)
            if let imgView = imageView {
                // get height of imageView
                let h = imgView.frame.height
                // get current (default) image edge insets
                var insets: UIEdgeInsets = imageEdgeInsets
                // set inset Right to width of self minus imageView Height + Left and Right content insets
                insets.right = bounds.width - (h + contentEdgeInsets.left + contentEdgeInsets.right)
                // update image edge insets
                imageEdgeInsets = insets
            }
        }
    }
    

    example in use:

    class ViewController: UIViewController {
    
        override func viewDidLoad() {
            super.viewDidLoad()
            
            let radioBtn = RadioButton()
            
            // background color so we can see its frame
            radioBtn.backgroundColor = .red
            radioBtn.translatesAutoresizingMaskIntoConstraints = false
            view.addSubview(radioBtn)
    
            // give it a 22-pt bold font
            radioBtn.titleLabel?.font = .boldSystemFont(ofSize: 22.0)
            
            // set the Title
            radioBtn.setTitle("Testing", for: [])
    
            // Height: 40
            radioBtn.heightAnchor.constraint(equalToConstant: 40).isActive = true
            // Width: 240
            radioBtn.widthAnchor.constraint(equalToConstant: 240.0).isActive = true
            // centered in view
            radioBtn.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
            radioBtn.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
    
        }
    }
    

    Result:

    enter image description here