I have a UILabel I've made programmatically as:
var label = UILabel()
I've then declared some styling for the label, including a font, such as:
label.frame = CGRect(x: 20, y: myHeaderView.frame.height / 2, width: 300, height: 30)
label.font = UIFont(name: "Typo GeoSlab Regular Demo", size: 15)
label.textColor = UIColor(hue: 0/360, saturation: 0/100, brightness: 91/100, alpha: 1)
The first part of the label will always read : "Filter:" then followed by another part of the string, for example, "Most popular"
I would like the word filter to be in bold, so the whole thing would look like:
Filter: Most popular
I want to simplest way of creating this effect. I've been searching the internet for how to achieve this and there are so many ways, some which just look like pages of code. And most of it seems to be in Objective-C. I would like it in Swift please :)
I don't know if i'm on the right lines, but is this what NSRange can help achieve?
Update
I use a series of if statements to change my label variable. Such as:
if indexArray == 1 {
label.text = "Filter: Film name"
} else if indexArray == 2 {
label.text = "Filter: Most popular"
} else if indexArray == 3 {
label.text = "Filter: Star rating"
}
You will want to use attributedString which allows you to style parts of a string etc. This can be done like this by having two styles, one normal, one bold, and then attaching them together:
let boldText = "Filter:"
let attrs = [NSAttributedString.Key.font : UIFont.boldSystemFont(ofSize: 15)]
let attributedString = NSMutableAttributedString(string:boldText, attributes:attrs)
let normalText = "Hi am normal"
let normalString = NSMutableAttributedString(string:normalText)
attributedString.append(normalString)
When you want to assign it to a label:
label.attributedText = attributedString
You can use NSMutableAttributedString and NSAttributedString to create customized string. The function below makes given boldString bold in given string.
Swift 3
func attributedText(withString string: String, boldString: String, font: UIFont) -> NSAttributedString {
let attributedString = NSMutableAttributedString(string: string,
attributes: [NSFontAttributeName: font])
let boldFontAttribute: [String: Any] = [NSFontAttributeName: UIFont.boldSystemFont(ofSize: font.pointSize)]
let range = (string as NSString).range(of: boldString)
attributedString.addAttributes(boldFontAttribute, range: range)
return attributedString
}
Example usage
authorLabel.attributedText = attributedText(withString: String(format: "Author : %#", user.name), boldString: "Author", font: authorLabel.font)
Swift 4
func attributedText(withString string: String, boldString: String, font: UIFont) -> NSAttributedString {
let attributedString = NSMutableAttributedString(string: string,
attributes: [NSAttributedStringKey.font: font])
let boldFontAttribute: [NSAttributedStringKey: Any] = [NSAttributedStringKey.font: UIFont.boldSystemFont(ofSize: font.pointSize)]
let range = (string as NSString).range(of: boldString)
attributedString.addAttributes(boldFontAttribute, range: range)
return attributedString
}
Swift 4.2 and 5
func attributedText(withString string: String, boldString: String, font: UIFont) -> NSAttributedString {
let attributedString = NSMutableAttributedString(string: string,
attributes: [NSAttributedString.Key.font: font])
let boldFontAttribute: [NSAttributedString.Key: Any] = [NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: font.pointSize)]
let range = (string as NSString).range(of: boldString)
attributedString.addAttributes(boldFontAttribute, range: range)
return attributedString
}
Result:
Swift 4.2 & 5.0:
First off we create a protocol that UILabel, UITextField and UITextView can adopt.
public protocol ChangableFont: AnyObject {
var rangedAttributes: [RangedAttributes] { get }
func getText() -> String?
func set(text: String?)
func getAttributedText() -> NSAttributedString?
func set(attributedText: NSAttributedString?)
func getFont() -> UIFont?
func changeFont(ofText text: String, with font: UIFont)
func changeFont(inRange range: NSRange, with font: UIFont)
func changeTextColor(ofText text: String, with color: UIColor)
func changeTextColor(inRange range: NSRange, with color: UIColor)
func resetFontChanges()
}
We want to be able to add multiple changes to our text, therefore we create the rangedAttributes property. It's a custom struct that holds attributes and the range in which they are applied.
public struct RangedAttributes {
public let attributes: [NSAttributedString.Key: Any]
public let range: NSRange
public init(_ attributes: [NSAttributedString.Key: Any], inRange range: NSRange) {
self.attributes = attributes
self.range = range
}
}
Another problem is that UILabel its font property is strong and UITextField its font property is weak/optional. To make them both work with our ChangableFont protocol we include the getFont() -> UIFont? method. This also counts for UITextView its text and attributedText properties. That's why we implement the getter and setter methods for them as well.
extension UILabel: ChangableFont {
public func getText() -> String? {
return text
}
public func set(text: String?) {
self.text = text
}
public func getAttributedText() -> NSAttributedString? {
return attributedText
}
public func set(attributedText: NSAttributedString?) {
self.attributedText = attributedText
}
public func getFont() -> UIFont? {
return font
}
}
extension UITextField: ChangableFont {
public func getText() -> String? {
return text
}
public func set(text: String?) {
self.text = text
}
public func getAttributedText() -> NSAttributedString? {
return attributedText
}
public func set(attributedText: NSAttributedString?) {
self.attributedText = attributedText
}
public func getFont() -> UIFont? {
return font
}
}
extension UITextView: ChangableFont {
public func getText() -> String? {
return text
}
public func set(text: String?) {
self.text = text
}
public func getAttributedText() -> NSAttributedString? {
return attributedText
}
public func set(attributedText: NSAttributedString?) {
self.attributedText = attributedText
}
public func getFont() -> UIFont? {
return font
}
}
Now we can go ahead and create the default implementation for UILabel, UITextField and UITextView by extending our protocol.
public extension ChangableFont {
var rangedAttributes: [RangedAttributes] {
guard let attributedText = getAttributedText() else {
return []
}
var rangedAttributes: [RangedAttributes] = []
let fullRange = NSRange(
location: 0,
length: attributedText.string.count
)
attributedText.enumerateAttributes(
in: fullRange,
options: []
) { (attributes, range, stop) in
guard range != fullRange, !attributes.isEmpty else { return }
rangedAttributes.append(RangedAttributes(attributes, inRange: range))
}
return rangedAttributes
}
func changeFont(ofText text: String, with font: UIFont) {
guard let range = (self.getAttributedText()?.string ?? self.getText())?.range(ofText: text) else { return }
changeFont(inRange: range, with: font)
}
func changeFont(inRange range: NSRange, with font: UIFont) {
add(attributes: [.font: font], inRange: range)
}
func changeTextColor(ofText text: String, with color: UIColor) {
guard let range = (self.getAttributedText()?.string ?? self.getText())?.range(ofText: text) else { return }
changeTextColor(inRange: range, with: color)
}
func changeTextColor(inRange range: NSRange, with color: UIColor) {
add(attributes: [.foregroundColor: color], inRange: range)
}
private func add(attributes: [NSAttributedString.Key: Any], inRange range: NSRange) {
guard !attributes.isEmpty else { return }
var rangedAttributes: [RangedAttributes] = self.rangedAttributes
var attributedString: NSMutableAttributedString
if let attributedText = getAttributedText() {
attributedString = NSMutableAttributedString(attributedString: attributedText)
} else if let text = getText() {
attributedString = NSMutableAttributedString(string: text)
} else {
return
}
rangedAttributes.append(RangedAttributes(attributes, inRange: range))
rangedAttributes.forEach { (rangedAttributes) in
attributedString.addAttributes(
rangedAttributes.attributes,
range: rangedAttributes.range
)
}
set(attributedText: attributedString)
}
func resetFontChanges() {
guard let text = getText() else { return }
set(attributedText: NSMutableAttributedString(string: text))
}
}
With in the default implementation I use a little helper method for getting the NSRange of a substring.
public extension String {
func range(ofText text: String) -> NSRange {
let fullText = self
let range = (fullText as NSString).range(of: text)
return range
}
}
We're done! You can now change parts of the text its font and text color.
titleLabel.text = "Welcome"
titleLabel.font = UIFont.systemFont(ofSize: 70, weight: .bold)
titleLabel.textColor = UIColor.black
titleLabel.changeFont(ofText: "lc", with: UIFont.systemFont(ofSize: 60, weight: .light))
titleLabel.changeTextColor(ofText: "el", with: UIColor.blue)
titleLabel.changeTextColor(ofText: "co", with: UIColor.red)
titleLabel.changeTextColor(ofText: "m", with: UIColor.green)
Swift 4 alternative:
let attrs = [NSAttributedStringKey.font : UIFont.boldSystemFont(ofSize: 14)]
let attributedString = NSMutableAttributedString(string: "BOLD TEXT", attributes:attrs)
let normalString = NSMutableAttributedString(string: "normal text")
attributedString.append(normalString)
myLabel.attributedText = attributedString
You can directly do on String if you prefer:
extension String {
func withBoldText(text: String, font: UIFont? = nil) -> NSAttributedString {
let _font = font ?? UIFont.systemFont(ofSize: 14, weight: .regular)
let fullString = NSMutableAttributedString(string: self, attributes: [NSAttributedString.Key.font: _font])
let boldFontAttribute: [NSAttributedString.Key: Any] = [NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: _font.pointSize)]
let range = (self as NSString).range(of: text)
fullString.addAttributes(boldFontAttribute, range: range)
return fullString
}}
Usage:
label.attributeString = "my full string".withBoldText(text: "full")
for the ones who prefer extensions
Swift 5.0
/// will set a regual and a bold text in the same label
public func setRegualAndBoldText(regualText: String,
boldiText: String) {
let attrs = [NSAttributedString.Key.font : UIFont.boldSystemFont(ofSize: font.pointSize)]
let regularString = NSMutableAttributedString(string: regualText)
let boldiString = NSMutableAttributedString(string: boldiText, attributes:attrs)
regularString.append(boldiString)
attributedText = regularString
}
and use:
label.setRegualAndBoldText(regualText: "height: ", boldiText: "1.65 :(")
Just sharing my own quite-flexible implementation in Swift 4.0. Cause there are some requirements, like mine currently, that you need to set not only bold but italic the part of a label's text.
import UIKit
extension UILabel {
/** Sets up the label with two different kinds of attributes in its attributed text.
* #params:
* - primaryString: the normal attributed string.
* - secondaryString: the bold or highlighted string.
*/
func setAttributedText(primaryString: String, textColor: UIColor, font: UIFont, secondaryString: String, secondaryTextColor: UIColor, secondaryFont: UIFont) {
let completeString = "\(primaryString) \(secondaryString)"
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.alignment = .center
let completeAttributedString = NSMutableAttributedString(
string: completeString, attributes: [
.font: font,
.foregroundColor: textColor,
.paragraphStyle: paragraphStyle
]
)
let secondStringAttribute: [NSAttributedStringKey: Any] = [
.font: secondaryFont,
.foregroundColor: secondaryTextColor,
.paragraphStyle: paragraphStyle
]
let range = (completeString as NSString).range(of: secondaryString)
completeAttributedString.addAttributes(secondStringAttribute, range: range)
self.attributedText = completeAttributedString
}
}
If you know which character place values you want to bold I created a function which takes ranges of characters and optional fonts (use nil if you just want to use the standard system font of size 12), and returns an NSAttributedString which you can attach to a label as its attributed text. I wanted to bolden the 0th, 10th, 22-23rd, 30th and 34th characters of my string so i used [[0,0], [10,10], [22,23], [30,30], [34,34]] for my boldCharactersRanges value.
Usage:
func boldenParts(string: String, boldCharactersRanges: [[Int]], regularFont: UIFont?, boldFont: UIFont?) -> NSAttributedString {
let attributedString = NSMutableAttributedString(string: string, attributes: [NSAttributedString.Key.font: regularFont ?? UIFont.systemFont(ofSize: 12)])
let boldFontAttribute: [NSAttributedString.Key: Any] = [NSAttributedString.Key.font: boldFont ?? UIFont.boldSystemFont(ofSize: regularFont?.pointSize ?? UIFont.systemFontSize)]
for range in boldCharactersRanges {
let currentRange = NSRange(location: range[0], length: range[1]-range[0]+1)
attributedString.addAttributes(boldFontAttribute, range: currentRange)
}
return attributedString
}
override func viewDidLoad() {
super.viewDidLoad()
let label = UILabel()
label.frame = CGRect(x: 0, y: 0, width: 180, height: 50)
label.numberOfLines = 0
label.center = view.center
let text = "Under the pillow is a vogue article"
let secretMessage = boldenParts(string: text, boldCharactersRanges: [[0,0], [10,10], [22,23], [30,30], [34,34]], regularFont: UIFont(name: "Avenir", size: 15), boldFont: UIFont(name: "Avenir-Black", size: 15))
label.attributedText = secretMessage
view.addSubview(label)
}
Swift 4.0 solution
let font = UIFont.systemFont(ofSize: 14)
func boldSearchResult(searchString: String, resultString: String) -> NSMutableAttributedString {
let attributedString: NSMutableAttributedString = NSMutableAttributedString(string: resultString)
guard let regex = try? NSRegularExpression(pattern: searchString.lowercased(), options: []) else {
return attributedString
}
let range: NSRange = NSMakeRange(0, resultString.count)
regex.enumerateMatches(in: resultString.lowercased(), options: [], range: range) { (textCheckingResult, matchingFlags, stop) in
guard let subRange = textCheckingResult?.range else {
return
}
attributedString.addAttributes([NSAttributedString.Key.font : font], range: subRange)
}
return attributedString
}
Related
Kerning is not working if i pass -0.36, if i take screen shot from iPhone and comparing with design the string is not matching the length.
func addCharacterSpacing(kernValue: Double = 1.15) {
if let labelText = text, labelText.count > 0 {
let attributedString = NSMutableAttributedString(string: labelText)
attributedString.addAttribute(NSAttributedString.Key.kern, value: kernValue, range: NSRange(location: 0, length: attributedString.length - 1))
attributedText = attributedString
}
}
Finally ended with up by creating #discardableResult func, that exactly match with screen shot from iPhone and comparing with design. Pass parms accordingly.
#discardableResult func applyAttributesWithKerning(_ text: String, font:UIFont, lineSpace: CGFloat, charSpace: CGFloat, color:UIColor) -> NSMutableAttributedString {
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineSpacing = lineSpace
var attrs: [NSAttributedString.Key: Any] = [NSAttributedString.Key.paragraphStyle: paragraphStyle]
attrs[NSAttributedString.Key.kern] = charSpace
attrs[NSAttributedString.Key.font] = font
attrs[NSAttributedString.Key.foregroundColor] = color
let boldString = NSMutableAttributedString(string:text, attributes: attrs)
append(boldString)
return self
}
I have the following which allows me to create a bullet list which works really well, however, after the bullet list is created I need to manipulate the outputted Attributed string to have certain elements either in bold or in italics or both.
The function I have is:
#IBOutlet var label: UILabel!
let bulletString = ["String 1","String 2","String 3"]
label.attributedText = label.bulletPoints(stringList: bulletString, font: UIFont.stdFontMediumSeventeen, bullet: "•", lineSpacing: 4, paragraphSpacing: 4, textColor: UIColor.darkGreyColor, bulletColor: UIColor.darkGreyColor)
func bulletPoints(stringList: [String],font: UIFont,bullet: String = "\u{2022}",indentation: CGFloat = 20,lineSpacing: CGFloat = 2,paragraphSpacing: CGFloat = 12,textColor: UIColor = .gray,bulletColor: UIColor = .red) -> NSAttributedString{
let textAttributes: [NSAttributedStringKey: Any] = [NSAttributedStringKey.font: font, NSAttributedStringKey.foregroundColor: textColor]
let bulletAttributes: [NSAttributedStringKey: Any] = [NSAttributedStringKey.font: font, NSAttributedStringKey.foregroundColor: bulletColor]
let paragraphStyle = NSMutableParagraphStyle()
let nonOptions = [NSTextTab.OptionKey: Any]()
paragraphStyle.tabStops = [NSTextTab(textAlignment: .left, location: indentation, options: nonOptions)]
paragraphStyle.defaultTabInterval = indentation
paragraphStyle.lineSpacing = lineSpacing
paragraphStyle.paragraphSpacing = paragraphSpacing
paragraphStyle.headIndent = indentation
let bulletList = NSMutableAttributedString()
for string in stringList {
let formattedString = "\(bullet)\t\(string)\n"
let attributedString = NSMutableAttributedString(string: formattedString)
attributedString.addAttributes(
[NSAttributedStringKey.paragraphStyle : paragraphStyle],
range: NSMakeRange(0, attributedString.length))
attributedString.addAttributes(
textAttributes,
range: NSMakeRange(0, attributedString.length))
let string:NSString = NSString(string: formattedString)
let rangeForBullet:NSRange = string.range(of: bullet)
attributedString.addAttributes(bulletAttributes, range: rangeForBullet)
bulletList.append(attributedString)
}
return bulletList
}
What I am looking for is a way to pass in a boolean to state if the bullet string requires either bold or italic text and if so what the elements of the intital string are that need this treatment.
The bulletPoints function sits in an extension file and works as expected.
Using a model to link the bold/italic to the strings in question, as Neil suggests, helps you in this case. Here's a version which links font traits to the strings for each bullet, then uses those when building up the string.
I've refactored your bulletPoints function as well, to remove the use of ranges and simplify it a little. It could stay in an extension (I assume you have it on UILabel?) but there's no reason for it to, since it returns the string anyway. I've written it as a function which could be used in any class
class ViewController: UIViewController {
#IBOutlet var label: UILabel!
override func viewWillAppear(_ animated: Bool) {
let bulletStrings = [BulletString(string: "String 1", traits: []),
BulletString(string: "String 2", traits: [.traitBold]),
BulletString(string: "String 3", traits: [.traitItalic]),
BulletString(string: "String 4", traits: [.traitBold, .traitItalic])]
label.attributedText = bulletPoints(stringList: bulletStrings, font: UIFont.systemFont(ofSize: 15.0), bullet: "•", lineSpacing: 4, paragraphSpacing: 4, textColor: UIColor.darkGray, bulletColor: UIColor.darkGray)
}
func bulletPoints(stringList: [BulletString],
font: UIFont,
bullet: String = "\u{2022}",
indentation: CGFloat = 20,
lineSpacing: CGFloat = 2,
paragraphSpacing: CGFloat = 12,
textColor: UIColor = .gray,
bulletColor: UIColor = .red) -> NSAttributedString {
let bulletList = NSMutableAttributedString()
for bulletString in stringList {
let attributedString = NSMutableAttributedString(string: "")
let bulletAttributes: [NSAttributedStringKey: Any] = [
.foregroundColor: bulletColor,
.font: font]
attributedString.append(NSAttributedString(string: bullet, attributes: bulletAttributes))
let textAttributes: [NSAttributedStringKey: Any] = [
.font: font.withTraits(traits: bulletString.traits),
.foregroundColor: textColor,
.paragraphStyle : paragraphStyle(indentation: indentation, lineSpacing: lineSpacing, paragraphSpacing: paragraphSpacing)
]
attributedString.append(NSAttributedString(string:"\t\(bulletString.string)\n", attributes: textAttributes))
bulletList.append(attributedString)
}
return bulletList
}
private func paragraphStyle(indentation: CGFloat, lineSpacing: CGFloat, paragraphSpacing: CGFloat) -> NSParagraphStyle {
let style = NSMutableParagraphStyle()
let nonOptions = [NSTextTab.OptionKey: Any]()
style.tabStops = [NSTextTab(textAlignment: .left, location: indentation, options: nonOptions)]
style.defaultTabInterval = indentation
style.lineSpacing = lineSpacing
style.paragraphSpacing = paragraphSpacing
style.headIndent = indentation
return style
}
}
struct BulletString {
let string: String
let traits: UIFontDescriptorSymbolicTraits
}
extension UIFont {
func withTraits(traits:UIFontDescriptorSymbolicTraits...) -> UIFont {
let descriptor = self.fontDescriptor
.withSymbolicTraits(UIFontDescriptorSymbolicTraits(traits))!
return UIFont(descriptor: descriptor, size: 0)
}
}
If you wanted to have the bullets match the style of their strings, i.e. be bolded or italicised, you could just add the attributes in a single pass for each bullet
You can achieve it using multiple fonts and text ranges. If you know the ranges of the text on which you want to apply multiple styles, you can just use fonts. Check the below example.
let fullString = "Bold normal italic"
let attrString = NSMutableAttributedString(string: fullString, attributes: [.font: UIFont.systemFont(ofSize: 18.0)])
let range1 = (fullString as NSString).range(of: "Bold")
let range2 = (fullString as NSString).range(of: "italic")
attrString.addAttributes([.font: UIFont.boldSystemFont(ofSize: 20.0)], range: range1)
attrString.addAttributes([.font: UIFont.boldSystemFont(ofSize: 20.0).italics()], range: range2)
label.attributedText = attrString
Whereas I use simple extension for UIFont.
extension UIFont {
func withTraits(_ traits: UIFontDescriptorSymbolicTraits) -> UIFont {
if let fd = fontDescriptor.withSymbolicTraits(traits) {
return UIFont(descriptor: fd, size: pointSize)
}
return self
}
func italics() -> UIFont {
return withTraits(.traitItalic)
}
}
So basically, what you need to know is, which text should be marked as italic, bold and normal. Afterwards just calculate the ranges for those texts in your original text using NSString.range(of: ) and update the attributes appropriately.
Note: You can also calculate the range using start and endIndex. For reference check this SO answer.
One of the possible ways to make it work with the function mentioned in the question - to modify stringList parameter.
First of all let's define model class BulletString:
class BulletString {
var text: String
var attributes: [NSAttributedStringKey : Any]?
init(string: String) {
text = string
}
}
Now your bullet stringList in you function should be [BulletString] type. Define two bulletStrings and pass them to your function. Here is a working solution with your function:
let bulletString1 = BulletString.init(string: "string1")
bulletString1.attributes = [NSAttributedStringKey.font: UIFont.boldSystemFont(ofSize: 18.0)]
let bulletString2 = BulletString.init(string: "string2")
let bullets = [bulletString1, bulletString2]
label.attributedText = bulletPoints(stringList: bullets, font: UIFont.systemFont(ofSize: 17), bullet: "•", lineSpacing: 4, paragraphSpacing: 4, textColor: UIColor.darkGray, bulletColor: UIColor.darkGray)
label.textColor = .black
func bulletPoints(stringList: [BulletString], font: UIFont,bullet: String = "\u{2022}",indentation: CGFloat = 20,lineSpacing: CGFloat = 2,paragraphSpacing: CGFloat = 12,textColor: UIColor = .gray,bulletColor: UIColor = .red) -> NSAttributedString{
let textAttributes: [NSAttributedStringKey: Any] = [NSAttributedStringKey.font: font, NSAttributedStringKey.foregroundColor: textColor]
let bulletAttributes: [NSAttributedStringKey: Any] = [NSAttributedStringKey.font: font, NSAttributedStringKey.foregroundColor: bulletColor]
let paragraphStyle = NSMutableParagraphStyle()
let nonOptions = [NSTextTab.OptionKey: Any]()
paragraphStyle.tabStops = [NSTextTab(textAlignment: .left, location: indentation, options: nonOptions)]
paragraphStyle.defaultTabInterval = indentation
paragraphStyle.lineSpacing = lineSpacing
paragraphStyle.paragraphSpacing = paragraphSpacing
paragraphStyle.headIndent = indentation
let bulletList = NSMutableAttributedString()
for bulletString in stringList {
let formattedString = "\(bullet)\t\(bulletString.text)\n"
let attributedString = NSMutableAttributedString(string: formattedString)
attributedString.addAttributes(
[NSAttributedStringKey.paragraphStyle : paragraphStyle],
range: NSMakeRange(0, attributedString.length))
attributedString.addAttributes(
textAttributes,
range: NSMakeRange(0, attributedString.length))
// Here your custom attributes you provided in BulletString
if let attr = bulletString.attributes {
attributedString.addAttributes(attr, range: NSMakeRange(0, attributedString.length))
}
let string:NSString = NSString(string: formattedString)
let rangeForBullet:NSRange = string.range(of: bullet)
attributedString.addAttributes(bulletAttributes, range: rangeForBullet)
bulletList.append(attributedString)
}
return bulletList
}
Results
I've tried a couple of different methods and extensions after coming across them on S.O. to no avail. Is there a definitive way to bold only part of a UIButton.titleLabel?
These are some of the extensions I've tried:
func attributedText(fullStr: String, boldStr: String) -> NSAttributedString {
let attributedString = NSMutableAttributedString(string: fullStr as String, attributes: [NSFontAttributeName:UIFont.systemFontOfSize(12.0)])
let boldFontAttribute = [NSFontAttributeName: UIFont.boldSystemFontOfSize(12.0)]
// Part of string to be bold
attributedString.addAttributes(boldFontAttribute, range: NSMakeRange(0, boldStr.characters.count))
return attributedString
}
func boldRange(range: Range<String.Index>) {
if let text = self.attributedTitleForState(UIControlState.Normal) {
let attr = NSMutableAttributedString(attributedString: text)
let start = text.string.startIndex.distanceTo(range.startIndex)
let length = range.startIndex.distanceTo(range.endIndex)
attr.addAttributes([NSFontAttributeName: UIFont.boldSystemFontOfSize(16)], range: NSMakeRange(start, length))
self.setAttributedTitle(attr, forState: UIControlState.Normal)
}
}
func boldSubstring(substr: String) {
let range = substr.rangeOfString(substr)
if let r = range {
boldRange(r)
}
}
Anyone have anything?
Swift 4 version of chicobermuda's answer:
let text = "This is the"
let attr = NSMutableAttributedString(string: "\(text) button's text!")
attr.addAttribute(NSAttributedStringKey.font, value: UIFont.boldSystemFont(ofSize: 14), range: NSMakeRange(0, text.count))
cell.nameLabel.setAttributedTitle(attr, forState: .normal)
// "**This is the** button's text!"
it works fine
I have a string like this
var str = "#text1 this is good #text1"
Now replace text1 with another string, say t 1. I am able to replace the text, but i am not able to bold it. I want to bold the new string t 1, so that the final output will be:
#t 1 this is good #t 1
How can I do it?
All the examples I am seeing are in Objective-C, but I want to do it in Swift.
Usage:
let label = UILabel()
label.attributedText =
NSMutableAttributedString()
.bold("Address: ")
.normal(" Kathmandu, Nepal\n\n")
.orangeHighlight(" Email: ")
.blackHighlight(" prajeet.shrestha#gmail.com ")
.bold("\n\nCopyright: ")
.underlined(" All rights reserved. 2020.")
Result:
Here is a neat way to make a combination of bold and normal texts in a single label plus some other bonus methods.
Extension: Swift 5.*
extension NSMutableAttributedString {
var fontSize:CGFloat { return 14 }
var boldFont:UIFont { return UIFont(name: "AvenirNext-Bold", size: fontSize) ?? UIFont.boldSystemFont(ofSize: fontSize) }
var normalFont:UIFont { return UIFont(name: "AvenirNext-Regular", size: fontSize) ?? UIFont.systemFont(ofSize: fontSize)}
func bold(_ value:String) -> NSMutableAttributedString {
let attributes:[NSAttributedString.Key : Any] = [
.font : boldFont
]
self.append(NSAttributedString(string: value, attributes:attributes))
return self
}
func normal(_ value:String) -> NSMutableAttributedString {
let attributes:[NSAttributedString.Key : Any] = [
.font : normalFont,
]
self.append(NSAttributedString(string: value, attributes:attributes))
return self
}
/* Other styling methods */
func orangeHighlight(_ value:String) -> NSMutableAttributedString {
let attributes:[NSAttributedString.Key : Any] = [
.font : normalFont,
.foregroundColor : UIColor.white,
.backgroundColor : UIColor.orange
]
self.append(NSAttributedString(string: value, attributes:attributes))
return self
}
func blackHighlight(_ value:String) -> NSMutableAttributedString {
let attributes:[NSAttributedString.Key : Any] = [
.font : normalFont,
.foregroundColor : UIColor.white,
.backgroundColor : UIColor.black
]
self.append(NSAttributedString(string: value, attributes:attributes))
return self
}
func underlined(_ value:String) -> NSMutableAttributedString {
let attributes:[NSAttributedString.Key : Any] = [
.font : normalFont,
.underlineStyle : NSUnderlineStyle.single.rawValue
]
self.append(NSAttributedString(string: value, attributes:attributes))
return self
}
}
Note: If compiler is missing UIFont/UIColor, replace them with NSFont/NSColor.
var normalText = "Hi am normal"
var boldText = "And I am BOLD!"
var attributedString = NSMutableAttributedString(string:normalText)
var attrs = [NSAttributedString.Key.font : UIFont.boldSystemFont(ofSize: 15)]
var boldString = NSMutableAttributedString(string: boldText, attributes:attrs)
attributedString.append(boldString)
When you want to assign it to a label:
yourLabel.attributedText = attributedString
edit/update: Xcode 13.1 • Swift 5.5.1
If you know HTML and CSS you can use it to easily control the font style, color and size of your attributed string as follow:
DiscussionThe HTML importer should not be called from a background thread (that is, the options dictionary includes documentType with a value of html). It will try to synchronize with the main thread, fail, and time out. Calling it from the main thread works (but can still time out if the HTML contains references to external resources, which should be avoided at all costs). The HTML import mechanism is meant for implementing something like markdown (that is, text styles, colors, and so on), not for general HTML import.
extension StringProtocol {
var html2AttStr: NSAttributedString? {
try? NSAttributedString(data: Data(utf8), options: [.documentType: NSAttributedString.DocumentType.html, .characterEncoding: String.Encoding.utf8.rawValue], documentAttributes: nil)
}
}
"<style type=\"text/css\">#red{color:#F00}#green{color:#0F0}#blue{color: #00F; font-weight: Bold; font-size: 32}</style><span id=\"red\" >Red,</span><span id=\"green\" > Green </span><span id=\"blue\">and Blue</span>".html2AttStr
If you're working with localised strings, you might not be able to rely on the bold string always being at the end of the sentence. If this is the case then the following works well:
e.g. Query "blah" does not match any items
/* Create the search query part of the text, e.g. "blah".
The variable 'text' is just the value entered by the user. */
let searchQuery = "\"\(text)\""
/* Put the search text into the message */
let message = "Query \(searchQuery). does not match any items"
/* Find the position of the search string. Cast to NSString as we want
range to be of type NSRange, not Swift's Range<Index> */
let range = (message as NSString).rangeOfString(searchQuery)
/* Make the text at the given range bold. Rather than hard-coding a text size,
Use the text size configured in Interface Builder. */
let attributedString = NSMutableAttributedString(string: message)
attributedString.addAttribute(NSFontAttributeName, value: UIFont.boldSystemFontOfSize(label.font.pointSize), range: range)
/* Put the text in a label */
label.attributedText = attributedString
I extended David West's great answer so that you can input a string and tell it all the substrings you would like to embolden:
func addBoldText(fullString: NSString, boldPartsOfString: Array<NSString>, font: UIFont!, boldFont: UIFont!) -> NSAttributedString {
let nonBoldFontAttribute = [NSFontAttributeName:font!]
let boldFontAttribute = [NSFontAttributeName:boldFont!]
let boldString = NSMutableAttributedString(string: fullString as String, attributes:nonBoldFontAttribute)
for i in 0 ..< boldPartsOfString.count {
boldString.addAttributes(boldFontAttribute, range: fullString.rangeOfString(boldPartsOfString[i] as String))
}
return boldString
}
And then call it like this:
let normalFont = UIFont(name: "Dosis-Medium", size: 18)
let boldSearchFont = UIFont(name: "Dosis-Bold", size: 18)
self.UILabel.attributedText = addBoldText("Check again in 30 days to find more friends", boldPartsOfString: ["Check", "30 days", "find", "friends"], font: normalFont!, boldFont: boldSearchFont!)
This will embolden all the substrings you want bolded in your given string
This is the best way that I have come up with. Add a function you can call from anywhere and add it to a file without a class like Constants.swift and then you can embolden words within any string, on numerous occasions by calling just ONE LINE of code:
To go in a constants.swift file:
import Foundation
import UIKit
func addBoldText(fullString: NSString, boldPartOfString: NSString, font: UIFont!, boldFont: UIFont!) -> NSAttributedString {
let nonBoldFontAttribute = [NSFontAttributeName:font!]
let boldFontAttribute = [NSFontAttributeName:boldFont!]
let boldString = NSMutableAttributedString(string: fullString as String, attributes:nonBoldFontAttribute)
boldString.addAttributes(boldFontAttribute, range: fullString.rangeOfString(boldPartOfString as String))
return boldString
}
Then you can just call this one line of code for any UILabel:
self.UILabel.attributedText = addBoldText("Check again in 30 DAYS to find more friends", boldPartOfString: "30 DAYS", font: normalFont!, boldFont: boldSearchFont!)
//Mark: Albeit that you've had to define these somewhere:
let normalFont = UIFont(name: "INSERT FONT NAME", size: 15)
let boldFont = UIFont(name: "INSERT BOLD FONT", size: 15)
Building on Jeremy Bader and David West's excellent answers, a Swift 3 extension:
extension String {
func withBoldText(boldPartsOfString: Array<NSString>, font: UIFont!, boldFont: UIFont!) -> NSAttributedString {
let nonBoldFontAttribute = [NSFontAttributeName:font!]
let boldFontAttribute = [NSFontAttributeName:boldFont!]
let boldString = NSMutableAttributedString(string: self as String, attributes:nonBoldFontAttribute)
for i in 0 ..< boldPartsOfString.count {
boldString.addAttributes(boldFontAttribute, range: (self as NSString).range(of: boldPartsOfString[i] as String))
}
return boldString
}
}
Usage:
let label = UILabel()
let font = UIFont(name: "AvenirNext-Italic", size: 24)!
let boldFont = UIFont(name: "AvenirNext-BoldItalic", size: 24)!
label.attributedText = "Make sure your face is\nbrightly and evenly lit".withBoldText(
boldPartsOfString: ["brightly", "evenly"], font: font, boldFont: boldFont)
Swift 4 and higher
For Swift 4 and higher that is a good way:
let attributsBold = [NSAttributedString.Key.font : UIFont.systemFont(ofSize: 16, weight: .bold)]
let attributsNormal = [NSAttributedString.Key.font : UIFont.systemFont(ofSize: 16, weight: .regular)]
var attributedString = NSMutableAttributedString(string: "Hi ", attributes:attributsNormal)
let boldStringPart = NSMutableAttributedString(string: "John", attributes:attributsBold)
attributedString.append(boldStringPart)
yourLabel.attributedText = attributedString
In the Label the Text looks like: "Hi John"
usage....
let attrString = NSMutableAttributedString()
.appendWith(weight: .semibold, "almost bold")
.appendWith(color: .white, weight: .bold, " white and bold")
.appendWith(color: .black, ofSize: 18.0, " big black")
two cents...
extension NSMutableAttributedString {
#discardableResult func appendWith(color: UIColor = UIColor.darkText, weight: UIFont.Weight = .regular, ofSize: CGFloat = 12.0, _ text: String) -> NSMutableAttributedString{
let attrText = NSAttributedString.makeWith(color: color, weight: weight, ofSize:ofSize, text)
self.append(attrText)
return self
}
}
extension NSAttributedString {
public static func makeWith(color: UIColor = UIColor.darkText, weight: UIFont.Weight = .regular, ofSize: CGFloat = 12.0, _ text: String) -> NSMutableAttributedString {
let attrs = [NSAttributedStringKey.font: UIFont.systemFont(ofSize: ofSize, weight: weight), NSAttributedStringKey.foregroundColor: color]
return NSMutableAttributedString(string: text, attributes:attrs)
}
}
Accepting as valid the response of Prajeet Shrestha in this thread, I would like to extend his solution using the Label if it is known and the traits of the font.
Swift 4
extension NSMutableAttributedString {
#discardableResult func normal(_ text: String) -> NSMutableAttributedString {
let normal = NSAttributedString(string: text)
append(normal)
return self
}
#discardableResult func bold(_ text: String, withLabel label: UILabel) -> NSMutableAttributedString {
//generate the bold font
var font: UIFont = UIFont(name: label.font.fontName , size: label.font.pointSize)!
font = UIFont(descriptor: font.fontDescriptor.withSymbolicTraits(.traitBold) ?? font.fontDescriptor, size: font.pointSize)
//generate attributes
let attrs: [NSAttributedStringKey: Any] = [NSAttributedStringKey.font: font]
let boldString = NSMutableAttributedString(string:text, attributes: attrs)
//append the attributed text
append(boldString)
return self
}
}
Super easy way to do this.
let text = "This string is having multiple font"
let attributedText =
NSMutableAttributedString.getAttributedString(fromString: text)
attributedText.apply(font: UIFont.boldSystemFont(ofSize: 24), subString:
"This")
attributedText.apply(font: UIFont.boldSystemFont(ofSize: 24), onRange:
NSMakeRange(5, 6))
For more detail click here:
https://github.com/iOSTechHub/AttributedString
For -> Search Television by size
1-way using NString and its Range
let query = "Television"
let headerTitle = "size"
let message = "Search \(query) by \(headerTitle)"
let range = (message as NSString).range(of: query)
let attributedString = NSMutableAttributedString(string: message)
attributedString.addAttribute(NSAttributedString.Key.font, value: UIFont.boldSystemFont(ofSize: label1.font.pointSize), range: range)
label1.attributedText = attributedString
another without using NString and its Range
let query = "Television"
let headerTitle = "size"
let (searchText, byText) = ("Search ", " by \(headerTitle)")
let attributedString = NSMutableAttributedString(string: searchText)
let byTextAttributedString = NSMutableAttributedString(string: byText)
let attrs = [NSAttributedString.Key.font : UIFont.boldSystemFont(ofSize: label1.font.pointSize)]
let boldString = NSMutableAttributedString(string: query, attributes:attrs)
attributedString.append(boldString)
attributedString.append(byTextAttributedString)
label1.attributedText = attributedString
swift5
This could be useful
class func createAttributedStringFrom (string1 : String ,strin2 : String, attributes1 : Dictionary<String, NSObject>, attributes2 : Dictionary<String, NSObject>) -> NSAttributedString{
let fullStringNormal = (string1 + strin2) as NSString
let attributedFullString = NSMutableAttributedString(string: fullStringNormal as String)
attributedFullString.addAttributes(attributes1, range: fullStringNormal.rangeOfString(string1))
attributedFullString.addAttributes(attributes2, range: fullStringNormal.rangeOfString(strin2))
return attributedFullString
}
Swift 3.0
Convert html to string and font change as per your requirement.
do {
let str = try NSAttributedString(data: ("I'm a normal text and <b>this is my bold part . </b>And I'm again in the normal text".data(using: String.Encoding.unicode, allowLossyConversion: true)!), options: [ NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType], documentAttributes: nil)
myLabel.attributedText = str
myLabel.font = MONTSERRAT_BOLD(23)
myLabel.textAlignment = NSTextAlignment.left
} catch {
print(error)
}
func MONTSERRAT_BOLD(_ size: CGFloat) -> UIFont
{
return UIFont(name: "MONTSERRAT-BOLD", size: size)!
}
Swift 5.1
use NSAttributedString.Key instead of NSAttributedStringKey
let test1Attributes:[NSAttributedString.Key: Any] = [.font : UIFont(name: "CircularStd-Book", size: 14)!]
let test2Attributes:[NSAttributedString.Key: Any] = [.font : UIFont(name: "CircularStd-Bold", size: 16)!]
let test1 = NSAttributedString(string: "\(greeting!) ", attributes:test1Attributes)
let test2 = NSAttributedString(string: firstName!, attributes:test2Attributes)
let text = NSMutableAttributedString()
text.append(test1)
text.append(test2)
return text
for making mixed-type strings (Attributed String ) It is better to use Xcode's interface builder if the text is static.
it is very easy and convenient.
Just use code something like this:
let font = UIFont(name: "Your-Font-Name", size: 10.0)!
let attributedText = NSMutableAttributedString(attributedString: noteLabel.attributedText!)
let boldedRange = NSRange(attributedText.string.range(of: "Note:")!, in: attributedText.string)
attributedText.addAttributes([NSAttributedString.Key.font : font], range: boldedRange)
noteLabel.attributedText = attributedText
two liner in swift 4:
button.setAttributedTitle(.init(string: "My text", attributes: [.font: UIFont.systemFont(ofSize: 20, weight: .bold)]), for: .selected)
button.setAttributedTitle(.init(string: "My text", attributes: [.font: UIFont.systemFont(ofSize: 20, weight: .regular)]), for: .normal)
With recent versions (iOS 15+) you can use AttributedString to create Markdown strings :
let rawMarkdown = "This is **bold**"
let content;
do {
content = try AttributedString(markdown: rawMarkdown)
} catch {
content = AttributedString(rawMarkdown)
}
and display them with Swift UI's Text:
Text(content)
Improving upon Prajeet Shrestha answer : -
You can make a generic extension for NSMutableAttributedString which involves less code. In this case I have chosen to use system font but you could adapt it so you can input the font name as a parameter.
extension NSMutableAttributedString {
func systemFontWith(text: String, size: CGFloat, weight: CGFloat) -> NSMutableAttributedString {
let attributes: [String: AnyObject] = [NSFontAttributeName: UIFont.systemFont(ofSize: size, weight: weight)]
let string = NSMutableAttributedString(string: text, attributes: attributes)
self.append(string)
return self
}
}
You can do this using simple custom method written below.
You have give whole string in first parameter and text to be bold in the second parameter. Hope this will help.
func getAttributedBoldString(str : String, boldTxt : String) -> NSMutableAttributedString {
let attrStr = NSMutableAttributedString.init(string: str)
let boldedRange = NSRange(str.range(of: boldTxt)!, in: str)
attrStr.addAttributes([NSAttributedString.Key.font : UIFont.systemFont(ofSize: 17, weight: .bold)], range: boldedRange)
return attrStr
}
usage:
initalString = I am a Boy
label.attributedText = getAttributedBoldString(str : initalString, boldTxt : "Boy")
resultant string = I am a Boy
How to underline a UILabel in Swift? I searched the Objective-C ones but couldn't quite get them to work in Swift.
You can do this using NSAttributedString
Example:
let underlineAttribute = [NSAttributedString.Key.underlineStyle: NSUnderlineStyle.thick.rawValue]
let underlineAttributedString = NSAttributedString(string: "StringWithUnderLine", attributes: underlineAttribute)
myLabel.attributedText = underlineAttributedString
EDIT
To have the same attributes for all texts of one UILabel, I suggest you to subclass UILabel and overriding text, like that:
Swift 5
Same as Swift 4.2 but: You should prefer the Swift initializer NSRange over the old NSMakeRange, you can shorten to .underlineStyle and linebreaks improve readibility for long method calls.
class UnderlinedLabel: UILabel {
override var text: String? {
didSet {
guard let text = text else { return }
let textRange = NSRange(location: 0, length: text.count)
let attributedText = NSMutableAttributedString(string: text)
attributedText.addAttribute(.underlineStyle,
value: NSUnderlineStyle.single.rawValue,
range: textRange)
// Add other attributes if needed
self.attributedText = attributedText
}
}
}
Swift 4.2
class UnderlinedLabel: UILabel {
override var text: String? {
didSet {
guard let text = text else { return }
let textRange = NSMakeRange(0, text.count)
let attributedText = NSMutableAttributedString(string: text)
attributedText.addAttribute(NSAttributedString.Key.underlineStyle , value: NSUnderlineStyle.single.rawValue, range: textRange)
// Add other attributes if needed
self.attributedText = attributedText
}
}
}
Swift 3.0
class UnderlinedLabel: UILabel {
override var text: String? {
didSet {
guard let text = text else { return }
let textRange = NSMakeRange(0, text.characters.count)
let attributedText = NSMutableAttributedString(string: text)
attributedText.addAttribute(NSUnderlineStyleAttributeName , value: NSUnderlineStyle.styleSingle.rawValue, range: textRange)
// Add other attributes if needed
self.attributedText = attributedText
}
}
}
And you put your text like this :
#IBOutlet weak var label: UnderlinedLabel!
override func viewDidLoad() {
super.viewDidLoad()
label.text = "StringWithUnderLine"
}
OLD:
Swift (2.0 to 2.3):
class UnderlinedLabel: UILabel {
override var text: String? {
didSet {
guard let text = text else { return }
let textRange = NSMakeRange(0, text.characters.count)
let attributedText = NSMutableAttributedString(string: text)
attributedText.addAttribute(NSUnderlineStyleAttributeName, value:NSUnderlineStyle.StyleSingle.rawValue, range: textRange)
// Add other attributes if needed
self.attributedText = attributedText
}
}
}
Swift 1.2:
class UnderlinedLabel: UILabel {
override var text: String! {
didSet {
let textRange = NSMakeRange(0, count(text))
let attributedText = NSMutableAttributedString(string: text)
attributedText.addAttribute(NSUnderlineStyleAttributeName, value:NSUnderlineStyle.StyleSingle.rawValue, range: textRange)
// Add other attributes if needed
self.attributedText = attributedText
}
}
}
Swift 5 & 4.2 one liner:
label.attributedText = NSAttributedString(string: "Text", attributes:
[.underlineStyle: NSUnderlineStyle.single.rawValue])
Swift 4 one liner:
label.attributedText = NSAttributedString(string: "Text", attributes:
[.underlineStyle: NSUnderlineStyle.styleSingle.rawValue])
Swift 3 one liner:
label.attributedText = NSAttributedString(string: "Text", attributes:
[NSUnderlineStyleAttributeName: NSUnderlineStyle.styleSingle.rawValue])
Swift 5:
1- Create a String extension to get attributedText
extension String {
var underLined: NSAttributedString {
NSMutableAttributedString(string: self, attributes: [.underlineStyle: NSUnderlineStyle.single.rawValue])
}
}
2- Use it
On buttons:
<#YourButton#>.setAttributedTitle(<#YourButtonTitle#>.underLined, for: .normal)
On Labels:
<#YourLabel#>.attributedText = <#YourLabelTitle#>.underLined
Or Stoyboard version
If you are looking for a way to do this without inheritance:
Swift 5
extension UILabel {
func underline() {
if let textString = self.text {
let attributedString = NSMutableAttributedString(string: textString)
attributedString.addAttribute(NSAttributedString.Key.underlineStyle,
value: NSUnderlineStyle.single.rawValue,
range: NSRange(location: 0, length: attributedString.length))
attributedText = attributedString
}
}
}
Swift 3/4
// in swift 4 - switch NSUnderlineStyleAttributeName with NSAttributedStringKey.underlineStyle
extension UILabel {
func underline() {
if let textString = self.text {
let attributedString = NSMutableAttributedString(string: textString)
attributedString.addAttribute(NSUnderlineStyleAttributeName, value: NSUnderlineStyle.styleSingle.rawValue, range: NSRange(location: 0, length: attributedString.length))
attributedText = attributedString
}
}
}
extension UIButton {
func underline() {
let attributedString = NSMutableAttributedString(string: (self.titleLabel?.text!)!)
attributedString.addAttribute(NSUnderlineStyleAttributeName, value: NSUnderlineStyle.styleSingle.rawValue, range: NSRange(location: 0, length: (self.titleLabel?.text!.characters.count)!))
self.setAttributedTitle(attributedString, for: .normal)
}
}
Just a little fix for the Shlome answer in Swift 4 and Xcode 9.
extension UILabel {
func underline() {
if let textString = self.text {
let attributedString = NSMutableAttributedString(string: textString)
attributedString.addAttribute(NSAttributedStringKey.underlineStyle,
value: NSUnderlineStyle.styleSingle.rawValue,
range: NSRange(location: 0, length: attributedString.length - 1))
attributedText = attributedString
}
}
}
extension UIButton {
func underline() {
let attributedString = NSMutableAttributedString(string: (self.titleLabel?.text!)!)
attributedString.addAttribute(NSAttributedStringKey.underlineStyle,
value: NSUnderlineStyle.styleSingle.rawValue,
range: NSRange(location: 0, length: (self.titleLabel?.text!.count)!))
self.setAttributedTitle(attributedString, for: .normal)
}
}
You can underline the UILabel text using Interface Builder.
Here is the link of my answer : Adding underline attribute to partial text UILabel in storyboard
Same Answer in Swift 4.2
For UILable
extension UILabel {
func underline() {
if let textString = self.text {
let attributedString = NSMutableAttributedString(string: textString)
attributedString.addAttribute(NSAttributedString.Key.underlineStyle,
value: NSUnderlineStyle.single.rawValue,
range: NSRange(location: 0, length: textString.count))
self.attributedText = attributedString
}
}
}
Call for UILabel like below
myLable.underline()
For UIButton
extension UIButton {
func underline() {
if let textString = self.titleLabel?.text {
let attributedString = NSMutableAttributedString(string: textString)
attributedString.addAttribute(NSAttributedString.Key.underlineStyle,
value: NSUnderlineStyle.single.rawValue,
range: NSRange(location: 0, length: textString.count))
self.setAttributedTitle(attributedString, for: .normal)
}
}
}
Call for UIButton like below
myButton.underline()
I looked into above answers and some of them are force unwrapping text value. I will suggest to get value by safely unwrapping. This will avoid crash in case of nil value.
Hope This helps :)
Swift 4, 4.2 and 5.
#IBOutlet weak var lblUnderLine: UILabel!
I need to underline particular text in UILabel. So, find range and set attributes.
let strSignup = "Don't have account? SIGNUP NOW."
let rangeSignUp = NSString(string: strSignup).range(of: "SIGNUP NOW.", options: String.CompareOptions.caseInsensitive)
let rangeFull = NSString(string: strSignup).range(of: strSignup, options: String.CompareOptions.caseInsensitive)
let attrStr = NSMutableAttributedString.init(string:strSignup)
attrStr.addAttributes([NSAttributedString.Key.foregroundColor : UIColor.white,
NSAttributedString.Key.font : UIFont.init(name: "Helvetica", size: 17)! as Any],range: rangeFull)
attrStr.addAttributes([NSAttributedString.Key.foregroundColor : UIColor.white,
NSAttributedString.Key.font : UIFont.init(name: "Helvetica", size: 20)!,
NSAttributedString.Key.underlineStyle: NSUnderlineStyle.thick.rawValue as Any],range: rangeSignUp) // for swift 4 -> Change thick to styleThick
lblUnderLine.attributedText = attrStr
Output
Underline to multiple strings in a sentence.
extension UILabel {
func underlineMyText(range1:String, range2:String) {
if let textString = self.text {
let str = NSString(string: textString)
let firstRange = str.range(of: range1)
let secRange = str.range(of: range2)
let attributedString = NSMutableAttributedString(string: textString)
attributedString.addAttribute(NSAttributedString.Key.underlineStyle, value: NSUnderlineStyle.single.rawValue, range: firstRange)
attributedString.addAttribute(NSAttributedString.Key.underlineStyle, value: NSUnderlineStyle.single.rawValue, range: secRange)
attributedText = attributedString
}
}
}
Use by this way.
lbl.text = "By continuing you agree to our Terms of Service and Privacy Policy."
lbl.underlineMyText(range1: "Terms of Service", range2: "Privacy Policy.")
Swift 4 changes.
Remeber to use NSUnderlineStyle.styleSingle.rawValue instead of NSUnderlineStyle.styleSingle.
'let attributedString = NSAttributedString(string: "Testing")
let textRange = NSMakeRange(0, attributedString.length)
let underlinedMessage = NSMutableAttributedString(attributedString: attributedString)
underlinedMessage.addAttribute(NSAttributedStringKey.underlineStyle,
value:NSUnderlineStyle.styleSingle.rawValue,
range: textRange)
label.attributedText = underlinedMessage
`
You can use this also if you want to achieve only half part of label as underline:- //For Swift 4.0+
let attributesForUnderLine: [NSAttributedString.Key: Any] = [
.font: UIFont(name: AppFont.sourceSansPro_Regular, size: 12) ?? UIFont.systemFont(ofSize: 11),
.foregroundColor: UIColor.blue,
.underlineStyle: NSUnderlineStyle.single.rawValue]
let attributesForNormalText: [NSAttributedString.Key: Any] = [
.font: UIFont(name: AppFont.sourceSansPro_Regular, size: 12) ?? UIFont.systemFont(ofSize: 11),
.foregroundColor: AppColors.ColorText_787878]
let textToSet = "Want to change your preferences? Edit Now"
let rangeOfUnderLine = (textToSet as NSString).range(of: "Edit Now")
let rangeOfNormalText = (textToSet as NSString).range(of: "Want to change your preferences?")
let attributedText = NSMutableAttributedString(string: textToSet)
attributedText.addAttributes(attributesForUnderLine, range: rangeOfUnderLine)
attributedText.addAttributes(attributesForNormalText, range: rangeOfNormalText)
yourLabel.attributedText = attributedText
The answer above is causing an error in my build environment.
This doesn't work in Swift 4.0:
attributedText.addAttribute(NSUnderlineStyleAttributeName,
value: NSUnderlineStyle.styleSingle.rawValue,
range: textRange)
Try this instead:
attributedText.addAttribute(NSAttributedStringKey.underlineStyle,
value: NSUnderlineStyle.styleSingle.rawValue,
range: textRange)
hope this helps someone.
// Swift 4 Version
let attributedString = NSMutableAttributedString(string: "Your Text Here", attributes: [NSAttributedStringKey.underlineStyle : true])
self.yourlabel.attributedText = attributedString
A class to set and remove underline for UIbuttons for Swift 5. I hope this helps
import Foundation
import UIKit
class UiUtil {
static let underlineThickness = 2
class func removeUnderlineFromButton( _ button:UIButton ) {
if let str = button.titleLabel?.attributedText {
let attributedString = NSMutableAttributedString( attributedString: str )
attributedString.removeAttribute(.underlineStyle, range:
NSRange.init(location: 0, length: attributedString.length))
button.setAttributedTitle(attributedString, for: .normal)
}
}
class func setUnderlineFromButton( _ button:UIButton ) {
if let str = button.titleLabel?.attributedText {
let attributedStringUnderline = NSMutableAttributedString( attributedString:
str )
attributedStringUnderline.addAttribute(
NSAttributedString.Key.underlineStyle,
value: underlineThickness,
range: NSRange.init(location: 0, length: attributedStringUnderline.length)
)
button.setAttributedTitle(attributedStringUnderline, for: .normal)
}
}
}
I have algorithm that used in my app. In this algorithm you can underline substring even that have space between words
extension NSMutableAttributedString{
static func findSubStringAndUnderlineIt(subStringToBeFound : String,totalString : String)-> NSMutableAttributedString?{
let attributedString = NSMutableAttributedString(string: totalString)
var spaceCount = 0
if subStringToBeFound.contains(" "){
spaceCount = subStringToBeFound.components(separatedBy:" ").count-1
}
if let range = attributedString.string.range(of: subStringToBeFound, options: .caseInsensitive){
attributedString.addAttribute(NSAttributedString.Key.underlineStyle, value: NSUnderlineStyle.single.rawValue, range: NSMakeRange((range.lowerBound.utf16Offset(in: subStringToBeFound)) ,(range.upperBound.utf16Offset(in: subStringToBeFound)) +
spaceCount))
return attributedString
}
return attributedString
}
}
in used section
lblWarning.attributedText = NSMutableAttributedString.findSubStringAndUnderlineIt(subStringToBeFound:"Not: Sadece uygulamanın reklamları kaldırılacaktır.", totalString: lblWarning.text!)
For Swift 2.3
extension UIButton {
func underline() {
let attributedString = NSMutableAttributedString(string: (self.titleLabel?.text!)!)
attributedString.addAttribute(NSUnderlineStyleAttributeName, value: NSUnderlineStyle.StyleSingle.rawValue, range: NSRange(location: 0, length: (self.titleLabel?.text!.characters.count)!))
self.setAttributedTitle(attributedString, forState: .Normal)
}
}
and in ViewController
#IBOutlet var yourButton: UIButton!
in ViewDidLoad Method or in your function just write
yourButton.underline()
it will underline the title of your button