How to insert custom emoji in UILabel programmatically, in Swift? - ios

I am building an iOS app where I have to add support for emojis within UILabel, so basically, whenever I receive a string containing either of these:
[kick-off]
[yellow-card]
[red-card]
[introduce]
[substitute]
[attention]
[free-kick]
[penalty]
[offside]
[extra-time]
[throw-in]
[corner]
[goal-post]
[bar]
[cheers]
[goal]
I have to replace these tags with a corresponding emoji. I have custom images for these emojis:
https://cdn-waf-beta.global.ssl.fastly.net/0.55.12/static/images/WAF_live_icons_sprite.png
Any idea how could I pull that off using Swift?

You need to create an NSTextAttachment and append it to an NSAttributedString.
Example:
let stringAttributes = [
// insert any attributes here
NSFontAttributeName : UIFont.systemFontOfSize(14)
,NSForegroundColorAttributeName : UIColor.blackColor()
]
let attributedString = NSMutableAttributedString(string: "the string before your image ", attributes: stringAttributes)
// Image needs to be NSData
let imageData:NSData = UIImagePNGRepresentation(yourImage)!
// Attachment type is very important
let attachment = NSTextAttachment(data: imageData, ofType: "public.png")
let attachmentString = NSAttributedString(attachment: attachment)
attributedString.appendAttributedString(attachmentString)
print(attributedString)
You can find the appropriate uti (Uniform Type Identifier) here

Here is the latest (swift v5) syntax with a few additions:
func strgWithImage(yourImage: String, preImage: String, postImage: String) -> NSMutableAttributedString {
// Call function with attributed text label:
// testLabel.attributedText = strgWithImage(yourImage: "doneX.png", preImage: "preS", postImage: "postS")
let stringAttributes = [
// insert any attributes here
NSAttributedString.Key.font : UIFont.systemFont(ofSize: 14)
,NSAttributedString.Key.foregroundColor : UIColor.black
]
let attributedString = NSMutableAttributedString(string: preImage, attributes: stringAttributes)
let postString = NSMutableAttributedString(string: postImage, attributes: stringAttributes)
let attachment = NSTextAttachment()
attachment.image = UIImage(named:yourImage) // loaded in assets
let imageOffsetY: CGFloat = -15.0
attachment.bounds = CGRect(x: 0, y: imageOffsetY, width: attachment.image!.size.width, height: attachment.image!.size.height)
let attachmentString = NSAttributedString(attachment: attachment)
attributedString.append(attachmentString)
attributedString.append(postString)
return attributedString
}

Related

One string with multiple paragraph styles

