Change LineBreak Characters from UILabel - ios

When i Add let say following text into my UILabel,
Lorem, Ipsum, is simply, dummy text of, the printing, and typesetting industry.
Now, let say my UILabel's width is limited but number of lines = 0(unlimited), then it will show the text like:
Lorem, Ipsum, is
simply, dummy text
of, the printing,
and typesetting
industry.
Here, you can see that line breaks are done at whitespaces, now i want them to update, and i want line breaks only when there is newline or comma(,) is there. So, How can i Implement that.
My Expected output is
Lorem, Ipsum,
is simply,
dummy text of,
the printing,
and typesetti
ng industry.

Tested solution
Create text and customText empty string
let text = "Lorem, Ipsum, is simply, dummy text of, the printing, and typesetting industry."
var customText = ""
Populate customText by substituting spaces with non-breakable spaces \u{00a0} if previous character is not ,
text.characters.enumerated().forEach { (idx, character) in
let prevChar = text[text.index(text.startIndex, offsetBy: max(0, idx-1))]
if character == " " && prevChar != "," {
customText.append("\u{00a0}")
}
else {
customText.append(character)
}
}
Create your label and assign customText to its text
let label = UILabel(frame: CGRect(x: 0, y: 0, width: 115, height: 5))
label.numberOfLines = 0
label.text = customText
label.sizeToFit()

NSString *string = #"Lorem, Ipsum,\n is simply,\n dummy text of,\n the printing,\n and typesett\ning industry.";
Now use this string

Maybe you should subclass the UILabel,CustomLabel.You should calculate the content and commas and add \n。

Swift 3.0
Simply add text like this. I have tested this and here you go with your expected output, but don't forget to set the label height according to this.
label33.text = "Lorem Ipsum,\nis simply,\ndummy text of,\nthe printing,\nand typesetti\nng industry."

Related

Search visualization using UILabel

I have a goal to implement local search inside Core Data entities. Technical part of finding occurrences is pretty clear. But I'm not sure how to display it correctly. Case: we have string in our entity
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
and user want to find entries with word adipiscing. We have UILabel with width i.e. 320dp and numberOfLines = 1. If we will display whole string without correct trimming it would be
Lorem ipsum dolor sit amet, consec...
which just useless for user. I want label to look like
...consectetur adipiscing elit, se...
So in other words I want search occurrence to be "focused" somewhere about middle of UILabel. How can I trim the string where occurrence has happened depending on label font and width?
It's not complete, but should bring you on track:
let wholeString = "This is a very long Hello world in a simple String"
let someSearch = wholeString.range(of: "Hello")!
// Make sure to call dropLast() twice to remove the space before 'Hello'
var stringBeforeSomeSearch = wholeString[wholeString.startIndex...someSearch.lowerBound].dropLast().dropLast()
// This is everything before the searched string
let prefixRange = stringBeforeSomeSearch.range(of: " ", options: .backwards)!.upperBound...someSearch.lowerBound
print(wholeString[prefixRange]) // prints "long H"
print(wholeString[prefixRange.lowerBound...someSearch.upperBound]) // prints "long Hello "
If you need more explanation, I'll be back later on.
Here's a solution that uses UITextView instead of UILabel, to achieve the desired search visualisation.
The concept is to use a UITextView with userInteraction and vertical scroll disabled and horizontal scroll enabled.
Use the textView method of scroll to range to show the desired word.
let textView = UITextView()
textView.isUserInteractionEnabled = false
textView.contentSize = CGSize(width: textWidth, height: textView.frame.size.width)
scrollTo(text: "swift")
func scrollTo(text: String) {
if let string = textView.text,
let range = string.localizedStandardRange(of: text) {
let viewRange = NSRange(range, in: string)
textView.scrollRangeToVisible(viewRange)
}
}
textWidth is the width of your total text. Follow this link for help.

Read/see More at the end of the label

