I'm working with a TextView and I wanted to create two different links within a text where the user accepts the terms and conditions and the privacy policy.
Also I need each link to open a different UIViewController.
Can anyone help me with an example to understand how to achieve this?
I need to understand how to create two Hyper links and how to open them in two different ViewControllers
Thank you all for any help you can give me
For example ... I would like to get a TextView similar to this
You can use the following UITextView delegate Method and Attributed string Tested on swift 5.1 :
let attributedString = NSMutableAttributedString(string: "By continueing you agree terms and conditions and the privacy policy")
attributedString.addAttribute(.link, value: "terms://termsofCondition", range: (attributedString.string as NSString).range(of: "terms and conditions"))
attributedString.addAttribute(.link, value: "privacy://privacypolicy", range: (attributedString.string as NSString).range(of: "privacy policy"))
textView.linkTextAttributes = [ NSAttributedString.Key.foregroundColor: UIColor.blue]
textView.attributedText = attributedString
textView.delegate = self
textView.isSelectable = true
textView.isEditable = false
textView.delaysContentTouches = false
textView.isScrollEnabled = false
func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
if URL.scheme == "terms" {
//push view controller 1
return false
} else if URL.scheme == "privacy"{
// pushViewcontroller 2
return false
}
return true
// let the system open this URL
}
The UITextView call this function if the user taps or longPresses the URL link. Implementation of this method is optional. By default, the UITextview opens those applications which are responsible for handling the URL type and pass them the URL. You can use this method to trigger an alternative action
Set your textView properties like this.
textView.attributedText = "By Continuing, you aggree to terms <a href='http://termsandservicelink'>Terms Of Services</a> and <a href='https://privacypolicylink'>Privacy Policy</a>".convertHtml()
textView.isEditable = false
textView.dataDetectorTypes = [.link]
textView.linkTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.blue, NSAttributedString.Key.underlineColor: UIColor.clear]
You can handle tap event on your link in this delegate.
func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
//Check your url whether it is privacy policy or terms and do accordigly
return true
}
Here is String extension.
extension String{
func convertHtml() -> NSAttributedString{
guard let data = data(using: .utf8) else { return NSAttributedString() }
do{
return try NSAttributedString(data: data, options: [NSAttributedString.DocumentReadingOptionKey.documentType : NSAttributedString.DocumentType.html, NSAttributedString.DocumentReadingOptionKey.characterEncoding: String.Encoding.utf8.rawValue], documentAttributes: nil)
}catch{
return NSAttributedString()
}
}
}
This result is reached using NSAttributedString, Using NSAttributedString, we can style the text,
myLabel.text = "By signing up you agree to our Terms & Conditions and Privacy Policy"
let text = (myLabel.text)!
let underlineAttriString = NSMutableAttributedString(string: text)
let range1 = (text as NSString).rangeOfString("Terms & Conditions")
underlineAttriString.addAttribute(NSUnderlineStyleAttributeName, value: NSUnderlineStyle.StyleSingle.rawValue, range: range1)
let range2 = (text as NSString).rangeOfString("Privacy Policy")
underlineAttriString.addAttribute(NSUnderlineStyleAttributeName, value: NSUnderlineStyle.StyleSingle.rawValue, range: range2)
myLabel.attributedText = underlineAttriString
Extend UITapGestureRecognizer to provide a convenient function to detect if a certain range (NSRange) is tapped on in a UILabel.
extension UITapGestureRecognizer {
func didTapAttributedTextInLabel(label: UILabel, inRange targetRange: NSRange) -> Bool {
// Create instances of NSLayoutManager, NSTextContainer and NSTextStorage
let layoutManager = NSLayoutManager()
let textContainer = NSTextContainer(size: CGSize.zero)
let textStorage = NSTextStorage(attributedString: label.attributedText!)
// Configure layoutManager and textStorage
layoutManager.addTextContainer(textContainer)
textStorage.addLayoutManager(layoutManager)
// Configure textContainer
textContainer.lineFragmentPadding = 0.0
textContainer.lineBreakMode = label.lineBreakMode
textContainer.maximumNumberOfLines = label.numberOfLines
let labelSize = label.bounds.size
textContainer.size = labelSize
// Find the tapped character location and compare it to the specified range
let locationOfTouchInLabel = self.locationInView(label)
let textBoundingBox = layoutManager.usedRectForTextContainer(textContainer)
let textContainerOffset = CGPointMake((labelSize.width - textBoundingBox.size.width) * 0.5 - textBoundingBox.origin.x,
(labelSize.height - textBoundingBox.size.height) * 0.5 - textBoundingBox.origin.y);
let locationOfTouchInTextContainer = CGPointMake(locationOfTouchInLabel.x - textContainerOffset.x,
locationOfTouchInLabel.y - textContainerOffset.y);
let indexOfCharacter = layoutManager.characterIndexForPoint(locationOfTouchInTextContainer, inTextContainer: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
return NSLocationInRange(indexOfCharacter, targetRange)
}
}
UITapGestureRecognizer send action to tapLabel:, and detect using the extension method didTapAttributedTextInLabel:inRange:.
#IBAction func tapLabel(gesture: UITapGestureRecognizer) {
let text = (myLabel.text)!
let termsRange = (text as NSString).rangeOfString("Terms & Conditions")
let privacyRange = (text as NSString).rangeOfString("Privacy Policy")
if gesture.didTapAttributedTextInLabel(myLabel, inRange: termsRange) {
print("Tapped terms")
} else if gesture.didTapAttributedTextInLabel(myLabel, inRange: privacyRange)
{
print("Tapped privacy")
} else {
print("Tapped none")
}
}
I am developing a social app in which I need show post and sharing etc.
I also need to show tag friend.
When post is show on list UI is also similar to Facebook post. Please check below screen shot.
As you can see Clicking Post title is as attributed single line, Where I need to open User Profile when user click on his name that is Person Name Abc and want to open Page when user click on Buzzfeed india,
As we need to use a UILabel to display content like this I am not able to get click on particular word. I have already tried https://github.com/null09264/FRHyperLabel but when there is multiple line Then clicking on word is not perfect.
You can use TTTAttributedLabel to make a particular part or words tappable in a UILabel.
For example
label.text = #"Fork me on GitHub!"; // Repository URL will be automatically detected and linked
NSRange range = [label.text rangeOfString:#"me"];
[label addLinkToURL:[NSURL URLWithString:#"meUrl"] withRange:range];
Here me becomes tappable and on tapping that word delegate method didSelectLinkWithURL gets called and you can check for that link inside that function
(void)attributedLabel:(TTTAttributedLabel *)label didSelectLinkWithURL:(NSURL *)url {
NSLog(#"link %#", [url absoluteString]);
if ([url absoluteString] == #"meUrl") {
//implement your logic here
}
NSLog(#"whole label %#", label);
}
Try using this:
Add Extension
extension UITapGestureRecognizer {
func didTapAttributedTextInLabel(label: UILabel, inRange targetRange: NSRange) -> Bool {
// Create instances of NSLayoutManager, NSTextContainer and NSTextStorage
let layoutManager = NSLayoutManager()
let textContainer = NSTextContainer(size: CGSize.zero)
let textStorage = NSTextStorage(attributedString: label.attributedText!)
// Configure layoutManager and textStorage
layoutManager.addTextContainer(textContainer)
textStorage.addLayoutManager(layoutManager)
// Configure textContainer
textContainer.lineFragmentPadding = 0.0
textContainer.lineBreakMode = label.lineBreakMode
textContainer.maximumNumberOfLines = label.numberOfLines
let labelSize = label.bounds.size
textContainer.size = labelSize
// Find the tapped character location and compare it to the specified range
let locationOfTouchInLabel = self.location(in: label)
let textBoundingBox = layoutManager.usedRect(for: textContainer)
let textContainerOffset = CGPoint(x:(labelSize.width - textBoundingBox.size.width) * 0.5 - textBoundingBox.origin.x,y:(labelSize.height - textBoundingBox.size.height) * 0.5 - textBoundingBox.origin.y);
let locationOfTouchInTextContainer = CGPoint(x:locationOfTouchInLabel.x - textContainerOffset.x,y:locationOfTouchInLabel.y - textContainerOffset.y);
let indexOfCharacter = layoutManager.characterIndex(for: locationOfTouchInTextContainer, in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
return NSLocationInRange(indexOfCharacter, targetRange)
}
}
Then Add UITapGestureRecognizer on your UILable. Please make sure UILabel.isUserInteractionEnabled is true
override func viewDidLoad() {
super.viewDidLoad()
initialSetup()
}
func initialSetup(){
let tap = UITapGestureRecognizer(target: self, action: #selector(self.didTap(_:)))
lblTitle.addGestureRecognizer(tap)
lblTitle.isUserInteractionEnabled = true
lblTitle.numberOfLines = 0
let personname = "Person Name Abc"
let buzzIndia = "Buzzfeed india"
let str = "\(personname) shared \(buzzIndia)'s video"
let attributedString = NSMutableAttributedString(string:str)
let range = (str as NSString).range(of: personname)
attributedString.addAttribute(NSForegroundColorAttributeName, value: UIColor.black, range: range)
if let font = UIFont(name: "Helvetica Bold", size: 14) {
attributedString.addAttribute(NSFontAttributeName, value: font, range: range)
}
let rangeBuzz = (str as NSString).range(of: buzzIndia)
attributedString.addAttribute(NSForegroundColorAttributeName, value: UIColor.black, range: rangeBuzz)
if let font = UIFont(name: "Helvetica Bold", size: 14) {
attributedString.addAttribute(NSFontAttributeName, value: font, range: rangeBuzz)
}
lblTitle.attributedText = attributedString
}
func didTap(_ gesture: UITapGestureRecognizer) {
let text = (lblTitle.text)!
let name = (text as NSString).range(of: "Person Name Abc")
let buzzfeed = (text as NSString).range(of: "Buzzfeed india")
if gesture.didTapAttributedTextInLabel(label: lblTitle, inRange: name) {
//Repective "Person Name Abc" action
print("Person Name Abc")
}else if gesture.didTapAttributedTextInLabel(label: lblTitle, inRange: buzzfeed) {
//Repective "Buzzfeed india" action
print("Buzzfeed india")
}else {
print("other tapped")
}
}
Reference: http://samwize.com/2016/03/04/how-to-create-multiple-tappable-links-in-a-uilabel/
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 am trying to display an attributed string in a UITextview with clickable links. I've created a simple test project to see where I'm going wrong and still can't figure it out. I've tried enabling user interaction and setting the shouldInteractWithURLs delegate method, but it's still not working. Here's my code (for a view controller that only contains a textview)
#IBOutlet weak var textView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let string = "Google"
let linkString = NSMutableAttributedString(string: string)
linkString.addAttribute(NSLinkAttributeName, value: NSURL(string: "https://www.google.com")!, range: NSMakeRange(0, string.characters.count))
linkString.addAttribute(NSFontAttributeName, value: UIFont(name: "HelveticaNeue", size: 25.0)!, range: NSMakeRange(0, string.characters.count))
textView.attributedText = linkString
textView.delegate = self
textView.selectable = true
textView.userInteractionEnabled = true
}
And here are the delegate methods I've implemented:
func textViewShouldBeginEditing(textView: UITextView) -> Bool {
return false
}
func textView(textView: UITextView, shouldInteractWithURL URL: NSURL, inRange characterRange: NSRange) -> Bool {
return true
}
This still isn't working. I've searched on this topic and nothing has helped yet. Thanks so much in advance.
Just select the UITextView in your storyboard and go to "Show Attributes inspector" and select selectable and links. As the image below shows. Make sure Editable is unchecked.
For swift3.0
override func viewDidLoad() {
super.viewDidLoad()
let linkAttributes = [
NSLinkAttributeName: NSURL(string: "http://stalwartitsolution.co.in/luminutri_flow/terms-condition")!
] as [String : Any]
let attributedString = NSMutableAttributedString(string: "Please tick box to confirm you agree to our Terms & Conditions, Privacy Policy, Disclaimer. ")
attributedString.setAttributes(linkAttributes, range: NSMakeRange(44, 18))
attributedString.addAttribute(NSUnderlineStyleAttributeName, value: NSNumber(value: 1), range: NSMakeRange(44, 18))
textview.delegate = self
textview.attributedText = attributedString
textview.linkTextAttributes = [NSForegroundColorAttributeName: UIColor.red]
textview.textColor = UIColor.white
}
func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
return true
}
Swift 3 iOS 10: Here's Clickable extended UITextView that detect websites inside the textview automatically as long as the link start with www. for example: www.exmaple.com if it exist anywhere in the text will be clickable. Here's the class:
import Foundation
import UIKit
public class ClickableTextView:UITextView{
var tap:UITapGestureRecognizer!
override public init(frame: CGRect, textContainer: NSTextContainer?) {
super.init(frame: frame, textContainer: textContainer)
print("init")
setup()
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
func setup(){
// Add tap gesture recognizer to Text View
tap = UITapGestureRecognizer(target: self, action: #selector(self.myMethodToHandleTap(sender:)))
// tap.delegate = self
self.addGestureRecognizer(tap)
}
func myMethodToHandleTap(sender: UITapGestureRecognizer){
let myTextView = sender.view as! UITextView
let layoutManager = myTextView.layoutManager
// location of tap in myTextView coordinates and taking the inset into account
var location = sender.location(in: myTextView)
location.x -= myTextView.textContainerInset.left;
location.y -= myTextView.textContainerInset.top;
// character index at tap location
let characterIndex = layoutManager.characterIndex(for: location, in: myTextView.textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
// if index is valid then do something.
if characterIndex < myTextView.textStorage.length {
let orgString = myTextView.attributedText.string
//Find the WWW
var didFind = false
var count:Int = characterIndex
while(count > 2 && didFind == false){
let myRange = NSRange(location: count-1, length: 2)
let substring = (orgString as NSString).substring(with: myRange)
// print(substring,count)
if substring == " w" || (substring == "w." && count == 3){
didFind = true
// print("Did find",count)
var count2 = count
while(count2 < orgString.characters.count){
let myRange = NSRange(location: count2 - 1, length: 2)
let substring = (orgString as NSString).substring(with: myRange)
// print("Did 2",count2,substring)
count2 += 1
//If it was at the end of textView
if count2 == orgString.characters.count {
let length = orgString.characters.count - count
let myRange = NSRange(location: count, length: length)
let substring = (orgString as NSString).substring(with: myRange)
openLink(link: substring)
print("It's a Link",substring)
return
}
//If it's in the middle
if substring.hasSuffix(" "){
let length = count2 - count
let myRange = NSRange(location: count, length: length)
let substring = (orgString as NSString).substring(with: myRange)
openLink(link: substring)
print("It's a Link",substring)
return
}
}
return
}
if substring.hasPrefix(" "){
print("Not a link")
return
}
count -= 1
}
}
}
func openLink(link:String){
if let checkURL = URL(string: "http://\(link.replacingOccurrences(of: " ", with: ""))") {
if UIApplication.shared.canOpenURL(checkURL) {
UIApplication.shared.open(checkURL, options: [:], completionHandler: nil)
print("url successfully opened")
}
} else {
print("invalid url")
}
}
public override func didMoveToWindow() {
if self.window == nil{
self.removeGestureRecognizer(tap)
print("ClickableTextView View removed from")
}
}
}
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