Search code examples
iosswiftnsattributedstring

Underline covers text in NSAttributedString


I'm trying to create an attributed string but the underline covers my text instead of appearing behind it:

enter image description here

Is there a way to fix this? I'm using the following code:

let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineSpacing = 10.0

let attributes = [NSForegroundColorAttributeName: UIColor.white,
                  NSUnderlineStyleAttributeName: NSUnderlineStyle.styleThick.rawValue,
                  NSUnderlineColorAttributeName: UIColor.red,
                  NSParagraphStyleAttributeName: paragraphStyle]

let attributedString = NSAttributedString(string: "Story", attributes: attributes)

Thanks!

EDIT:

To give more context:

I'm displaying the attributed string on a UILabel placed in a .xib file:

view.textLabel.attributedText = attributedString

The label has the following font: System Bold 32.0

I'm running the code on iPhone 6 - iOS 10.3 simulator.

EDIT 2:

I should have mentioned that the label may, at some point, contain more than one line of text. That's why the numberOfLines is set to 0.

EDIT 3: If anybody encounters this problem -- it seems that there is a lot of difference in how underline is drawn on iOS 9 vs 10 as well as UILabel vs UITextView. I've ended up having to draw the underline myself by subclassing NSLayoutManager.


Solution

  • Yes, there is such problem as you have described. It shows up when you use multiline UILabel, so not only setting numberOfLines to 0, but type more than 1 line in it.

    Example

    let selectedStringAttributes: [String: Any]
        = [NSFontAttributeName: UIFont.boldSystemFont(ofSize: 28),
           NSForegroundColorAttributeName: UIColor.green,
           NSUnderlineStyleAttributeName: NSUnderlineStyle.styleSingle.rawValue,
           NSUnderlineColorAttributeName: UIColor.green]
    
    let label = UILabel(frame: CGRect(x: 100, y: 100, width: 500, height: 100))
    label.numberOfLines = 0
    
    label.attributedText = NSAttributedString(string: "String to test underline", attributes: selectedStringAttributes)
    

    And everything will look pretty good.

    But if you want to use such text:

    label.attributedText = NSAttributedString(string: "String to\ntest underline", attributes: selectedStringAttributes)
    

    or label's width is too short, than:

    Not so good

    So the reason for such behaviour is of course bug in NSAttributedString. As it mentioned in radar there is a workaround

    You should add this attribute to your NSAttributedString

    NSBaselineOffsetAttributeName: 0
    

    And magic will happen.