I want to have one string with different paragraphs styles. The goal is to customize the paragraph/line spacing for different parts of the string. I researched and found this answer but since I added multiple new line characters, not sure how to implement.
Design
This is my goal in terms of layout:
Code
This is the code I have which makes it look like the left image above. Please see the comments Not working in the code. Notice how the spacing is set for the main string, but the other strings can't then set their own custom spacing:
struct BookModel: Codable {
let main: String
let detail: String
}
func createAttributedString(for model: BookModel) -> NSMutableAttributedString {
let fullString = NSMutableAttributedString()
let mainString = NSMutableAttributedString(string: model.main)
let mainStringParagraphStyle = NSMutableParagraphStyle()
mainStringParagraphStyle.alignment = .center
mainStringParagraphStyle.lineSpacing = 10
mainStringParagraphStyle.paragraphSpacing = 30
let mainStringAttributes: [NSAttributedString.Key: Any] = [.paragraphStyle: mainStringParagraphStyle]
let spacingAfterQuote = NSMutableAttributedString(string: "\n")
let lineImageAttachment = NSTextAttachment(image: #imageLiteral(resourceName: "line-image"))
let lineImageString = NSMutableAttributedString(attachment: lineImageAttachment)
let lineParagraphStyle = NSMutableParagraphStyle()
lineParagraphStyle.alignment = .left
lineParagraphStyle.lineSpacing = 0 // Not working - instead of 0 it is 30 from `mainStringParagraphStyle`
lineParagraphStyle.paragraphSpacing = 0 // Not working - instead of 0 it is 30 from `mainStringParagraphStyle`
let lineAttributes: [NSAttributedString.Key: Any] = [.paragraphStyle: lineParagraphStyle]
let spacingAfterSeparator = NSMutableAttributedString(string: "\n")
let spacingAfterSeparatorParagraphStyle = NSMutableParagraphStyle()
spacingAfterSeparatorParagraphStyle.alignment = .left
spacingAfterSeparatorParagraphStyle.lineSpacing = 0 // Not working - instead of 0 it is 30 from `mainStringParagraphStyle`
spacingAfterSeparatorParagraphStyle.paragraphSpacing = 5 // Not working - instead of 5 it is 30 from `mainStringParagraphStyle`
let spacingAfterSeparatorAttributes: [NSAttributedString.Key: Any] = [.paragraphStyle: spacingAfterSeparatorParagraphStyle]
let detailString = NSMutableAttributedString(string: model.detail)
let detailStringAttributes: [NSAttributedString.Key: Any] = [.font: UIFont.systemFont(ofSize: 20)]
fullString.append(mainString)
fullString.append(spacingAfterQuote)
fullString.append(lineImageString)
fullString.append(spacingAfterSeparator)
fullString.append(detailString)
fullString.addAttributes(mainStringAttributes, range: fullString.mutableString.range(of: model.main))
fullString.addAttributes(lineAttributes, range: fullString.mutableString.range(of: lineImageString.string))
fullString.addAttributes(spacingAfterSeparatorAttributes, range: fullString.mutableString.range(of: spacingAfterSeparator.string))
fullString.addAttributes(detailStringAttributes, range: fullString.mutableString.range(of: model.detail))
return fullString
}
Any thoughts on how to achieve the image on the right?
Question Update 1
The code below is working! There is only one slight problem. When I add lineSpacing, there is extra space at the end of the last line in main string. Notice that I have this set to zero: mainStringParagraphStyle.paragraphSpacing = 0, but there is still space at the end because mainStringParagraphStyle.lineSpacing = 60.
The reason I ask this is to have more fine grain control of spacing. For example, have a perfect number between the line image and main string. Any thoughts on this?
I put code and picture below:
Code:
func createAttributedString(for model: BookModel) -> NSMutableAttributedString {
let fullString = NSMutableAttributedString()
let mainStringParagraphStyle = NSMutableParagraphStyle()
mainStringParagraphStyle.alignment = .center
mainStringParagraphStyle.paragraphSpacing = 0 // The space after the end of the paragraph
mainStringParagraphStyle.lineSpacing = 60 // NOTE: This controls the spacing after the last line instead of just `paragraphSpacing`
let mainString = NSAttributedString(string: "\(model.main)\n",
attributes: [.paragraphStyle: mainStringParagraphStyle, .font: UIFont.systemFont(ofSize: 24)])
let lineImageStringParagraphStyle = NSMutableParagraphStyle()
lineImageStringParagraphStyle.alignment = .center
let lineImageAttachment = NSTextAttachment(image: #imageLiteral(resourceName: "line-view"))
let lineImageString = NSMutableAttributedString(attachment: lineImageAttachment)
lineImageString.addAttribute(.paragraphStyle, value: lineImageStringParagraphStyle, range: NSRange(location: 0, length: lineImageString.length))
let detailStringParagraphStyle = NSMutableParagraphStyle()
detailStringParagraphStyle.alignment = .center
detailStringParagraphStyle.paragraphSpacingBefore = 5 // The distance between the paragraph’s top and the beginning of its text content
detailStringParagraphStyle.lineSpacing = 0
let detailString = NSAttributedString(string: "\n\(model.detail)",
attributes: [.paragraphStyle: detailStringParagraphStyle, .font: UIFont.systemFont(ofSize: 12)])
fullString.append(mainString)
fullString.append(lineImageString)
fullString.append(detailString)
return fullString
}
Updated answer:
Here's a new example. I set the spacing at the top and at the bottom of the paragraph with the image. This allows line breaks to be used in model.main and model.detail if needed. Also, instead of lineSpacing, I used lineHeightMultiple. This parameter affects the indentation between lines without affecting the last line:
func createAttributedString(for model: BookModel) -> NSAttributedString {
let fullString = NSMutableAttributedString()
let mainStringParagraphStyle = NSMutableParagraphStyle()
mainStringParagraphStyle.alignment = .center
mainStringParagraphStyle.lineHeightMultiple = 2 // Note that this is a multiplier, not a value in points
let mainString = NSAttributedString(string: "\(model.main)\n", attributes: [.paragraphStyle: mainStringParagraphStyle, .font: UIFont.systemFont(ofSize: 24)])
let lineImageStringParagraphStyle = NSMutableParagraphStyle()
lineImageStringParagraphStyle.alignment = .center
lineImageStringParagraphStyle.paragraphSpacingBefore = 10 // The space before image
lineImageStringParagraphStyle.paragraphSpacing = 20 // The space after image
let lineImageAttachment = NSTextAttachment(image: #imageLiteral(resourceName: "line-image"))
let lineImageString = NSMutableAttributedString(attachment: lineImageAttachment)
lineImageString.addAttribute(.paragraphStyle, value: lineImageStringParagraphStyle, range: NSRange(location: 0, length: lineImageString.length))
let detailStringParagraphStyle = NSMutableParagraphStyle()
detailStringParagraphStyle.alignment = .center
let detailString = NSAttributedString(string: "\n\(model.detail)", attributes: [.paragraphStyle: detailStringParagraphStyle, .font: UIFont.systemFont(ofSize: 12)])
fullString.append(mainString)
fullString.append(lineImageString)
fullString.append(detailString)
return fullString
}
Also have a look at my library StringEx. It allows you to create a NSAttributedString from the template and apply styles without having to write a ton of code:
import StringEx
...
func createAttributedString(for model: BookModel) -> NSAttributedString {
let pattern = "<main />\n<image />\n<detail />"
let ex = pattern.ex
ex[.tag("main")]
.insert(model.main)
.style([
.aligment(.center),
.lineHeightMultiple(2),
.font(.systemFont(ofSize: 24))
])
let lineImageAttachment = NSTextAttachment(image: #imageLiteral(resourceName: "line-image"))
let lineImageString = NSAttributedString(attachment: lineImageAttachment)
ex[.tag("image")]
.insert(lineImageString)
.style([
.aligment(.center),
.paragraphSpacingBefore(10),
.paragraphSpacing(20)
])
ex[.tag("detail")]
.insert(model.detail)
.style([
.aligment(.center),
.font(.systemFont(ofSize: 12))
])
return ex.attributedString
}
Old answer:
I think you can just set the spacing at the end of the first paragraph (main string) and the spacing at the beginning of the last paragraph (detail string):
func createAttributedString(for model: BookModel) -> NSMutableAttributedString {
let fullString = NSMutableAttributedString()
let mainStringParagraphStyle = NSMutableParagraphStyle()
mainStringParagraphStyle.alignment = .center
mainStringParagraphStyle.paragraphSpacing = 30 // The space after the end of the paragraph
let mainString = NSAttributedString(string: "\(model.main)\n", attributes: [.paragraphStyle: mainStringParagraphStyle])
let lineImageStringParagraphStyle = NSMutableParagraphStyle()
lineImageStringParagraphStyle.alignment = .center
let lineImageAttachment = NSTextAttachment(image: #imageLiteral(resourceName: "line-image"))
let lineImageString = NSMutableAttributedString(attachment: lineImageAttachment)
lineImageString.addAttribute(.paragraphStyle, value: lineImageStringParagraphStyle, range: NSRange(location: 0, length: lineImageString.length))
let detailStringParagraphStyle = NSMutableParagraphStyle()
detailStringParagraphStyle.alignment = .center
detailStringParagraphStyle.paragraphSpacingBefore = 5 // The distance between the paragraph’s top and the beginning of its text content
let detailString = NSAttributedString(string: "\n\(model.detail)", attributes: [.paragraphStyle: detailStringParagraphStyle])
fullString.append(mainString)
fullString.append(lineImageString)
fullString.append(detailString)
return fullString
}

How to Align Text with NSAttributedString in swift

I am looking for align Text in NSAttributedString. It should be on same indent(List of string on same Column).
Output:
I have used below code
func setAttributedText() {
let image1Attachment = NSTextAttachment()
image1Attachment.image = UIImage(named: "checkgreen.png")
image1Attachment.bounds = CGRect(x: 0,
y: (contentLabel.font.capHeight - image1Attachment.image!.size.height).rounded() / 2, width: image1Attachment.image!.size.width,
height: image1Attachment.image!.size.height)
var attributes = [NSAttributedString.Key: Any]()
attributes[.font] = UIFont.preferredFont(forTextStyle: .body)
attributes[.foregroundColor] = UIColor.black
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.headIndent = 50
attributes[.paragraphStyle] = paragraphStyle
let line0 = NSAttributedString(string: "Dummy text is text that is used in the publishing industry or by web designers to occupy the space which will later be filled with 'real' content. \n")
let line1 = NSAttributedString(string: "Your account will be charged for renewal within 24-hours prior to the end of the current subscription perio\n")
// let line2 = NSAttributedString(string: "You can manage your subscriptions and turn off auto-renewal by going to your Account Settings on th")
let checkgreenImageAttribute = NSMutableAttributedString(attributedString: NSAttributedString(attachment: image1Attachment))
let finalString = NSMutableAttributedString(attributedString: checkgreenImageAttribute)
finalString.append(line0)
finalString.append(checkgreenImageAttribute)
finalString.append(line1)
// finalString.append(checkgreenImageAttribute)
// finalString.append(line2)
contentLabel.attributedText = finalString
}
NOTE: i don't want to use bullet points
In an attributed string, a NSTextAttachment becomes a character in the string.
So, apply your attributes to the entire string after you've "assembled" it:
let checkgreenImageAttribute = NSMutableAttributedString(attributedString: NSAttributedString(attachment: image1Attachment))
let finalString = NSMutableAttributedString(attributedString: checkgreenImageAttribute)
finalString.append(line0)
finalString.append(checkgreenImageAttribute)
finalString.append(line1)
// apply paragraph attributes here
finalString.addAttributes(attributes, range: NSRange(location: 0, length: finalString.length))

How to mask text of a UILabel or a UITextView?

I would like to mask text of a UILabel to achieve the following result
In Swift, You can do it like this:
var attributedString = NSMutableAttributedString(string: "Your String")
let textAttachment = NSTextAttachment()
textAttachment.image = UIImage(named: "Your Image Name")
let attrStringWithImage = NSAttributedString(attachment: textAttachment)
attributedString.insert(attrStringWithImage, at: 0)
label.attributedText = attributedString
This will work for you .
extension UILabel
{
func addImage(imageName: String)
{
let attachment:NSTextAttachment = NSTextAttachment()
attachment.image = UIImage(named: imageName)
let attachmentString:NSAttributedString = NSAttributedString(attachment: attachment)
let myString:NSMutableAttributedString = NSMutableAttributedString(string: self.text!)
myString.appendAttributedString(attachmentString)
self.attributedText = myString
}
}
Another version of the code that allow adding the icon before or after the label.
extension UILabel
{
func addImage(imageName: String, afterLabel bolAfterLabel: Bool = false)
{
let attachment: NSTextAttachment = NSTextAttachment()
attachment.image = UIImage(named: imageName)
let attachmentString: NSAttributedString = NSAttributedString(attachment: attachment)
if (bolAfterLabel)
{
let strLabelText: NSMutableAttributedString = NSMutableAttributedString(string: self.text!)
strLabelText.appendAttributedString(attachmentString)
self.attributedText = strLabelText
}
else
{
let strLabelText: NSAttributedString = NSAttributedString(string: self.text!)
let mutableAttachmentString: NSMutableAttributedString = NSMutableAttributedString(attributedString: attachmentString)
mutableAttachmentString.appendAttributedString(strLabelText)
self.attributedText = mutableAttachmentString
}
}
//you can remove the image by calling this function
func removeImage()
{
let text = self.text
self.attributedText = nil
self.text = text
}
}
For this use a UITextView instead of UILabel.
let imgRectBezier = UIBezierPath(rect: imgView.frame)
txtView.textContainer.exclusionPaths = [imgRectBezier]
Using this, the text will be excluded from the frame area added in the exclusion paths. You can even exclude multiple frames.

How to center AttributedText link in UITextView swift4

Is there any way I can just make the text center and change the size/font?
I set it in storyboard but nothing happen when I ran the app. I also tried to use UILabel but it didn't work (when I click it the web didn't show)
The answer I found they didn't include the URL just attributes.paragraphstyle.
I don't know how to write it so I can keep the URL string also change the font/size/alignment.
func searchMealRecipe() {
MealRecipeService.sharedInstance.foodID = foodID
MealRecipeService.sharedInstance.getMealRecipe { (responseString, mealRecipeItem) in
if responseString == "error" {
/* popup here alerting user about error*/
} else {
self.mealRecipeItem = mealRecipeItem
self.ingredientRecipeTitleLabel.text = (mealRecipeItem.name)?.htmlString?.string
self.ingredientRecipeImageView.sd_setImage(with: URL(string: mealRecipeItem.imageURL!), placeholderImage: UIImage(named: ""))
self.ingredientRecipeTextView.text = mealRecipeItem.ingredients?.joined(separator: "\n")
let string = "Sounds Good! Show Me the Steps 📖"
let attributedString = NSMutableAttributedString(string: string, attributes:[NSAttributedString.Key.link: URL(string: "\(mealRecipeItem.link!)")!])
self.ingredientRecipeLinkTextView.attributedText = attributedString
}
}
}
I'm using xcode10 beta and swift4.2
//Update(I figured it out the answer and it's working)
//the attributes accept array so I can put multiple styles in the array.
let string = "Sounds Good! Show Me the Steps 📖"
let style = NSMutableParagraphStyle()
style.alignment = NSTextAlignment.center
let attributedString = NSMutableAttributedString(string: string, attributes:[NSAttributedString.Key.link: URL(string: "\(mealRecipeItem.link!)")!, NSAttributedString.Key.paragraphStyle: style])
self.ingredientRecipeLinkTextView.attributedText = attributedString
you can use NSMutableParagraphStyle
let style = NSMutableParagraphStyle()
style.alignment = NSTextAlignment.center
lbl.centerAttributedText = NSAttributedString(string: "Total Balance",attributes: [your Attributed URL Code here])

Change color of a certain word in UILabel

I just want to change color of some word in my Label's string.
What I want to do is:
How Im trying to do:
But it doesnt work and it says: 'NSMutableRLEArray replaceObjectsInRange:withObject:length:: Out of bounds'
So I need an advice to do this easy thing, and I need a trick to do this. You guys can send your way.
Try this, your attributed string has no value when you try get the range of the string. You got to give the attributed string a string value before that. You
let descriptionLabel: UILabel = {
let label = UILabel()
// String variable containing the text.
let fullString = "mehmet alper tabak"
// Choose wwhat you want to be colored.
let coloredString = "alper"
// Get the range of the colored string.
let rangeOfColoredString = (fullString as NSString).range(of: coloredString)
// Create the attributedString.
let attributedString = NSMutableAttributedString(string:fullString)
attributedString.setAttributes([NSForegroundColorAttributeName: UIColor.red],
range: rangeOfColoredString)
label.attributedText = attributedString
return label
}()
descriptionLabel.frame = CGRect(x: 50, y: 50, width: 140, height: 100)
self.view.addSubview(descriptionLabel)
Or you can do an extension of UILabel.
extension UILabel {
func colorString(text: String?, coloredText: String?, color: UIColor? = .red) {
let attributedString = NSMutableAttributedString(string: text!)
let range = (text! as NSString).range(of: coloredText!)
attributedString.setAttributes([NSForegroundColorAttributeName: color!],
range: range)
self.attributedText = attributedString
}
To use it.
self.testLabel.colorString(text: "mehmet alper tabak",
coloredText: "alper")
It is out of bounds because myMutableString is empty at the time that you are trying to set attributes. Try instead:
let myMutableString = NSMutableAttributedString.init(string: label.text)
Create attributedString1 with first color
let attributedString1 = NSAttributedString(string: "robert plant ", attributes: [NSForegroundColorAttributeName: color1])
Create attributedString2 with second color
let attributedString1 = NSAttributedString(string: "with queen", attributes: [NSForegroundColorAttributeName: color2])
Add them to myMutableString
myMutableString.append(attributedString1)
myMutableString.append(attributedString1)

Resources