Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Attributed string with text attachment and truncation

I've got an attributed string with an image attached (NSTextAttachment). This works alright, but I've got a problem with truncation that I can't seem to solve.

In the example, suppose the string ## is the image. So my string looks something like Hello world! ##. Tail truncation is set on the paragraph style.

Now, if the space is constrained, the text is truncated with ellipsis (which is what I want). But unfortunately the image is truncated as well.

So the result is something like:

Hello w...

but I'd like it to look like:

Hello...##

That is, I want the image attachment to not get truncated, it should always be visible.

The reason for the attachment is that I want the image to always be at the end of the string, so when the text is short the image is at the end and when the text wraps to multiple lines I also want the image to be at the end. Trying to manually position the image "on the outside" wouldn't work as the text then wouldn't get truncated correctly.

So, is there a way to tell NSAttributedString not to truncate the image?

Example code that produces the attributed string:

NSString *title;
NSMutableAttributedString *attributedString;
NSMutableParagraphStyle *paragraph;
NSDictionary *attributes;
NSTextAttachment *attachment;

paragraph = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
paragraph.hyphenationFactor = 1.0;
paragraph.lineBreakMode = NSLineBreakByTruncatingTail;

attributes = @{
    NSForegroundColorAttributeName : [self titleTextColor],
    NSParagraphStyleAttributeName : paragraph,
};

title = @"Hello world!";
attributedString = [[NSMutableAttributedString alloc] initWithString:title
                                                          attributes:attributes];

attachment = [[NSTextAttachment alloc] init];
attachment.image = [UIImage imageNamed:@"myImage"];
[attributedString appendAttributedString:[NSAttributedString attributedStringWithAttachment:attachment]];
[attachment release];

self.titleLabel.attributedText = attributedString;

[attributedString release];
[paragraph release];

Edit: A vital part of this (which gets lost in the description above a bit) is that this solution needs to work for multiline texts.

like image 562
DarkDust Avatar asked Apr 01 '14 09:04

DarkDust


2 Answers

This is not easily achievable. You could set the paragraph style to NSLineBreakByTruncatingMiddle, but then the result would be that the last line is truncated in the middle ("Hel...rld!##")

So you have a few options.

  • You could redesign so that the image isn't placed at the end of the text.
  • You could calculate yourself the end of string, truncate it and add the text attachment - not simple to do.
  • Use iOS7's Text Kit to implement a custom truncation logic. Unfortunately, despite what has been written erroneously on many blogs and Apple's own documentation, UILabel does not use TextKit to render text, it uses CoreText, thus making things a little harder. I would recommend dropping the UILabel entirely and going with a custom UIView implementation with Text Kit backing. You can find here a small example of a view drawing text using Text Kit, resulting in a view similar to a label. Now you can get bounding boxes of the drawn glyphs, and place the image correctly.

Neither option is perfect. You have not mentioned what the image is and why you need it at the end of the last line. I would probably go with option #1 and change the design a little, although the Text Kit option is not that difficult to implement.

like image 123
Léo Natan Avatar answered Nov 14 '22 11:11

Léo Natan


I do not think that you get the desired results with "tricks". You have to do the real work: Subclass NSTextContainer and overwrite -lineFragmentRectForProposedRect:atIndex:writingDirection:remainingRect:. That includes setting up your own text stack.

The code is too long to be posted here. But there are some samples in documentation. (The documentation of OS X is richer, IIRC. There are differences, but you can use it for a basic understanding of the parts.) So this answer is still a pointer.

As you are living in Munich, I assume that you understand german. Therefore I want to mention that there is a code sample in the second part of my book, chapter 6, for a view that layouts text around a hole in the middle. You can do the same by sparing out a rect at the end.

Hope that helps!

like image 45
Amin Negm-Awad Avatar answered Nov 14 '22 13:11

Amin Negm-Awad