7

I have a label that uses a NSMutableAttributedString to write out the text as:

enter image description here

What I want to do is lower the asterisk's top padding so that it's midY is even with the word Cuisine like below:

enter image description here

How can I add padding using a NSMutableAttributedString?

I know I can create a separate label with the asterisk alone and use anchors w/ a constant to center it but I want to see how this is possible using a NSMutableAttributedString

let cuisineLabel: UILabel = {
    let label = UILabel()
    label.translatesAutoresizingMaskIntoConstraints = false

    let attributedText = NSMutableAttributedString(string: "Cuisine ", attributes: [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 17), NSAttributedStringKey.foregroundColor: UIColor.lightGray])

    attributedText.append(NSAttributedString(string: "*", attributes: [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 24), NSAttributedStringKey.foregroundColor: UIColor.red]))

    label.attributedText = attributedText

    return label
}()
Lance Samaria
  • 17,576
  • 18
  • 108
  • 256

2 Answers2

6

The baselineOffset attribute key is used for this purpose.

let cuisine = NSMutableAttributedString(string: "Cuisine")
let asterisk = NSAttributedString(string: "*", attributes: [.baselineOffset: -3])
cuisine.append(asterisk)

enter image description here

Obviously, you will have to calculate the offset using the font size of the rest of the text. This is why I believe that using a full width asterisk (*) is easier.

Result with full width asterisk (you might want its font size to be a proportion of the font size of the rest of the string):

enter image description here

Sweeper
  • 213,210
  • 22
  • 193
  • 313
  • what is a full width asterisk? – Lance Samaria Sep 14 '18 at 16:54
  • @LanceSamaria It's code point U+FF0A: http://www.fileformat.info/info/unicode/char/ff0a/index.htm – Sweeper Sep 14 '18 at 16:55
  • @Sweeper Is there any way to calculate the offset using the font size ? – Mitesh Dobareeya Dec 10 '20 at 13:44
  • Try breaking down the problem. To calculate the offset, you need to know how high the asterisk and how high the rest of the text is, which you can get with [`size(withAttributes:)`](https://developer.apple.com/documentation/foundation/nsstring/1531844-size). @MiteshDobareeya – Sweeper Dec 10 '20 at 13:48
  • @Sweeper I am using $ sing. Is there any other way to set the $ into the center? I found the high of $ and the rest of the text but it won't help to set into the center. I divided the remaining space but the $ going too much up. – Mitesh Dobareeya Dec 10 '20 at 14:11
4

As Code Different points out, you can do this with baselineOffset attribute. A value of -8 should work for your case:

import UIKit
import PlaygroundSupport

class MyViewController : UIViewController {
    override func loadView() {
        let view = UIView()
        view.backgroundColor = .white

        self.view = view

        let cuisineLabel: UILabel = {
            let label = UILabel()
            label.translatesAutoresizingMaskIntoConstraints = false
            label.frame = CGRect(x: 150, y: 200, width: 200, height: 20)
            let attributedText = NSMutableAttributedString(string: "Cuisine ", attributes: [
                NSAttributedStringKey.font: UIFont.systemFont(ofSize: 17),
                NSAttributedStringKey.foregroundColor: UIColor.lightGray])

            attributedText.append(NSAttributedString(string: "*", attributes: [
                NSAttributedStringKey.font: UIFont.systemFont(ofSize: 24),
                NSAttributedStringKey.baselineOffset: -8,
                NSAttributedStringKey.foregroundColor: UIColor.red]))

            label.attributedText = attributedText

            return label
        }()

        view.addSubview(cuisineLabel)

    }
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()

If you're struggling with line height offsets being messed up because of the new baseline and you're using a multi-line label, try playing with lineHeightMultiple:

let lineStyle = NSParagraphStyle()
lineStyle.lineHeightMultiple = 0.8

...

NSAttributedStringKey.paragraphStyle = style

If not (and you're using multiple labels stacked on top of one another) then you probably just need to adjust the frame of each label in the series to compensate.

brandonscript
  • 68,675
  • 32
  • 163
  • 220
  • I used Sweeper and rmaddy suggestions of using a different size asterisk with unicode character to keep the size at 17. Thanks for the help. Btw I upvoted you. – Lance Samaria Sep 14 '18 at 17:12
  • 1
    The only issue that I encountered was when attempting to set `lineHeightMultiple`. `let lineStyle = NSParagraphStyle()` should be `let lineStyle = NSMutableParagraphStyle()`. – Nick Kohrn Nov 11 '20 at 19:43
  • Good catch, @NickKohrn – brandonscript Nov 16 '20 at 05:08