I am trying to create a read more button at the end of my label. I want it to display 3 lines by default. I am coding in swift not objective c. Only when the user clicks the read more part of the label, should the label expand. It should look and work exactly like it does on instagram except on Instagram, it is in a tableview cell. My label and read more button will be in a scrollview. I have managed to get the expanding and contracting part working by adjusting the number of lines property of the label.
if descriptionLabel.numberOfLines == 0{
descriptionLabel.numberOfLines = 3
}else {
descriptionLabel.numberOfLines = 0
}
descriptionLabel.lineBreakMode = NSLineBreakMode.byWordWrapping
I am having problems with putting a "...more" at the end of the label and cutting the text off at the right place. I have looked at other people's responses to similar questions but nothing seems to work properly.
I can put a button over the last line of text so making the see more part of the label clickable also isn't the problem. The problem I am having is truncating the text at the right place and placing the see more text at the right place so that it displays.
I also want the read more button to only appear when it is necessary. I don't want to it appear when there are only 1-3 lines of text. This is also something I am having issues with.
I can't use this https://github.com/apploft/ExpandableLabel because it does not support scrollviews just tableviews.
the swift solution here didn't work: Add "...Read More" to the end of UILabel. It crashed the app.
Finally, the read more button should be in line with the last line of text and at the end of it. It would be an added benefit it this worked in a tableview cell as well!
I found ReadMoreTextView in Github, which is based on UITextView. The key method in this library is the following:
private func characterIndexBeforeTrim(range rangeThatFits: NSRange) -> Int {
if let text = attributedReadMoreText {
let readMoreBoundingRect = attributedReadMoreText(text: text, boundingRectThatFits: textContainer.size)
let lastCharacterRect = layoutManager.boundingRectForCharacterRange(range: NSMakeRange(NSMaxRange(rangeThatFits)-1, 1), inTextContainer: textContainer)
var point = lastCharacterRect.origin
point.x = textContainer.size.width - ceil(readMoreBoundingRect.size.width)
let glyphIndex = layoutManager.glyphIndex(for: point, in: textContainer, fractionOfDistanceThroughGlyph: nil)
let characterIndex = layoutManager.characterIndexForGlyph(at: glyphIndex)
return characterIndex - 1
} else {
return NSMaxRange(rangeThatFits) - readMoreText!.length
}
}
To display text like "xxxx...Read More", the library
Get how many characters could be display in the UITextView: Use NSLayoutManager.characterRange(forGlyphRange:, actualGlyphRange:)
Get the position of the last visible character and the width of "...Read More": Use NSLayoutManager.boundingRect(forGlyphRange glyphRange: NSRange, in container: NSTextContainer)
Get the character index before trimming: Use NSLayoutManager.characterIndexForGlyph(at glyphIndex: Int)
Replace text which should be trimmed with "...Read More": UITextStorage.replaceCharacters(in range: NSRange, with attrString: NSAttributedString)
Please check :
func addSeeMore(str: String, maxLength: Int) -> NSAttributedString {
var attributedString = NSAttributedString()
let index: String.Index = str.characters.index(str.startIndex, offsetBy: maxLength)
let editedText = String(str.prefix(upTo: index)) + "... See More"
attributedString = NSAttributedString(string: editedText)
return attributedString
}
You can use like :
let str = "Lorem Ipsum is simply dummy text of the printing and typesetting industry."
descriptionLabel.attributedText = addSeeMore(str: str, maxLength: 20)
// Output : Lorem Ipsum is simpl... See More

UILabel alignment in chat bubble (UICollectionViewCell)

I'm trying to align a UILabel inside a UICollectionViewCell like Whatsapp, Telegram, etc... Last line aligned to the left of the hour label, the others that ends on top of it.
I've tried adding at the end of the text some transparent characters and it worked until I tried some emojis; extra characters changed emoji code.
This is a sample of the code I'm using with TTTAttributedLabel for links detection.
let text = "Lorem ipsum dolor sit amet, enim apeirian his cu. Saperet interesset ex cum, ut per altera quodsi causae."
let endTransparentText = "aaaaa"
let showingText = text + endTransparentText
cell.lblMessage.setText(showingText) { (attributedString) -> NSMutableAttributedString? in
attributedString?.addAttribute(kCTForegroundColorAttributeName as String, value: UIColor.clear, range: NSMakeRange((showingText.characters.count-endTransparentText.characters.count), endTransparentText.characters.count))
return attributedString
}
Someone had to deal with this problem?

Change paragraph height (not line spacing) in a UILabel

