Reputation: 918
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.
Upvotes: 2
Views: 5293
Reputation: 4080
I know its late reply but after hours search, found a fix as below
extension NSAttributedString {
func withUnderlineSpacing(spacing: CGFloat) -> NSAttributedString {
let attributedString = NSMutableAttributedString(attributedString: self)
attributedString.addAttribute(.underlineStyle, value: NSUnderlineStyle.single.rawValue, range: NSRange(location: 0, length: self.length))
attributedString.addAttribute(.baselineOffset, value: spacing, range: NSRange(location: 0, length: self.length))
return attributedString
}
}
Upvotes: 0
Reputation: 136
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.
Upvotes: 6
Reputation: 2219
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
}
}
Upvotes: 2
Reputation: 535944
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.)
Upvotes: 3
Reputation: 15778
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)
Upvotes: 2