Multiple lines UILabel with multiple buttons and fonts - ios

I'm making a UITableViewCell for some activity feed objects, to give you an idea they're going to be like the Facebook posts where you have multiple links in one post.
In my case there are going to be two links to other UIViewController for each post and one plain UILabel that connects the two and explains the connection (such as "X has commented on Y's post") where you could tap both X and Y for some actions to happen.
As right now, I just made 3 separate UILabels. The problem with that is that I'm not sure how to handle names that are too long in multiple lines.
Meaning if for example instead of X, the post was "XXXXXXXXXXXXXXXXX has commented on Y's post", then "has commented on Y's post" would need to go on another line.
As right now I just link the 3 UILabels with constraints such that they're next to each other but that wouldn't work when they're too long.
If you have any idea on how could I approach this issue, it would be really appreciated if you could let me know.
Thanks in advance.

There are too many labels, I think you can use this extension:
extension NSMutableAttributedString {
public func setAsLink(textToFind:String, linkURL:String) -> Bool {
let foundRange = self.mutableString.rangeOfString(textToFind)
if foundRange.location != NSNotFound {
self.addAttribute(NSLinkAttributeName, value: linkURL, range: foundRange)
return true
}
return false
}
}
Then you can do:
let labelFont = UIFont(name: "HelveticaNeue-Bold", size: 18)
let attributes :[String:AnyObject] = [NSFontAttributeName : labelFont!]
let attrString = NSAttributedString(string:"foo www.google.com", attributes: attributes)
let urlPath: String = "http://www.google.com"
let url: NSURL = NSURL(string: urlPath)!
attrString.setAsLink("www.google.com", linkURL:url)
myLabel.attributedText = attrString
UPDATE: (after your comments)
If you need to intercept urllink you can transform your label to textView (UITextView), set it the delegate and handle the shouldInteractWithURL method:
class ViewController: UIViewController, UITextViewDelegate {
... // on your code do:
myTextView.delegate = self
...
func textView(textView: UITextView!, shouldInteractWithURL URL: NSURL!, inRange characterRange: NSRange) -> Bool {
if URL.scheme == "http://www.google.com" {
//do whatever you want
launchMyMethodForThisUrl()
}
}

Set number of line of label to zero like,
label.numberOfLines = 0 // it will increase line when needed
And use attribute string to for different fonts and size or links etc.
and then set that string to label as,
label.attributedText = attributedString
Update :
If you want some action event on particular text then you should use UITextview instead of label. it will be more easier by implementing shouldInteractWithURL delegate of it.
Another good solution is use thirdparty library like TTTAttributedLabel. It is exact what you want i think. have a look once.
Update 2 :
refer this link or this link for your desired output
Hope this will help :)

Related

Put two words in a same line if they fit, if not put them in a new line

