19

I'm adding an icon to a UILabel using NSTextAttachment inside an NSMutableAttributedString like this:

//Setting up icon
let moneyIcon = NSTextAttachment()
moneyIcon.image = UIImage(named: "MoneyIcon")
let moneyIconString = NSAttributedString(attachment: moneyIcon)

//Setting up text
let balanceString = NSMutableAttributedString(string: " 1,702,200")
balanceString.insert(moneyIconString, at: 0)

//Adding string to label
self.attributedText = balanceString
self.sizeToFit()

But for some reason the icon isn't vertically aligned

Does anybody know how can I align it?

Thank you!

Tamás Sengel
  • 55,884
  • 29
  • 169
  • 223
FS.O6
  • 1,394
  • 2
  • 20
  • 42
  • [Check out this question](https://stackoverflow.com/questions/19487369/center-two-different-size-fonts-vertically-in-an-nsattributedstring), which is about two differently sized fonts, but should give you an idea. – Tamás Sengel Dec 16 '17 at 12:56
  • @the4kman Unfortunately it doesn't help. What you've sent is about font sizes, and the icon doesn't have a font size of course... Any other idea? – FS.O6 Dec 16 '17 at 15:09
  • What do you want to do exactly? Put up the "text"? Then use the `NSBaselineOffsetAttributeName` for the `balanceString`. Or put down the image? Then change the `bounds` of `moneyIcon`: https://stackoverflow.com/questions/26105803/center-nstextattachment-image-next-to-single-line-uilabel – Larme Dec 19 '17 at 10:17

2 Answers2

31

use bounds property of NSTextAttachment.

//Setting up icon
let moneyIcon = NSTextAttachment()
moneyIcon.image = UIImage(named: "MoneyIcon")

let imageSize = moneyIcon.image!.size
moneyIcon.bounds = CGRect(x: CGFloat(0), y: (font.capHeight - imageSize.height) / 2, width: imageSize.width, height: imageSize.height)

let moneyIconString = NSAttributedString(attachment: moneyIcon)

//Setting up text
let balanceString = NSMutableAttributedString(string: " 1,702,200")
balanceString.insert(moneyIconString, at: 0)

//Adding string to label
self.attributedText = balanceString
self.sizeToFit()
Warif Akhand Rishi
  • 23,920
  • 8
  • 80
  • 107
20

This answer, which is about vertically centering two differently sized fonts in a single NSAttributedString, mentions using the baseline offset to calculate the center of the string.

You can use the same approach when using an image:

  1. Subtract the font size from the image's height and divide it by 2.

  2. Subtract the font's descender from the value (since font size isn't the same as the ascent of your font). The font that you are particularly using (Baloo-Regular) has a descender value that differs from the standard and it should be divided by 2. Other fonts (including San Fransisco) don't need that fix or require a different divisor.

This code covers most cases, if your font behaves differently, you should check out the guide for managing texts in Text Kit.

// *Setting up icon*

let moneyIcon = NSTextAttachment()

// If you're sure a value is not and will never be nil, you can use "!".
// Otherwise, avoid it.

let moneyImage = UIImage(named: "MoneyIcon")!

moneyIcon.image = moneyImage
let moneyIconString = NSAttributedString(attachment: moneyIcon)

// *Setting up NSAttributedString attributes*

let balanceFontSize: CGFloat = 16

let balanceFont = UIFont(name: "Baloo", size: balanceFontSize)!

let balanceBaselineOffset: CGFloat = {
    let dividend =  moneyImage.size.height - balanceFontSize

    return dividend / 2 - balanceFont.descender / 2
}()

let balanceAttr: [NSAttributedString.Key: Any] = [
    .font: balanceFont,
    .baselineOffset: balanceBaselineOffset
]

// *Setting up text*

let balanceString = NSMutableAttributedString(
    string: " 1,702,200",
    attributes: balanceAttr
)

balanceString.insert(moneyIconString, at: 0)
Tamás Sengel
  • 55,884
  • 29
  • 169
  • 223