简体   繁体   中英

Underline covers text in NSAttributedString

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

在此处输入图像描述

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.

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:

不太好

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.

On my machine, showing your attributed string in a black-backgrounded UILabel, it makes a quite nice-looking display:

在此处输入图像描述

The red thick underline is nicely separated from the text, and is interrupted to allow the descender of the "y" to pass through it.

NOTE You cannot combine the font of the UILabel (set in Interface Builder) with its attributedText . You must set the entire label's text formatting in the attributedText . So, my code looks like this:

    let attributes : [String:Any] = [NSForegroundColorAttributeName: UIColor.white,
                      NSUnderlineStyleAttributeName: NSUnderlineStyle.styleThick.rawValue,
                      NSUnderlineColorAttributeName: UIColor.red,
                      NSFontAttributeName: UIFont.boldSystemFont(ofSize: 32)]
    let attributedString = NSAttributedString(string: "Story", attributes: attributes)
    lab.backgroundColor = .black
    lab.attributedText = attributedString

(You will notice that I removed your stipulation of the paragraph line spacing; there is only one line, so this stipulation adds nothing. However, I get the same result even if I restore it.)

Instead of using NSAttributedString you can draw border below the label with x space using this.

let space:CGFloat = 10

let border = CALayer()
border.backgroundColor = UIColor.red.cgColor
border.frame = CGRect(x: 0, y: (label?.frame.size.height)! + space, width: (label?.frame.size.width)!, height: 1)
label?.layer.addSublayer(border)

So this is my solution to this issue. I think it is "cleaner" and easier. Post me if you dont understand :)

class BottomLineTextField: UITextField {

    var bottomBorder = UIView()

    override func awakeFromNib() {
        super.awakeFromNib()
        setBottomBorder()
    }

    func setBottomBorder() {            
        self.translatesAutoresizingMaskIntoConstraints = false

        bottomBorder = UIView.init(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
        hasError = false
        bottomBorder.translatesAutoresizingMaskIntoConstraints = false

        addSubview(bottomBorder)

        bottomBorder.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
        bottomBorder.leftAnchor.constraint(equalTo: leftAnchor).isActive = true
        bottomBorder.rightAnchor.constraint(equalTo: rightAnchor).isActive = true
        bottomBorder.heightAnchor.constraint(equalToConstant: 1).isActive = true // Set underline height
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM