Swift 3 NSAttributedString multiple attributes - ios

just started swift 3 and I have problems with swift syntax.
i'm trying to display a simple NSAttributedString.
so 1st I set my attributes :
let attributeFontSaySomething : [String : AnyObject] = [NSFontAttributeName : UIFont.fontSaySomething()]
let attributeColorSaySomething : [String : AnyObject] = [NSForegroundColorAttributeName : UIColor.blue]
Then I create my string :
let attStringSaySomething = NSAttributedString(string: "Say something", attributes: self.attributeFontSaySomething)
What i would like to do is to create the string with my 2 attributes not only just one. But when i do :
let attStringSaySomething = NSAttributedString(string: "Say something", attributes: [self.attributeFontSaySomething, self.attributeColorSaySomething])
Xcode tells me I can't and want me to change this for a literal dictionary.
How can I create my string with the 2 attributes without using a NSMutableAttributedString ?

The main issue is that you are passing an array [attr.. , attr...] rather than one dictionary.
You need to merge the two dictionaries into one
let attributeFontSaySomething : [String : Any] = [NSFontAttributeName : UIFont.systemFont(ofSize: 12.0)]
let attributeColorSaySomething : [String : Any] = [NSForegroundColorAttributeName : UIColor.blue]
var attributes = attributeFontSaySomething
for (key, value) in attributeColorSaySomething {
attributes(value, forKey: key)
}
let attStringSaySomething = NSAttributedString(string: "Say something", attributes: attributes)
However it might be easier to create the dictionary literally:
let attributes : [String : Any] = [NSFontAttributeName : UIFont.systemFont(ofSize: 12.0), NSForegroundColorAttributeName : UIColor.blue]

Just create a single dictionary with both sets of attributes:
let attributes: [String:AnyObject] =
[NSFontAttributeName : UIFont.fontSaySomething(),
NSForegroundColorAttributeName : UIColor.blue]
And then use the dictionary with both key/value pairs when creating your attributed string.
There's no built-in mechanism in Swift for combining dictionaries, but you could add an override of the + operator if you wanted to be able to add dictionaries together (You'd have to work out what to do if both dictionaries contained the same key however.)

Use like this:
let attStringSaySomething = NSAttributedString.init(string: "Hello", attributes: [NSFontAttributeName: UIFont.systemFont(ofSize: 16), NSForegroundColorAttributeName:UIColor.black])

You can use this code for different attributes on different strings With Roboto font (For Roboto font use MDFRobotoFontLoader)
let yourAttributes = [NSForegroundColorAttributeName: UIColor.black, NSFontAttributeName: MDFRobotoFontLoader.sharedInstance().regularFont(ofSize: 20)]
let finalString = NSMutableAttributedString(string: "", attributes: yourAttributes)
let attributeStr = NSMutableAttributedString(string: "XYZGFDGii", attributes: yourAttributes)
finalString.append(attributeStr)
let yourOtherAttributes = [NSForegroundColorAttributeName: UIColor.red, NSFontAttributeName: MDFRobotoFontLoader.sharedInstance().regularFont(ofSize: 24)]
let partTwo = NSMutableAttributedString(string: "hfjghlkdhkjld", attributes: yourOtherAttributes)
finalString.append(partTwo)
This example uses Roboto font

Thanks for #vadian's answer
Update For Swift 4
let attributes : [NSAttributedStringKey : Any] = [NSAttributedStringKey(rawValue: NSAttributedStringKey.font.rawValue) : UIFont.systemFont(ofSize: 12.0), NSAttributedStringKey(rawValue: NSAttributedStringKey.foregroundColor.rawValue) : UIColor(hex:"4C0000")]
refreshControl.attributedTitle=NSAttributedString(string: "Refreshing...", attributes: attributes)

The problem is that you are inserting two dictionaries into a dictionary, what only expects [String: Any], not [[String: Any], [String: Any]] type.
You can do the following:
let attStringSaySomething = NSAttributedString(string: "Say something", attributes: [NSFontAttributeName : UIFont.fontSaySomething(), NSForegroundColorAttributeName : UIColor.blue])
You could also group the values into tuples instead of dictionaries, and insert them into your dictionary based on the key and value:
let attributeFontSaySomething = (key: NSFontAttributeName, value: UIFont.fontSaySomething())
let attributeColorSaySomething = (key: NSForegroundColorAttributeName, value: UIColor.blue)
let attStringSaySomething = NSAttributedString(string: "Say something", attributes: [attributeFontSaySomething.key : attributeFontSaySomething.value,attributeColorSaySomething.key : attributeColorSaySomething.value])

Some of the answers are out dated here, especially for Swift 4 and above, you can use something like:
let wholeString = "This is whole string"
let partToAttribute = "whole string"
let attributedMessage = NSMutableAttributedString(string: wholeString)
.highlightString(with: UIColor.blue, for: partToAttribute, isBackground: true)
.fontHighlightString(with: UIFont.makeBoldFont(size: 16), color: UIColor.white, for: partToAttribute)
titleLabel.attributedText = attributedMessage
So at the end, you apply 2 attributes which are highlightString and fontHighlightString

First you can initialise attributes by using
var myAttribute = [ NSAttributedString.Key.foregroundColor: UIColor.init(hexString: "#FFAEA9"), NSAttributedString.Key.font: UIFont(name: "Dubai-Medium", size: 16) ]
after that you can use it...
let myString = "Enter default amount"
let text = NSAttributedString(string: myString, attributes: myAttribute)
enterCustomAmount.setAttributedTitle(text, for: .normal)

Related

How to add color to a specific text in Xcode using swift?

I am appending a text on the end of a string that I have received by making the API Call in Xcode. Now, I want to add colour and make only the text "Tap to Read More" bold. Any suggestions on how can I achieve that? Would appreciate your help:)
self.content[i] = recievedData.title! + "\nTap to Read More "
let str = "Tap to Read More"
let attributes: [NSAttributedString.Key: Any] = [
.foregroundColor: UIColor.red,
]
let attributedQuote = NSAttributedString(string: str, attributes: attributes)
You can try NSAttributedString.
Example:
let str = "Tap to Read More"
// Create your attributed string
let attributes: [NSAttributedString.Key : Any] = [.foregroundColor : UIColor.red]
let attributedStr = NSAttributedString(string: str, attributes: attributes)
// Example for a UILabel
let lbl = UILabel()
lbl.attributedText = attributedStr
Doc on NSAttributedString: https://developer.apple.com/documentation/foundation/nsattributedstring

Multi-Line TableView Cell with Text Formatting

