Setting NSLineBreakByTruncatingHead has no effect - ios

I have the following lines of code:
textView.textContainer.maximumNumberOfLines = 10;
textView.textContainer.lineBreakMode = NSLineBreakByTruncatingHead;
[textView setText:#"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11"];
No matter what I set the lineBreakMode to, the output always excludes the 11, but includes the 1. I need to truncate the start of the string, not the end. Any ideas on why this isn't working?

That’s not what the line break mode does. If an individual line is wider than will fit in the text view, that mode should cause it to get truncated at the head rather than wrapping to an additional line, but it won’t cause the text view to change which lines it displays.
To get the effect you’re after, you’ll have to modify the string yourself. If it’s formatted the way your test string is (with explicit newlines) that’s relatively easy—-rangeOfString:options:range: will let you search backwards along the string until you find the start of the Nth line you want to keep—but if you’re relying on word wrapping, then you’ll have to use -boundingRectWithSize:options:attributes: and keep chopping words off the beginning until it fits within the size you need.

Related

How can I add the three dots of a UIlabel when the text is too long, at the middle of the text instead of at the end?

For example, I have the following two string: "How Munched is That Birdie in the Window?" and "S22 - E7". I want to present in the label the following: "How Munched is That Birdie in ... S22 - E7" If the string is too large according to the label's size and doesn't fit it". How you can see, the three dots are placed always in the first string, the second string is always shown full.
How can I achieve this?
Here is how to do it.
yourlabel.adjustsFontSizeToFitWidth = false
yourlabel.lineBreakMode = .byTruncatingMiddle
You can set the UILabel's ParagraphStyle LineBreakMode to byTruncatingMiddle, which will probably work in most cases with carefully planned label size. From the docs:
The line is displayed so that the beginning and end fit in the container and the missing text in the middle is indicated by an ellipsis glyph. This mode is used for single-line layout
If you want to guarantee none of the "S22 - E7" string is truncated, you would have more control using two labels and setting layout constraints such that the width of the protected label is preserved so that it can display the full string whenever possible, but that is probably overkill in most cases.
You may also find this answer helpful if decide to go a different route by manually manipulating the displayed string based on detecting how many characters will be visible given the width and font.

Fitting multi-line text into a dynamically size-changing node

A multiline auto typing text box class (which uses an SKNode as the parent) is created using basically 2 elements:
an SKSpriteNode that acts as text box frame & background image/texture holder.
an NSMutableArray containing a set limited amount (rows) of NSStrings that each have a set character length.
After modifying this text box class so that it can be initialized with any frame width & height, I realized I didn't program the NSMutableArray to automatically change its content in a such way that it nicely fits within the background node (with a bit of padding involved as well). So here I am wondering how to do that since NSString's can only return the character count and not the width & height of each string in points (points could have maybe helped me create character constraints in some way).
Right now, the NSMutableArray uses a hardcoded maximum character count per NSString & a maximum row count for the entire array (it's 5 rows right now and when that limit is reached, a new "page"/array is created). This forces me to manually re-adjust these parameters every time I change the background node frame size which defeats the purpose of the class allowing the background frame to change.
Thing is, I'm trying to solve this in such a way that when I post this class on github, I want the solution to take into consideration any fontName & fontSize.
What are my options for solving this problem?
I've done something similar to this. It doesn't work 100% as to what you want, but should be similar enough. It uses a root node and from there, it will build multi-line text using an array of NSString which will in turn be used to build the SKLabelNode.
I'll outline what I did. I should also say I only run this when new text is set. In other words, I do not incur the penalty of deriving the information every frame. Only once.
The generalized steps are:
You will iterate over each character in the text string. Note I do this because my code supports word wrapping as well as other alignment capabilities. So for me, I want that level of control. As this is being done only upon creation, I'm fine with the overhead. If you don't want to word wrap you could always just create an array of words and work from there.
As you iterate over each character, you'll be generating an array of lines. Where each line in the array is a line that will fit in your frame. For now let's not worry about vertical constraints. So here we are primarily worried about width. For the current line, each character you are iterating over will get added to the current line. For this potential line string, you will use NSString's sizeWithAttributes, which is configured for your font. For example in my code it is an NSDictionary which contains: NSFontAttributeName : [UIFont fontWithName:self.fontName size:self.size]. This will be used to check the width, if that width exceeds the frame width, you are overrunning the line.
So the code may look something like:
size = [line sizeWithAttributes:attributes];
if (size.width > maxTextWidth) {
needNewline = YES;
}
If you have overrun a line, you need to determine if you are word wrapping. If you are, you can just add the current line (minus one character) to the lines array. If not you have prune off the last word in the current line and then add that to the array of lines.
The tricky parts are dealing with whitespace and handling non-word wrapped overflow. I have not addressed whitespace but you need to consider this very much in your code. Additionally, you also do want to factor in leading pixels, etc.
Once you have your array of lines, you can then create your children SKLabelNodes. I add them to the root, which allows me to move the group anywhere it needs to be.
The real key here is the lines array generation.

In a UILabel, is it possible to force a line NOT to break in a certain place

I have a UILabel that is supposed to be two lines long. The text is localized into French and English.
I'm setting the attributedText property of the label. The text is constructed from three appended strings, say textFragment1, textFragment2 and textFragment3. The middle string is actually an image created with an NSTextAttachment.
I want to make sure that textFragment2 and textFragment3 are on the same line. The first string may or may not wrap depending on how long it is. The problem is that my French text is currently fairly short, so the line is wrapping after textFragment2.
I have fixed this temporarily by adding a line break symbol in the localized French text for textFragment1. I don't really love this solution though. I was wondering if there is a way to treat textFragment2 and textFragment3 so that they will always be together on the same line.
You could use a non-breaking space (\u00a0) to join textFragment2 and textFragment3. This character looks just like a normal space—i.e. it results in the same amount of whitespace—but line breaking will not take place on either side of it.
You could also use a zero-width space (\u2060). Using this character will not result in any whitespace, but it will still prevent line breaking on either side. This is what you want if you don’t want any space between textFragment2 and textFragment3 but you still want to prevent line breaking there. (It’s also useful if you have a word with a hyphen in the middle of it but you want to prevent the line from being broken after the hyphen.)
You can read more about these kinds of characters on Wikipedia.

Calculate characters that fit a fixed rect TextView

I have seen here people needing to calculate the size of the NSString given a size but I need to do the opposite.
Given a specified rect (or fixed UITextView, or multiline UILabel, no scrolling) I need to know:
if it managed to show all the chars of my NSString
if not, what is the last char shown
So that I can display the remaining text in another UITextView (of course if I could use a single UITextView I would not have this problem).
At first it seems a simple thing to do, but actually I am not finding a way, intuitively I think I could use either UITextView's:
textView.contentSize.height;
or NSString's:
sizeWithFont:constrainedToSize:lineBreakMode:
or a combination of the two, but I need to be precise and those methods do not help me in telling what is the last character that managed to fit the visible area of the UITextView.
Not sure if this is actually possible, but is a requirement of my client who thinks programming iOS is like printing a newspaper and expects to be able to format text around an image....
You could maybe get the maximum height of one line of text from a one character long string.
If you use that with sizeWithFont:constrainedToSize:lineBreakMode: then you should be able to know if your text runs onto more than one line (if the cgsize height is greater than the height of one line of text).
In order to find out the last character (or word) you would have to loop around the length of the string adding characters (or words) as you go and checking for when the cgsize height increases to add a new line, this will give you the character point when to split into multiple strings ( for multiple fields/labels/textviews ) or when to insert line breaks into the text ( if using a single multi-line textview or label ).
I hope you find an easier way...

kCTParagraphStyleSpecifierParagraphSpacing not respected for first line of a CTFrame

We have an app that is dependent heavily on kCTParagraphStyleSpecifierParagraphSpacing to manage spacing between paragraphs, which can vary throughout a body of text. For editing performance, we implemented our main Core Text view as a collection of CTFrames that are drawn/redrawn when appropriate.
We've found that if a paragraph uses a nonzero kCTParagraphStyleSpecifierParagraphSpacing as one of its CTParagraphStyleSettings attributes, this paragraph spacing is ignored if that paragraph is the first item in a CTFrame, even if there is a another paragraph preceding it in the text fed to the framesetter.
I suppose this behavior makes sense if you're drawing to a PDF intended to be printed, but given that we're trying to present our text a a single, scrollable and contiguous block of text, it is giving us problems. Is there any way to work around this problem?
If you can't change the behavior with a CTParagraphStyleSetting, I think it's a bug, or Apple thinks that behavior makes sense. Anyway, to get the result that you desire, I think the best way is to use CTTypesetter and handle lineSpacing and paragraphSpacing yourself. I think the CTFrame implementation is quite buggy, as I just run into another not long ago.
For rolling your own solution, you will need CTTypesetterSuggestClusterBreak or CTTypesetterSuggestLineBreak to calculate char count of each line. Line height can be the font size, and you add lineSpacing when drawing each line. When you encounter a newline(\n) character, add paragraphSpacing before drawing next line.
With CTTypesetter, things are more controllable, of course, it also adds some difficulties since you have to handle line breaks and indentation. But this is the only way I can think of to get a more desired result.
Good luck.

Resources