Search code examples
swiftnsparagraphstyle

Maximum lineheight cutting off text


[broken[1]

I'm looking to make a pixelPerfect UI. My designer gave me a few requirements for a specific font. I'm trying to implement them but I've run into some issues with paragraphStyle. All text first baselines must be positioned on a grid. When setting maximumLineHeight for my Title to 24 (2x grid) everything is fine. However, my bodyText needs a lineHeight of 16. This cuts off the top of the first line.

I've tried setting the label height, this just adds layout conflicts. I've tried setting the firstBaseline constraint to a grid measure away from it's own top. To no avail. I've also overridden the UILabel's draw method to add some additional top padding. However this throws off the fristBaseline constraint usage in my code by whatever padding I added. Very dirty and only the ultimate last resort.

import Foundation

public class UILabelBody: UILabel  {


    override public var text: String? {
        didSet {
            let attributedString = NSMutableAttributedString(string: self.text!)
            let paragraphStyle = NSMutableParagraphStyle()
            paragraphStyle.maximumLineHeight = 16

            attributedString.addAttributes([
                NSAttributedString.Key.kern: 0.4,
                NSAttributedString.Key.paragraphStyle : paragraphStyle],
                                           range: NSMakeRange(0, attributedString.length))
            self.attributedText = attributedString
        }
    }

    public init() {
        super.init(frame: CGRect.zero)
        self.translatesAutoresizingMaskIntoConstraints = false
        setup()
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    private func setup() {
        self.font = UIFont(name: "Oxygen", size: 16)
        self.textColor = UIColor(hexString: "1F1F1F")
    }
}

Here's the class that i set the constraints: private func setup() {

    self.addSubview(contentBox)
    self.addSubview(imageView)
    self.addSubview(title)
    self.addSubview(body)


    imageView.backgroundColor = UIColor.yellow.withAlphaComponent(0.8)
    contentBox.backgroundColor = UIColor.red.withAlphaComponent(0.1)
    title.backgroundColor = UIColor.orange.withAlphaComponent(0.2)

    let padding = Layout.grid(1.5)

    let highPriority = UILayoutPriority(1000)
    let lowPrioririty = UILayoutPriority(100)

    let titleFirstBaseline = title.firstBaselineAnchor.constraint(equalTo: contentBox.topAnchor, constant: Layout.grid(2.5))
    let bodyFirstBaseLine =  body.firstBaselineAnchor.constraint(equalTo: title.lastBaselineAnchor, constant: Layout.grid(2))
    let selfHeight = self.heightAnchor.constraint(equalToConstant: Layout.grid(28))
    let selfWidth = self.widthAnchor.constraint(equalToConstant: Layout.grid(30))
    selfWidth.priority = highPriority
    selfHeight.priority = lowPrioririty

    titleFirstBaseline.priority = highPriority
    bodyFirstBaseLine.priority = highPriority


    NSLayoutConstraint.activate([


            selfWidth,
            selfHeight,

            contentBox.topAnchor.constraint(equalTo: self.topAnchor, constant: padding),
            contentBox.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: padding),
            contentBox.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -padding),
            contentBox.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -padding),

            imageView.leadingAnchor.constraint(equalTo: contentBox.leadingAnchor),
            imageView.topAnchor.constraint(equalTo: contentBox.topAnchor),
            imageView.widthAnchor.constraint(equalToConstant: Layout.grid(4)),
            imageView.heightAnchor.constraint(equalToConstant: Layout.grid(4)),

            title.leadingAnchor.constraint(equalTo: imageView.trailingAnchor, constant: padding),
            titleFirstBaseline,
            title.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -padding),

            bodyFirstBaseLine,
            body.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: padding),
            body.bottomAnchor.constraint(equalTo: self.contentBox.bottomAnchor),
            body.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -padding),
    ])
}

Solution

  • After spending a few hours in the torture chamber I finally figured it out! The issue was the font itself. It was an .otf and not a .ttf. Because of this swift did not understand the font's internal lineheight and just cut it off. After switching to .ttf the issue was resolved

    enter image description here