Scenario
I have a JSON feed where I get all this information about places in a city. The design looks like this (see attached image).
The design wants bolded text, URL text, different font size text. Sometimes the URL might not exist in the JSON feed.
Question
Which option is the best way to lay this out in a TableViewCell?
I'm thinking Option 3 is the easiest as I read that in Swift 4 you can use the new string literals. Concat all the strings with line breaks and then place them in a UITextView. But I don't think it supports formatted text.
Looking for feedback please
Updated: Based on the research below I figured out that you can solve this by using:
- NSAttributedStringKey
- NSAttributedString
- NSMutableAttributedString
First Step
I created a private function called generateEntry that returns an NSMutableAttributedString
private func generateEntry(bType: String, bTitle:String, bDescription:String, bUrl: String) -> NSMutableAttributedString {
// -- passed in parameters
let bookmarkType:String = bType
let bookmarkTitle:String = bTitle
let bookmarkDesc: String = bDescription
let bookmarkUrl: String = bUrl
// -- type
// Step 1 - you need to create the attribute for the string you want to change. Size, color, kern effect
// Step 2 - then you create a NSAttributedString and apply the attributes from the previous line
let bTypeAttribute = [NSAttributedStringKey.font: UIFont(name: "Arial", size: 14), NSAttributedStringKey.foregroundColor: UIColor.black, NSAttributedStringKey.kern: NSNumber(value: 0.5)]
let test: NSAttributedString = NSAttributedString(string: bookmarkType.uppercased() + ": ", attributes: bTypeAttribute as Any as? [NSAttributedStringKey : Any])
// -- title
let bTitleAttribute = [NSAttributedStringKey.font: UIFont(name: "Arial-Bold", size: 18), NSAttributedStringKey.foregroundColor: UIColor.black]
let testb: NSAttributedString = NSAttributedString(string: bookmarkTitle, attributes: bTitleAttribute as Any as? [NSAttributedStringKey : Any])
// -- description
let bDescriptionAttribute = [NSAttributedStringKey.font: UIFont(name: "Arial", size: 18), NSAttributedStringKey.foregroundColor: UIColor.black]
let testc: NSAttributedString = NSAttributedString(string: ": " + bookmarkDesc, attributes: bDescriptionAttribute as Any as? [NSAttributedStringKey : Any])
// -- url
let bURLAttribute = [NSAttributedStringKey.font: UIFont(name: "Arial-Italic", size: 18), NSAttributedStringKey.foregroundColor: UIColor.black]
let testd: NSAttributedString = NSAttributedString(string: bookmarkUrl, attributes: bURLAttribute as Any as? [NSAttributedStringKey : Any])
// combine the strings
let mutableAttributedString = NSMutableAttributedString()
mutableAttributedString.append(test)
mutableAttributedString.append(testb)
mutableAttributedString.append(testc)
mutableAttributedString.append(NSAttributedString(string: "\n"))
mutableAttributedString.append(testd)
return mutableAttributedString
}
Step 2
Then within my tableViewCell I can use this private method to generate the NSMutableAttributedString and apply it to my custom tableViewCell. But instead of using cell.descriptionLabel.text you need to make sure to use attributedText
let testText = generateEntry(bType: "Type", bTitle: "PLACE", bDescription: "A description...", bUrl: "URL")
cell.descriptionLabel?.attributedText = testText

How to format attributed strings in swift?