So I have the following question:
I have attributed string contained in a UITextView. Attributed string is contained of two parts - the normal one is ordinary text, and the second part has NSURL in it - so for better visualisation it would look like:
For more information READ HERE
The problem that I am having is that if the text fits in the one line I must keep it in one line, and if "HERE" falls in second line I must put READ also in the second line.
So, the first case - if all fits
For more information READ HERE
All other cases -
For more information
READ HERE
I tried to do it with checking if size of the screen is bigger than textfield bounds but it didn't work:
if (label.bounds.size.width < size.width) ...
I tried also other similar solutions but i think they are all addable on normal UILabels and not modified attributed texts.
If you have any idea how to deal with this I would appreciate it.
Thanks :)
There are multiple solution to resolve this.
First one is to use non-breakable space
simply add "\u{00a0}" in between click here like CLICK\u{00a0}HERE
here is the link
Second one is instead of adding space you can add "_" into it.
e.g.
"CLICK_HERE" and you can replace the color of "_" with clear.
here is the code
class ViewController: UIViewController {
#IBOutlet weak var temp : UILabel!
override func viewDidLoad() {
let myString = "For more detail information READ_HERE"
let attributedString: NSMutableAttributedString = NSMutableAttributedString(string: myString)
attributedString.setColorForText(textForAttribute: "_", withColor: .clear)
temp.attributedText = attributedString
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
extension NSMutableAttributedString {
func setColorForText(textForAttribute: String, withColor color: UIColor) {
let range: NSRange = self.mutableString.range(of: textForAttribute, options: .caseInsensitive)
self.addAttribute(NSAttributedString.Key.foregroundColor, value: color, range: range)
}
}

Swift IOS Custom Action in Number Detection

In our App, we have an outbound VOIP phone dialer.
I would like to detect numbers within a string and add a custom action so that instead of the default behaviour to open the native dialer, it will go to our own dialler within the App and pre-populate the selected number.
My first thoughts were to get the body of the message into a string array of words and check each word to see if it is a number and then create an attributed string. But I'm struggling to understand how I can get an attributed string back into a string and what I would even specify to open the said screen with the number pre-populated.
I am aware of NSDataDetector which can list the numbers found in the string but I am stuck with how to replace those particular parts with a clickable action and return it back as a string.
If anyone has had similar experiences with this then any help would be much appreciated?
NOTE : The body of this message is being show in a UILabel control.
Update
this is what I had so far...
func AddNumberLink() -> String {
let body = self
let wordsInBody = body.components(separatedBy: .whitespaces)
for i in 0..<wordsInBody.count {
var word = wordsInBody[i]
if word.isTelephoneNumeric {
let attributedString = NSMutableAttributedString(string:word)
attributedString.addAttribute(NSAttributedStringKey.link, value:"https://www.google.com",range: NSRange(location: 0, length: word.count))
attributedString.addAttribute(NSAttributedStringKey.foregroundColor,value: UIColor.red, range: NSRange(location:0, length: word.count))
word = attributedString.string
}
}
return body
}
NSDataDetector gives you the ranges of the numbers. Now, create a (mutable) attributed string and for each range, add a link to the corresponding number URL (NSAttributedString.Key.link: urlString).
Then, display the text in a UITextView and add a delegate.
In the delegate, implement func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange) -> Bool
In this function, you can decide what action is appropriate.
Thank you all for your suggestions it has made me approach the problem in a different manner to my code above.
Like #Lutz and #Larme suggested above, by overriding the default behaviour when the UITextView is tapped I can gather the number via the UITextViewDelegate.
So the complete solution is :
Add number detector attribute to UITextView
Implement UITextViewDelegate func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool
Add custom logic to handle the tap accordingly.

How to convert smileys to emoticon?

Is there a way to convert ":)" to 😊 like : detection and conversion, I have a UITextView in a chat application which should convert the smiley to the respective emoticon.
Get the offical list: emoji
Just an example: yourTextView.text = "your smiley: \u{1f642}"
If you want to convert emoticons to emoji on the fly either you need to specify it by yourself and analyze the input string or using a 3rd-party lib e.g. pods for converting and watching input string through text change events e.g.: docs
You can use the logic in this package npm, you find also a map of smile and the respective emoji:
https://www.npmjs.com/package/smile2emoji
I have created this simple class based on the npm package suggested by #emish89 https://www.npmjs.com/package/smile2emoji.
https://gist.github.com/lorenzOliveto/f20a89e9f68276cae21497a177ad8a4c
Swift 5
You should implement delegate textViewDidChange for your UITextView and find all need substrings in its text then replace them inside with textStorage property:
extension ViewController : UITextViewDelegate {
func textViewDidChange(_ textView: UITextView) {
guard let text = textView.text else { return }
var index = text.startIndex
while let range = text.range(of: ":)", range: index..<text.endIndex) {
index = range.upperBound
textView.textStorage.replaceCharacters(in: NSRange(range, in: text), with: "😀")
}
}
}
It works while editing or paste text.

How to use NSAttributedStrings in iOS with Swift?

I would like to have a label with an attributed string that changes dynamically at runtime. Something like this
Is there a better way to dynamically change the price value without hardcoding the attributed keys dictionaries and manually building the NSAttributedString?
The best way to approach Attributed Strings on iOS is by using the built-in Attributed Text editor in the interface builder and avoid uneccessary hardcoding NSAtrributedStringKeys in your source files.
You can later dynamically replace placehoderls at runtime by using this extension:
extension NSAttributedString {
func replacing(placeholder:String, with valueString:String) -> NSAttributedString {
if let range = self.string.range(of:placeholder) {
let nsRange = NSRange(range,in:valueString)
let mutableText = NSMutableAttributedString(attributedString: self)
mutableText.replaceCharacters(in: nsRange, with: valueString)
return mutableText as NSAttributedString
}
return self
}
}
Add a storyboard label with attributed text looking like this.
Then you simply update the value each time you need like this:
label.attributedText = initalAttributedString.replacing(placeholder: "<price>", with: newValue)
Make sure to save into initalAttributedString the original value.
You can better understand this approach by reading this article:
https://medium.com/mobile-appetite/text-attributes-on-ios-the-effortless-approach-ff086588173e
May not know, this is what you're looking for but will solve your problem.
You may use labels for each and update amount easily without touching attributed string.
Here is result:
Sample code, I've tried for this demo:
#IBOutlet weak var lblAmount: UILabel!
func pricelabel() -> Void {
var amount = 0
Timer.scheduledTimer(withTimeInterval: 0.3, repeats: true) { (timer) in
amount += 10
self.lblAmount.text = "\(amount)"
}.fire()
}

Bold part of the text in UITextView in Swift using NSMutableAttributedString

I'm trying to bold part of a text in a UITextView.
Here is the code I'm using (though I simplified the text):
#IBOutlet weak var textview: UITextView!
/*...*/
var str : NSMutableAttributedString = NSMutableAttributedString(string:"this is a test", attributes: [NSFontAttributeName:textview.font!] )
str.addAttribute(NSFontAttributeName, value: UIFont(name: "GillSans-Bold", size: 16)!, range: NSMakeRange(10, 4))
textview.attributedText = str
However it is not working and I don't understand why... All the examples I've seen use this. There is no error, but nothing in the text is in bold.
Sorry if it's a duplicate, I found plenty of posts talking about putting text in bold in a uitextview, some used this technique but none had problems with it, as far as I know. Is there something I missed? Did I read something wrong?
Thanks in advance.
One reason might be that the font you're using doesn't exist or is not properly named in the code. Here's a neat trick to show the list of all available fonts:
//credits to Chris: http://codewithchris.com/common-mistakes-with-adding-custom-fonts-to-your-ios-app/#includefonts
func printFontFamilyNames(){
for family: String in UIFont.familyNames()
{
print("\(family)")
for names: String in UIFont.fontNamesForFamilyName(family)
{
print("== \(names)")
}
}
}
This may not directly answer your question but I'm sure you'll find it helpful. And also check the link on the top of this code block, from which I've extracted this function.
UPDATE
Here's a piece of code I wrote, which properly changes the font of attributed strings within a UILabel:
if let terms = self.terms {
let text = NSMutableAttributedString(string: terms.text!)
let textAsString = text.string as NSString
let range = NSMakeRange(0, textAsString.length)
textAsString.enumerateSubstringsInRange(range, options: NSStringEnumerationOptions.ByWords, usingBlock: { (substring, substringRange, enclosingRange, _) -> () in
if ["Create", "Account", "Terms", "of", "Service", "Privacy", "policy"].contains(substring!) {
if let font = UIFont(name: "OpenSans-Semibold", size: 11) {
text.addAttribute(NSFontAttributeName, value: font, range: substringRange)
} else {
Services.log("Invalid font")
}
}
//...
})
terms.attributedText = text
}
NOTE: This code contains project specific classes and variables, but I'm sure you can extract what you need.

Resources