Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamic attributed text

Tags:

ios

ios6

I'm not sure what my options are for doing the following:

enter image description here

The text in red will actually render in red and is also dynamic - the length will change.

Should I use an NSAttributedString and figure out the dynamic indexes and length to apply the attribute to?

Or is it possible to make each word before the dash its own label? (I'm not sure if it's possible to place multiple labels inline and have each one push the rest to the right as the size increases, kind of like how floating works in HTML/CSS)

like image 892
The Muffin Man Avatar asked Jun 16 '13 05:06

The Muffin Man


People also ask

What is a dynamic font?

ABSTRACT: Dynamic fonts are fonts whose character shape is defined every time the correspond- ing character is printed rather than when the font is defined as a whole.

What is an attributed string?

Attributed strings are character strings that have attributes for individual characters or ranges of characters. Attributes provide traits like visual styles for display, accessibility for guided access, and hyperlink data for linking between data sources.


1 Answers

The best option is definitely to do this with a single NSAttributedString in one label, because your other options are:

  1. Use multiple labels, change their frames every time something changes, move them around, figure out exactly how wide a space should be, make sure they aren't too wide for the screen or the view they should be in, etc. etc. As you can see, this will very quickly end up being very painful.

  2. Go over the top and use a UIWebView, which is clearly overkill for displaying single label, because it gives you all these amazing things that you don't need like scrolling and zoom and JavaScript and stuff, and all of these features are using CPU cycles, regardless of whether you use them or not, so this definitely isn't a very good option performance-wise, especially if you're going to have a large number of these (e.g. in every cell in a table view)

Whereas if you use a single string, all you have to do is calculate the ranges for the attributes correctly, assign the attribute string to UILabel's attributedText (note, iOS 6+ only, but judging by the tags on your question that's not a problem for you), and you're done. I'm currently using this approach quite extensively throughout my app, and it's been working well for a while (I did try the other two before settling on this one, and they were both quite painful to get working).

Here's a very basic example of doing this using an attributed string:

UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(10, 50, 300, 50)];
label.font = [UIFont fontWithName:@"HelveticaNeue-Light" size:16.0];
[self.view addSubview:label];

NSString *category = @"Socks";
NSString *store = @"Apple Store";

NSMutableAttributedString *string = [[NSMutableAttributedString alloc]
                                       initWithString:[NSString stringWithFormat:
                                       @"in %@ at %@ — 1 day ago",
                                           category, store]];

[string addAttribute:NSForegroundColorAttributeName
               value:[UIColor redColor]
               range:[string.string rangeOfString:category]];
[string addAttribute:NSForegroundColorAttributeName
               value:[UIColor redColor]
               range:[string.string rangeOfString:store]];

label.attributedText = string;

Note that this will not work as expected if, for example, the value of store and category are the same, since rangeOfString: will find the first occurrence of the given string, meaning that the category string might be highlighted twice, and the store string not at all.

Better example: Here's a slightly more advanced example that won't break because of equal strings, albeit with the added annoyance of having to manually calculate the ranges:

NSString *in = @"in ";
NSString *category = @"in";
NSString *at = @" at ";
NSString *store = @"in";

NSString *text = [NSString stringWithFormat:@"%@%@%@%@ — 1 day ago",
                                            in, category, at, store];
NSMutableAttributedString *string = [[NSMutableAttributedString alloc]
                                      initWithString:text];

[string addAttribute:NSForegroundColorAttributeName
               value:[UIColor redColor]
               range:NSMakeRange(in.length, category.length)];
[string addAttribute:NSForegroundColorAttributeName
               value:[UIColor redColor]
               range:NSMakeRange(in.length + category.length + at.length,
                                 store.length)];

This way will always work correctly, even if all of your strings are equal, however it's slightly more lengthy code-wise as you have to manually calculate the ranges to be highlighted (although this isn't really a major issue or a lot of code in absolute terms).

UPDATE: Just adding a simple example for using a bold font so you don't have to look for it:

[string addAttribute:NSFontAttributeName
               value:[UIFont fontWithName:@"HelveticaNeue-Bold" size:16.0]
               range:NSMakeRange(in.length, category.length)];
[string addAttribute:NSFontAttributeName
               value:[UIFont fontWithName:@"HelveticaNeue-Bold" size:16.0]
               range:NSMakeRange(in.length + category.length + at.length,
                                 store.length)];

Have a look at the NSAttributedString UIKit Additions Reference for more attributes that you can add to the string to achieve the effect you want.

like image 61
Greg Avatar answered Nov 15 '22 22:11

Greg