I'm working on a notification page where inputs are mostly come from a JSON file and I need to combine them with localized strings. This is how it should look:
As can be predicted colored parts come from the JSON file and rest of it comes from Localizable.strings. This is what comes from Localizable file:
"%# has joined %#"
If I use String(format: String, [...]) I have a plain black text and I cant specify the parts needs to be colored.
I need the same feature for NSAttributedString but it doesn't have this method.
So how can I format attributed strings?
Check following example:
var myMutableString = NSMutableAttributedString()
myMutableString = NSMutableAttributedString(string: "Your full label textString")
myMutableString.setAttributes([NSFontAttributeName : UIFont(name: "HelveticaNeue-Light", size: CGFloat(17.0))!
, NSForegroundColorAttributeName : UIColor(red: 232 / 255.0, green: 117 / 255.0, blue: 40 / 255.0, alpha: 1.0)], range: NSRange(location:12,length:8)) // What ever range you want to give
yourLabel.attributedText = myMutableString
Or another way:
To change the colour of a length of text you need to know the start and end index of the coloured-to-be characters in the string e.g.
var main_string = "Hello World"
var string_to_color = "World"
var range = (main_string as NSString).rangeOfString(string_to_color)
Then you convert to attributed string and use 'add attribute' with NSForegroundColorAttributeName:
var attributedString = NSMutableAttributedString(string:main_string)
attributedString.addAttribute(NSForegroundColorAttributeName, value: NSColor.redColor() , range: range)
Try the below code and update your logic accordingly.
let localizableStr = "%# has joined %#"
let localisedStr = NSLocalizedString(localizableStr, comment: "")
let components = localizableStr.components(separatedBy: "%#")
let formatterStr = components.count > 2 ? components[1] : "has joined"
let evaluatedStr = NSString(format: localisedStr as NSString, "Rishi ", "Stack OVerflow")
let attributedStr = NSMutableAttributedString(string: evaluatedStr as String)
attributedStr.addAttribute(NSForegroundColorAttributeName, value: UIColor.brown, range: NSMakeRange(0, attributedStr.length))
let formatterStrRange = evaluatedStr.range(of: formatterStr, options: .caseInsensitive)
attributedStr.addAttribute(NSForegroundColorAttributeName, value: UIColor.black, range: formatterStrRange)
My two localised strings:
"welcome message" = "%# has joined %#";
"welcome message" = "انضم %# إلى %#";
Results
extension String {
func localisedAttributedString(_ replacements: CVarArg..., attributes: [NSAttributedString.Key : Any], replacementAttributes: [[NSAttributedString.Key : Any]?] ) -> NSAttributedString {
let message = String.init(format: NSLocalizedString(self, comment: ""), arguments: replacements)
let attributedString = NSMutableAttributedString(string: message, attributes: attributes)
for (i, replacement) in replacements.enumerated() {
if let att = replacementAttributes[i] {
let range = (attributedString.string.range(of: "\(replacement)".localized)?.nsRange(in: attributedString.string)) ?? NSRange(location: 0, length: 0)
attributedString.addAttributes(att as [NSAttributedString.Key : Any], range: range)
}
}
return attributedString
}
}
HOW TO USE
//General attr: Applied to the entire string
let generalAttributes = [NSAttributedString.Key.font: UIFont.getFont(.regular, size: 20)]
//Additional attrs applied to the replacement / dynamic bits. You can pass nil too
let nameAttributes = [ NSAttributedString.Key.backgroundColor: UIColor.red]
let companyAttributes = [ NSAttributedString.Key.foregroundColor: UIColor.blue]
myLabel.attributedText = "welcome message".localisedAttributedString("adam".localized, "space".localized, attributes: generalAttributes, replacementAttributes: [nameAttributes, companyAttributes] )

How do I combine two attributed strings into one UILabel?

I have two attributed strings and I want to place them in one label, how would I do this?
let font = UIFont(name: "Hidden", size: 15)
let addfont = UIFont(name: "Hidden", size: 15)
var att = [NSFontAttributeName : font]
let attrString = NSAttributedString(
string: animalname[indexPath.row],
attributes: NSDictionary(
object: font!,
forKey: NSFontAttributeName) as! [String : AnyObject])
let attrStringAdd = NSAttributedString(
string: animalloc[indexPath.row],
attributes: NSDictionary(
object: addfont!,
forKey: NSFontAttributeName) as! [String : AnyObject])
cell.animaltext.attributedText = attrString + attrStringAdd
Concatinated both the string.
Find range for string1 and string2.
Then for particular range of string apply desired attribute.
Now assign this attributed string to UILabel.

iPhone -Remove the underline from link in UILabel

I am using TTTAttributedLabel (https://github.com/twotoasters/TTTAttributedLabel). Here I'm correctly getting the label with some clickable text.
I need to display my text like the username in above image(ie. without underline).How could I do that?
Try this code (sorry for formatting, written on phone...)
NSDictionary *linkAttributes = #{[NSNumber numberWithInt:kCTUnderlineStyleNone] : (id)kCTUnderlineStyleAttributeName};
self.label.linkAttributes = linkAttributes;
For swift 4.0
let LinkAttributes = NSMutableDictionary(dictionary: testLink.linkAttributes)
LinkAttributes[NSAttributedStringKey.underlineStyle] = NSNumber(value: false)
yourLabel.linkAttributes = LinkAttributes as NSDictionary as! [AnyHashable: Any]
if you are already having label with underline style and if you want to remove it programatically use the following code
let string = "your text"
let attributes: [NSAttributedString.Key: Any] = [
.font: UIFont.systemFont(ofSize: 13)
]
let attributedString = NSAttributedString(string: string,
attributes: attributes)
yourLabel.attributedText = attributedString

Resources