user2695433
user2695433

Reputation: 2163

Button with Image and Text vertically aligned using autolayout constraints

I m very new to IOS development and AutoLayout . I am facing issues to align the Image and Text inside UIbutton using Storyboard. I had tried to achieve it with TitleEdgeinset and ImageEdge insets accordingly to place the Title ( text ) vertically centered below the Image. But the issue is I have 3 similar buttons which are Vertically stacked ( StackView) and the Text is dynamically set since we have localized strings ( includes Arabic rtl ) . The image and text moves according to the text length. Is there any ways that I can achieve to make all the buttons with image and text vertically alligned.Also, different screen resolutions are not currently working if using edge insets. Appreciate your help . Thanks in advance.

Upvotes: 9

Views: 18791

Answers (9)

You can use this Custom Class

class VerticalButton: UIButton {

private let stackView = UIStackView()

override init(frame: CGRect) {
    super.init(frame: frame)
    setup()
}

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

private func setup() {
    stackView.axis = .vertical
    stackView.alignment = .center
    stackView.spacing = 5 // Adjust spacing between image and title
    stackView.isUserInteractionEnabled = false
    addSubview(stackView)
    
    stackView.translatesAutoresizingMaskIntoConstraints = false
    NSLayoutConstraint.activate([
        stackView.centerXAnchor.constraint(equalTo: centerXAnchor),
        stackView.centerYAnchor.constraint(equalTo: centerYAnchor),
        stackView.widthAnchor.constraint(equalTo: widthAnchor),
        stackView.heightAnchor.constraint(equalTo: heightAnchor)
    ])
}

override func setImage(_ image: UIImage?, for state: UIControl.State) {
    let imageView = UIImageView(image: image)
    imageView.contentMode = .scaleAspectFit
    stackView.addArrangedSubview(imageView)
}

override func setTitle(_ title: String?, for state: UIControl.State) {
    let titleLabel = UILabel()
    titleLabel.text = title
    titleLabel.textAlignment = .center
    titleLabel.font = self.titleLabel?.font
    stackView.addArrangedSubview(titleLabel)
}

override func layoutSubviews() {
    super.layoutSubviews()
    stackView.frame = bounds
}
}

How to Use:

 @IBOutlet weak var btnOutlet: VerticalButton!

 btnOutlet.setImage(UIImage(named: "Image-Name"), for: .normal)
 btnOutlet.setTitle("Title", for: .normal)

Upvotes: -1

evya
evya

Reputation: 3647

If you are using iOS 15.0 you can use:

// Create a button
let button = UIButton(type: .custom)
        
// Configure the button using UIButton.Configuration
var config = UIButton.Configuration.filled()
config.titlePadding = 10
config.imagePlacement = .top // this is whats makes the magic
config.imagePadding = 10
button.configuration = config

Upvotes: 2

abhimuralidharan
abhimuralidharan

Reputation: 5939

Combining Ajay's and Matej's answer:

UIButton with vertically aligned image and text

import Foundation
import UIKit
class VerticalButton: UIButton {
override func awakeFromNib() {
    super.awakeFromNib()
    self.contentHorizontalAlignment = .left
}

override func layoutSubviews() {
    super.layoutSubviews()
    centerButtonImageAndTitle()
}

private func centerButtonImageAndTitle() {
    let titleSize = self.titleLabel?.frame.size ?? .zero
    let imageSize = self.imageView?.frame.size  ?? .zero
    let spacing: CGFloat = 6.0
    self.imageEdgeInsets = UIEdgeInsets(top: -(titleSize.height + spacing),left: 0, bottom: 0, right:  -titleSize.width)
    self.titleEdgeInsets = UIEdgeInsets(top: 0, left: -imageSize.width, bottom: -(imageSize.height + spacing), right: 0)
 }
}

Upvotes: 5

Xavier Chia
Xavier Chia

Reputation: 257

Button with vertical image and text

I created a view, then put a VStack with image and text, as well as button. Make sure the VStack constraints are 0 on all 4 sides so it covers the button :)

Upvotes: 0

Matej Vargovčík
Matej Vargovčík

Reputation: 773

This is a solution that worked for me. Just set the button class to HorizontallyCenteredButton in storyboard and set the title and image top and bottom insets according to your needs (to place the image higher than title) and the button will adjust horizontal insets automatically so that the image is centered above title.

class HorizontallyCenteredButton: LocalizedButton {
    override func awakeFromNib() {
        super.awakeFromNib()
        self.contentHorizontalAlignment = .left
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        self.centerButtonImageAndTitle()
    }

    func centerButtonImageAndTitle() {
        let size = self.bounds.size
        let titleSize = self.titleLabel!.frame.size
        let imageSize = self.imageView!.frame.size

        self.imageEdgeInsets = UIEdgeInsets(top: self.imageEdgeInsets.top, left: size.width/2 - imageSize.width/2, bottom: self.imageEdgeInsets.bottom, right: 0)
        self.titleEdgeInsets = UIEdgeInsets(top: self.titleEdgeInsets.top, left: -imageSize.width + size.width/2 - titleSize.width/2, bottom: self.titleEdgeInsets.bottom, right: 0)
    }
}

Upvotes: 1

budiDino
budiDino

Reputation: 13547

I've modified Ajay's answer because my images weren't centered:

func centerButtonImageAndTitle(button: UIButton) {
  let spacing: CGFloat = 5
  let titleSize = button.titleLabel!.frame.size
  let imageSize = button.imageView!.frame.size

  button.titleEdgeInsets = UIEdgeInsets(top: 0, left: -imageSize.width, bottom: -(imageSize.height + spacing), right: 0)
  button.imageEdgeInsets = UIEdgeInsets(top: -(titleSize.height + spacing), left: -imageSize.width/2, bottom: 0, right: -titleSize.width)
}

Upvotes: 1

Russell
Russell

Reputation: 5554

this is an easy one, so apologies if you have already tried it!

you don't need to set anything for insets, just make sure that the 3 buttons are aligned with horizontal centres ... Add New Alignment Constraints

select the controls you want to align, and click on the button highlighted below (bottom right on the xCode screen) and select the Vertical Centres option to align the three with each other, or select Horizontally in Container to put them in the middle of your view.

Upvotes: -1

Ajay Kumar
Ajay Kumar

Reputation: 1827

Few days ago, I solved similar problem,try this

 private func adjustImageAndTitleOffsetsForButton (button: UIButton) {

    let spacing: CGFloat = 6.0

    let imageSize = button.imageView!.frame.size

    button.titleEdgeInsets = UIEdgeInsetsMake(0, -imageSize.width, -(imageSize.height + spacing), 0)

    let titleSize = button.titleLabel!.frame.size

    button.imageEdgeInsets = UIEdgeInsetsMake(-(titleSize.height + spacing), 0, 0, -titleSize.width)
}

call this method for each button, like

self.adjustImageAndTitleOffsetsForButton(yourButton)

Upvotes: 26

dudeman
dudeman

Reputation: 1136

To align all the buttons vertically, first select the buttons, then click the button on the bottom-right of the storyboard titled "Align", and finally select "Vertical Centers" in the menu that appears. That should do the trick.

Upvotes: 0

Related Questions