Is it possible to limit the distance between paragraphs that were created using \n\n smaller in UILabels using attributed strings?
So for example, I would like this:
To look like this:
Would this involve replace \n\n with something else? Or is there a much simpler solution using NSAttributedString?
First at all: The usage of \n\n to create distances between two paragraphs is no good idea at all. \n has the semantic meaning of a new paragraph, so you have three paragraphs, where two are semantically meant. This is like a amateurish secretary deals with paragraph distances. You should replace them with a single \n.
However, you should not use font sizes to adjust line spacing or paragraph spacing. This highly relies on the shape of a font and its definition. Things break fast.
Add a paragraph style, because they are built for paragraph spacing. Set the line height or paragraph spacing properties.
The solution I outlined in my comment works. You can set the font size of the the empty line / paragraph spacing as something that pleases your eyes:
[myAttributedString addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:5.0] range:NSMakeRange(6, 1)];
The following code finds all occurrences of \n\n and specify the second one to have a specific size:
unsigned long length = myAttributedString.length;
NSRange range = NSMakeRange(0, length);
NSRange found;
while (NSNotFound != (found =[myAttributedString.string rangeOfString:#"\n\n" options:0 range:range]).location) {
[myAttributedString addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:8.0] range:NSMakeRange(found.location + 1, 1)];
range = NSMakeRange(found.location + 2, length - found.location - 2);
}
One thing I didn't mention in the question, which I had thought was obvious from the example is that the description is not within my control, it is generated by users. Therefore, the carriage return characters are added by them when they are creating the text.
So the solution I came up with is the following:
Firstly, I replace any \n\n characters with a single carriage return. This was inspired by amin-negm-awad's answer. \n\n is not a desirable way to generate a paragraph space.
I am doing this using the following piece of code:
func sanitize() -> String {
var output = NSMutableString(string: self)
var numberOfReplacements = 0
do {
let range = NSMakeRange(0, output.length)
numberOfReplacements = newString.replaceOccurrencesOfString("\n\n", withString: "\n", options: NSStringCompareOptions.CaseInsensitiveSearch, range: range)
} while (numberOfReplacements > 0)
return output as String
}
The next part is to apply a paragraph style with an attributed string. Here is an example function that is fairly flexible:
func textAttributesWithFont(font: UIFont, andColor color: UIColor,
lineSpacing: CGFloat = 0,
maximumLineHeight: CGFloat = 0,
textAlignment: NSTextAlignment = .Natural) -> [NSObject: AnyObject] {
var attributes = [NSFontAttributeName : font, NSForegroundColorAttributeName : color]
var paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineSpacing = lineSpacing
paragraphStyle.alignment = textAlignment
paragraphStyle.maximumLineHeight = maximumLineHeight
paragraphStyle.paragraphSpacing = 4
attributes[NSParagraphStyleAttributeName] = paragraphStyle
return attributes
}
Finally the label is constructed using the attributes:
var label1 = UILabel()
let text1 = "This is a test that is supposed😓😛😠😑 to wrap with some paragaphs\n\nThis is a paragraph"
label1.attributedText = NSAttributedString(string:sanitizeComment(text1), attributes: attributes)
label1.numberOfLines = 0

iPhone UILabel - non breaking space

Is there a way to use non breaking spaces in UILabel text?
For example, I have label with 2 lines and line breaking mode set to word wrap. The content for this label is read from database, where it's stored as a string. Now sometimes my text in label looks like that:
lorem ipsum some text
1
but I want to display it like that:
lorem ipsum some
text 1
so basicly, I need to force non breaking space between 'text' and '1'.
I've found some solution here, but I think it could work when the text is entered in source code file. In my case the text is in database.
Any suggestions?
Use the no-break space (\u00a0) ex: #"hello**\u00a0**world!"
post.text = [postText stringByAppendingString: #"1\u00a0hour\u00a0ago."];
U+00A0 / no-break space / Common Separator, space
from: http://en.wikipedia.org/wiki/Whitespace_character
For Swift:
let sentence = "Barcelona, Real Madryt, Juventus Turyn, Bayern Monachium"
let sentencewithnbsp = String(map(sentence.generate()) {
$0 == " " ? "\u{00a0}" : $0
})
In Swift 4 I had to use all caps: \U00A0
Example:
lorem ipsum some\U00A0text 1
Update Feb 2020 from the comments. Thanks to #corban:
In the Localizable.strings file it still needs to be \U00A0 - in code
you have to use \u{00a0}
If you want to get this to work in a .strings file, you can type OPTION + SPACE instead of a regular space.
.strings files are usually UTF-8 encoded, so if you open it in Hex view, you will see "C2 A0" in place of the special space character.
In Inspector set number of lines for Label as 3 or 4 What ever you require Then the Content will be displayed in multiple lines.
You may need to implement a custom word-wrapping function.
// pseudo-code
instring;
get len(instring)
if len > textedit.width*charFontSize
for (textedit.width*charFontSize ) // cycle through string
insert `\n` into inString at shortest whitespace
or something like that.
I don't think there's a simple way to do this with UILabel. Of course one way to achieve the same effect is to manually insert a "\n" before "text" in your example. Another option is to use a UIWebView with static HTML instead of the UILabel, so you can use an actual &nbsp.
Here's a Swift extension to fix an orphan in a string by replacing the last space with a non-breaking space:
extension String {
var replacingLastSpace: String {
if let lastSpace = range(of: " ", options: .backwards, locale: .current) {
return replacingCharacters(in: lastSpace, with: "\u{00a0}")
}
return self
}
}
Although note as of iOS 11, UILabels solve this problem automatically.
If this is not going to happen often, you can do this:
NSString *string = #"lorem ipsum some \ntext 1";
label.text = string;
You can dynamically generate where you put the \n using character counts, word counts etc...

Resources