Search code examples
iosswiftuibuttonautolayoutuilabel

Align UIButton and UILabel text with different font sizes


I have a UIButton and a UILabel displayed inline. They have different size fonts, however I would like to align them so they appear on the same line.

At the moment the UILabel is slight above the baseline of the UIButton.

I was hoping to avoid manually setting a content offset as I want this to scale correctly where possible. I worry manual calculations may have unexpected side effects on changing font sizes etc.

not aligned elements

I have created a playground that should show the 2 elements:

//: A UIKit based Playground for presenting user interface

import UIKit
import PlaygroundSupport

class MyViewController : UIViewController {

  lazy var nameButton = configure(UIButton(type: .system), using: {
    $0.translatesAutoresizingMaskIntoConstraints = false
    $0.titleLabel?.font = .systemFont(ofSize: 18)
    $0.setTitleColor(.darkGray, for: .normal)
    $0.contentHorizontalAlignment = .leading
    $0.setContentHuggingPriority(UILayoutPriority.defaultHigh, for: .horizontal)
    $0.backgroundColor = .lightGray
    $0.setTitle("This is a button", for: .normal)
  })

  lazy var publishedDateLabel = configure(UILabel(frame: .zero), using: {
    $0.translatesAutoresizingMaskIntoConstraints = false
    $0.font = .systemFont(ofSize: 14)
    $0.textColor = .darkGray
    $0.setContentHuggingPriority(UILayoutPriority.defaultLow, for: .horizontal)
    $0.backgroundColor = .lightGray
    $0.text = "and this is a label"
  })

  override func loadView() {
    let view = UIView()
    view.backgroundColor = .white

    [nameButton, publishedDateLabel].forEach(view.addSubview(_:))

    NSLayoutConstraint.activate([
      nameButton.centerYAnchor.constraint(equalTo: view.centerYAnchor),
      nameButton.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 8),

      publishedDateLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor),
      publishedDateLabel.leadingAnchor.constraint(equalTo: nameButton.trailingAnchor),
      publishedDateLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -8)
    ])

    self.view = view
  }

  // setup helper method
  func configure<T>(_ value: T, using closure: (inout T) throws -> Void) rethrows -> T {
    var value = value
    try closure(&value)
    return value
  }
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()

I have tried making the label and button the same height by adding publishedDateLabel.heightAnchor.constraint(equalTo: nameButton.heightAnchor)

This didn't change the alignment however.

I also tried using publishedDateLabel.lastBaselineAnchor.constraint(equalTo: nameButton.lastBaselineAnchor) to align the anchors however this aligned the top of the elements

using bassline

How can align the bottom of the text in the button to the bottom of the text in the label?


Solution

  • Just comment out the heightAnchor use the lastBaselineAnchor:

    NSLayoutConstraint.activate([
          nameButton.centerYAnchor.constraint(equalTo: view.centerYAnchor),
          nameButton.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 8),
    
          publishedDateLabel.lastBaselineAnchor.constraint(equalTo: nameButton.lastBaselineAnchor),
          publishedDateLabel.leadingAnchor.constraint(equalTo: nameButton.trailingAnchor),
          publishedDateLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -8)
        ])
    

    Live playground view