Video hosted by Apple at devstreaming-cdn.apple.com

Configure player

Close

WWDC Index does not host video files

If you have access to video files, you can configure a URL pattern to be used in a video player.

URL pattern

preview

Use any of these variables in your URL pattern, the pattern is stored in your browsers' local storage.

$id
ID of session: wwdc2012-222
$eventId
ID of event: wwdc2012
$eventContentId
ID of session without event part: 222
$eventShortId
Shortened ID of event: wwdc12
$year
Year of session: 2012
$extension
Extension of original filename: mp4
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: [2012] [Session 222] Introductio...

WWDC12 • Session 222

Introduction to Attributed Strings for iOS

Essentials • iOS • 50:41

Text is an essential part of your application's interface. Get an introduction to the concepts behind manipulating and drawing attributed strings on iOS. Learn how UIKit has adopted attributed strings to make creating text effects even easier.

Speakers: Ian Baird, Johannes Fortmann, Aki Inoue

Unlisted on Apple Developer site

Downloads from Apple

SD Video (105.6 MB)

Transcript

This transcript was generated using Whisper, it may have transcription errors.

Good afternoon. This is session 222, Introduction to Attribute Strings for iOS. I'm Ati Inouye from the Cocoa Group. For the last couple of years, we've been getting numerous feedbacks requesting multi-style string drawing in iOS, much like active integration on OS X. As Chris announced yesterday, spectacularly in his "What's New in Cocoa Touch?" session, the wait is over.

We are introducing UIT NSF Eastern integration in iOS 6. These are the items we're covering today. First, I'm going to start with activism essentials-- how to create, how to modify, and how to work with them effortlessly and efficiently. So, with this material, you should be able to bootstrap and work with the attribute string right away. And I believe the materials present here should be valuable even for those of you who are already familiar with attribute string via Quartext. Then we're going to see some of the drawing attributes UITit provides. Later on, you'll see how these new capabilities are integrated into the UITit control classes. Let's get started.

So, if you go to iOS 6, when you wanted to render a string like this, you'd use ULLabel or your string drawing API. The data model for this functionality is based on NSStream. When you want to render text, you specify a font, you specify a string, along with other required information. For example, you specify a string and a font, string, and the bold form, string, font, color. And there was no object bindings attributes together. into attribute string, the object associates these attributes to the characters.

So, you can attach font to your string object. You can specify a color to a range, or even specify various fonts to different ranges. So, what can you exactly do with these attribute string objects? The first advantage is, of course, you can create multi-style attribute string. You can render and measure text, multi-style text as a single unit. This is from Stocks application.

And as you see in the new section, it shows both headline and summary. The both string is the headline. When it requires more than one line, it wraps to the second line. Then the summary string in regular typeface is concatenated right after it. It's very easy. Another advantage. is coming from the ability to attach an arbitrary number of attributes.

to its characters. That way, we can extend the capability without complicating the API much so that we provide much more expressive options in terms of text formatting via addressing the API. This is an example from Reminders. In order to reserve the space for priority icon, we are shifting the first line. You know, like this used to require you to manually measure lines and render them separately. Now it's as easy as creating an attribute string with indent option and render.

Also, we're now more expressive in terms of graphics representation. Shadow, outline, underline, so on and so forth. This is rendered entirely using UIKit attributes. You don't have to go down to... core graphics anymore, you can have this kind of text effects right from the comfort from your UITED object-oriented programming. What's NSAT Read Stream? NSAT Read Stream is not new, actually. In fact, it's been available in the foundation framework since iOS 3.2.

It's not just been used by your kid up until now. And these two base methods represent the model object for the-- the data model for the other recent object. It has a string and a dictionary of attributes per character. And this is the designate initializer for the object. In it with string attributes, it take a string and attributes. The attributes are specified to the entire string this way. So, let's create an attribute string. We're using NSFontAttributeName, an attribute key for specifying UI font to your string here. It's new for iOS 6. And using the initWithString attributes, we are creating a string with the system font at 12-point size here. Very easy.

One thing to note here is that attribute string associates attributes to characters. So, for an empty string, there's no attributes associated with it. This is an important characteristic of attribute string, so I'm going to revisit the fact later in this talk again. So now you created attribute string, let's modify it. Just like other foundation data object, this attribute string has clear separation between mutable and immutable classes. In this mutable attribute string, Mutable subclass of NSFH string has these two groups of mutable methods for changing attributes and changing string contents. Let's change the attributes first.

This is the best method for changing attributes. Set attributes range, it replaces the attributes at the range with the specified attributes. Straightforward. However, you typically work with these convenient methods for adding and removing attributes. This way, you can focus to a particular attribute. For example, if you have font picker UI, the object can focus to NSFont attribute name. And similarly, if you have color picker UI, you can focus to the text color attributes, so on and so forth.

So, these methods are useful for modifying existing attributes, of course, but it's also useful when you're creating the multi-style attributes. You might think when creating multi-style attribute string, You might consider instantiating an attribute string plus the run and concatenate together at the end. However, it's much more efficient to instantiate the entire string up from the modified attributes as necessary. Let's create multi-style attribute string efficiently. Here we're using another attribute new to IOS 6 and it's program color attribute name for specifying text color using UAColor.

And we're instancing the attribute string using init_twee_string attributes, but in this case, we are instantiating NSMutable attribute string so that later you can modify the contents freely. And here, we're using add attribute value range to specify the red color to the time stream. And here, now you have multi-saggible stream. Now, change the stream contents.

This is, again, the best method for changing the string contents for attribute string, replace characters in range with string. Just like there, just like its attribute counterpart, we have a convenient method for adding, replacing, and inserting attribute string. But I recommend to stick with the base replace characters in range with string method because this way you can avoid instantiate temporary attribute string just for modifying your string contents. So let's work with the-- the perspective's arranged with string here.

Here's an example. We have the same neurobiotic restraint from the previous example. We want to move the dentist appointment one earlier at 8:15. Using replace characters in range with string method, we accomplish that. Notice that we just specify a string here, yet the attribute is still there. Why is that?

because the attributes are preserved while you are working with the stream contents. That way... Thank you. - That way, you are still-- when you are working with string contents, you can focus to the string contents and forget about attributes. In order to do so, you can just remember three simple rules. First, when you're replacing a range of text, the attributes for the new string is coming from the first character of the old range.

When you're inserting, the attributes coming from the previous character, and when you're inserting at the beginning, you don't have the previous character, so you have the attribute from index 0. Now, remember, I mentioned no character, no attribute rule. because the attributes are associated with characters. When there's no attributes, there's no attributes to be inherited to a new string inserted. So, with this characteristic, you might get surprised sometimes. You might want to be careful about this.

In this example, we are inserting a string at rotation three Using the rule 2, the attribute is coming from the previous character, the lowercase "c." And with this example inserting at the beginning, we're getting out to get the index 0, the lowercase a, And also, when you are replacing a range of characters, you're getting the attribute from the first character from the old range, so lower Tc gives the attributes a new string. Finally, when you erase the entire string, there's no attribute anymore. So, when you insert a new string, there's still no attributes. So, you want to remember this fact.

Now you created modified string content and changed attributes and preserved attributes. Let's work with attributes inside the attribute string. Internet attributes... Using attributes at index effective range method, it returns an NSDictionary of attributes at specified location. Notice that the method takes another argument, a pointer to an NS range.

Upon returning from this method, it's filled with the considerable range for the star round at the location. So, it's useful when you want to identify the style run you are working with. But one thing to note here is that it doesn't return the maximum range possible because the way API is designed to be effective, it can return the date range readily available.

So when you have a string like this and access the attributes location 4, The range argument could be filled at index 3 and length 3. However, it's possible to get shorter range, or even just the character itself. So if you want to work with the attributes sequentially, and Qualys, the operation on style rams, you can use this convenience method.

attributes at index longest effective range and range, and attributes at index longest effective range and range. This method takes extra effort to ensure the range you get is the maximum continuous range for the start run. So when you access string like this, with attributes that index from this effective range in range, you get the full style runs as expected.

Similarly, if you use attribute at index, longest effective range in range with NS1 attribute name, you get two ranges. Similarly, if you use NSProgramColor attribute name, as you expected, three ranges. Very simple. So, if you want to work with attributes sequentially, process style run one by one, you can use this attribute animation method. It takes a block and the block is invoked per style run.

So, it's useful when you want to change the attributes based on existing attributes. For example, if you want to change the font attribute from the regular typeface to bold typeface, it's very useful. Similarly, it's useful for when you want to change the string contents while preserving the multi-style configuration.

How do you do that? In this example, we are advertising the entire stream that has multiple styles, and you want to preserve the styles there. To do so, first, we're going to get the current locale because the appartage operation is locale-sensitive operation. Then we're gonna call animate attributes and range options using block.

Instead of block, we create a substring using the specified range and replace the characters, and here using the upper case upper test string with locale, specifying the locale to do the proper local sensitive operation. And the result is upper test string, while the attributes are still preserved. Now you're familiar with the attributes string. Let's take a look at some of the drawing attributes we introduce in iOS 6.

This is an example string from the previous section. Once you have an attribute string like this, rendering is very easy. Just send the message to our point. That's it. In iOS 6, we're introducing NSStreamDrawing API from UI 10. It's much like the NSRT stream categories found in OS X. The method has two major groups, one simple, one extended. In a simple drawing method, we have drawing rect, draw point, size, very simple interface. Yet, because the abilities we are providing through attributes, they can perform pretty sophisticated tasks here.

Drawing reg renders itself inside a specified reg. And depending on the setting inside that restring itself, it can wrap or truncate itself properly. And drawAtPoint can render itself at a specified point. And since there's no constraint size specified here, it just renders as a single line mode. Similarly, the size method does the single line measurement of the receiver. And these are pretty much a gibbering, their counterpart in US String Drawing API. Drawing Rath is very similar to drawing Rath with phone line break mode alignment. And Draw Point, similar to Draw Point with phone size method with size with phone.

And we are introducing a slew of attributes you can use in iOS 6. You have many bells and whistles for configuring the appearance of layout of your attribute strings inside the applications. Here, I'm focusing to three of the basic attributes you're gonna be commonly using again and again.

You've already seen the first two, NSFont attribute name and NSForegroundColor attribute name. The third one is an NSParagraph attribute name that controls the text formatting options. Each of these attributes uses UI font, UI color, and NSParagraph style object as its Notice that some important characteristics, again, An attribute missing from attribute dictionary means that's implying the default value, not just unspecified. What does that mean?

Instead of just leaving the attribute unspecified, we use the default value for each attribute if they are missing from the attribute dictionary. For example, for the font attributes, we use the system font of system font size and the black color for text color, and similarly default paragraph style for the paragraph style attribute.

Here is an example illustrating the default value usage. Because since there is no unspecified attributes in the string, the string can always produce an expected result for you. For example, you have an attribute string here, this time using initWithString method. It's a convenience cover for the initializer. And this method does take any attributes. So the attributes is essentially empty for this attribute string.

Now, we are rendering, we are filling the background with red color. What this means is the CG content color property is left with a red color setting. Now, we render with the empty attributes. The end result is instead of using the CGColorColor property, we are using the default value, black color, to render here.

Now let's take a look at the final attribute in this session in the form of style attribute name. And this paragraph style encapsulates various paragraph-wide text formatting options for you. line break mode, alignment, line spacing, so on and so forth. They give you powerful text formatting capability to your applications. And because of that, you can have rich word processor-like text formatting in your application.

And notice that the single paragraph-style object is shared for an entire paragraph. Because the way attribute string works, you can apply and assign multiple paragraph style objects to a string, but only the first attribute, the first one associated with the first character in the paragraph is used for the entire paragraph style. And this is an example we showed early in this session from reminders. It's using the indenting option using Power Star to reserve the space for the priority icon.

We have this setting in Pargas style, line break mode, alignment, spacing for lines, between lines, and between Pargas. indentation, you can control margin from left to right and for the first line specifically, and you can have finer control over line height. And finally, you can control hyphenation and right to left settings using the paragraph style. We're going to see two of the commonly used Firehose style settings here.

First, line break mode. It's very similar to UI line break mode you are familiar with. We are creating mutable ParagraphStyle just like other objects. The NSParagraphStyle has separation of mutable and immutable classes. Here we're using NSMutableParagraphStyle. The init method returns the ParagraphStyle instance configured for the system default.

So all you have to do is just change the setting you want to customize here. Here, you are changing line break mode to tail-transition, and using this paragraph style, the end result is tail-transited text. Alignment, very similar to UATx alignment, you can specify left, center, right alignment as expected. In addition to that, we are providing full justification.

And finally, you can specify a natural alignment. What does this mean is that When you specify this setting, for majority of all languages, the text is left online, but if you are working with right-to-left languages such as Arabic or Hebrew, it's gonna be automatically right aligned. You still have appetite for more rendering power? We have advanced attribute stream sessions. We're going to show more bell and whistle for tweaking your application's attribute stream. So it's good to come back for this session you want to do these tricks.

Now, I'd like to switch over to my colleague from UIKit group, Ian Baird, who's going to show how these new capabilities are integrated into UIKit controls. Thank you, Aki. Hi, I'm Ian Baird. I'm a label engineer for UIKit, and I'm happy to talk to you about UIKit's adoption of attributed strings.

Or how do I get this stuff into my labels? The first thing to remember is that UILabel is really your nexus of power. If you understand how to use attributed text with UILabel, you essentially understand how to use attributed strings with the rest of UIKit. This is a UI label with a title. It says, hi, I'm a UI label with a title. And you can see it's unadorned. We've used thousands if not millions of these in our apps. I'd like to introduce to you today UI label with an attributed title.

There are a few existing properties on UILabel that you've probably used before. The first one is the text property. This takes an NSString instance, and it tells the label what you want it to represent. The next property is the font property. As you see, we've changed from the system to font, in this case, Helvetica Noia, to one of my favorites, Hefler text.

Next, you can change the text color by supplying a UI color instance to the text color attribute. You can even change the text alignment, as we have done here, changing the text alignment from center aligned to left aligned using the text alignment property You can set up word wrapping using line break mode. You can add a shadow with shadow color. And you can change the shadow offset.

We've added a new property to this, attributed text. As you can see, now you can supply an NS attributed string to UILabel, and it will represent your attributed string as we have done here. We've also added a new property, minimum scale factor. This tells Label exactly how small it can shrink all of your fonts before we'll begin to truncate the text to try to fit it within the bounds of the Label. We also added a new Boolean property, adjust letter spacing to fit width. This tells us exactly how much-- it tells us exactly that-- sorry. It tells us we can adjust the tracking between your characters to try to fit all of the text in the UILabel's bounds.

We've deprecated a property. Minimum font size. In a new world where you can have multiple fonts and multiple font point sizes, it no longer makes sense to specify a minimum font size. We would prefer that you instead use the new minimum scale factor property. There are a few things to remember when using labels and attributed strings. And this is one of the most important. When you're using any of the existing style properties, the properties are going to apply the style to the entire string. So, for example, if I set the font with the font property, NSFontAttributeName is going to be set to Helvetica Noi for the entire string. Next, if I change the text color using the text color property, you're going to change the text color for the entire attributed string.

and that's label. Next today, I'd like to tell you about what we've done with button by building on the methods you already know. First, the setTitleForControlState method allows you to supply, again, an NSString for a certain control state for the button. This will render the NSString on the face of the button.

New in iOS 6, set attributed title for control state allows you to supply an NS attributed string for a UI control state. And an important point to remember is that this new attributed title is going to take precedence. If you set title for UIControlStateNormal and set attributed title for UIControlStateNormal, the button is going to show the attributed title. We've even changed Pickerview to Pickerview Caliente.

To achieve this effect, just implement PickerView attributed title for row for component in your UIPickerView data source. We've added some properties to UITextView. The first one is attributed text. You can supply an attributed string to UITextView, and it's going to render the string as its content. Next, we've added a Boolean property, allows editing text attributes.

We're excited about it, too. It allows your users to interact with and change any of the style properties in the text view on the attributed string. You can then fetch this attributed string back out via the property. Any new update to the API of UIKit, any talk about new updates to UIKit is really incomplete without a discussion of TableView. TableView, as Chris said, is really the workhorse of UIKit. And today I'm happy to tell you there are no new properties or methods. Just set attributed text on the cell or header views text label. My colleague, Johannes Fortmann, has prepared a demo, and he's going to show you all this cool new functionality in UIKit.

Thanks, Ian. Hi, I'm Johannes Fortmann from the UIKit team. And I've prepared a little application that shows you how to use these attributed strings in your app to render beautiful text. My application is basically very simple. I have a few pages from speeches from famous plays. And I render them. You might use that on the bus to learn your text if you're an actor, for example, or just to look at the beautiful typography. Let me show you how that looks right now.

I'm switching to demo here. I'm going to run this for you so you can see how it looks. And here we go. As you can see, we have a beautiful collection view, our new class here. And we are showing several pages from speeches like Midsummer Night's Dream. And when you tap one of these, you can see a nice, though unstyled rendering of these.

So what I want to do first here is transform my app, which has been written against the old version of the OS, apparently, to use the new amazing features we have. I've already put in a method to compare and contrast these. So if I slide here, we see nothing, because I didn't actually implement the rendering, It should switch between these two rendering modes. So let's look at the implementation of this. Looking at the implementation here, we have a page view, and this page view renders pages paragraph by paragraph.

So what we're doing here is basically our data model has these pages laid out and separated in paragraphs. We are going -- walking through the page one paragraph at a time, drawing it, and sizing it up. We also have a case distinction here. If we're using unstyled drawing, we are just using the old rendering path, and the styled drawing, well, it's implemented with a copy here. That's obviously no good. So I'm going to delete this.

So first, if we want to render an attributed string, we need to get an attributed string. I've already implemented a method on my data model to actually format the string. And I'm going to call it to get my string, which I will then later render. Then because we have to advance our page cursor down our page, we have to size up our string. I'm going to do that. And then finally we have to actually draw it. That's it. That's how easy this is. Let's have a look at this.

All right. So, this is our new rendering. And I'm zooming in here so you can actually see that we are rendering with attributes because this is kind of tiny on the big screen. You see how we are beautifully rendering in two different fonts. And if I use my switch gesture, please pay attention to the will here because that's going to change. I'm switching back to the simple rendering now. And as you can see, the simple rendering, unlike the new attributed rendering, doesn't support attributes like the kerning attribute. So we don't actually kern in the simple rendering, whereas we do proper kerning with the attributed rendering system, which allows us to render this much more beautifully.

All right. Let's switch back to this overview here. And as you can see here, our title of our page is using a UI label. And right now, we are basically using the standard UI label. We are setting the title. And we are setting the text color. But we are not making use of the new attributes here. So let's change that by bringing this label into the new exciting world of attributed strings. I'm going to pull up my page preview cell. As you know, the collection view uses cells to render each of these objects we see here. And this is the cell we are using.

Basically, what we're doing right now is what I just said. We have a text label, set its text to the title of our page, and then we're setting the color. Now, we won't need this anymore, so I can delete it. And what we actually need to do now here is we want to create an attributed string. What we have to do is first we have to create a style dictionary which defines the style of our attributed string.

And I'm going to do that. I'm choosing to leave the font name empty so we use the default font as we did before. We didn't set a font on the label. I'm only setting the foreground color to be a red color, just like before. Okay. Then we have to create the attributed string here. That's a simple call to the alloc init method. We have to init with the old string, page title, and use the attributes to set on this string. And finally, I have to assign it to my label. That's it. Let's look at this.

Well, that was boring. We didn't change anything. It looks exactly the same. But we can see here that at least we've verified that our attributed string rendering works exactly like it did before with the non-attributed rendering. Let's go back and spice this up a little. You can see in our labels we have basically always the name of the play and then the name of a scene within this play. What I want to do now is kind of emphasize the name of the scene here. So I want to set a different font, a bold font. Let's do that.

I've already created a mutable attributed string, so I can now change the attributes on our attributed string. And to change the attributes on part of the string, what I have to do is first actually find the separator that divides our name of the play from the name of the scene. That's a hyphen in this case. So I'm going to find this hyphen. Oh, first I'm going to, of course, create the font I want to later set.

Then I'm going to find my hyphen. That's just a call to range of string. Now I have to move my range that I just created to just after the hyphen because we don't want the hyphen to actually be bold. And finally I have to extend the range to the full extent of my scene name. So let me do that.

The last thing I have to do is actually set my new bold font on this range, this range only, of course. So, I'm doing that. Let's have another look at the current appearance of this. All right. I'm going to zoom in here so we can actually see that this is bold. And it's a very nice bold. It's understated and tasteful, yet kind of beautiful.

All right. Let's have a look at the page view again. Now you see here we have these stage directions. I'm going to show you with the mouse here. It's the Oberon leading the fairy singing dance here, for example. And these are kind of indented. I did that with a few spaces. It's not as good as it could be. And, well, it's also black like the rest of the text, so people might actually accidentally read this. So what I want to do now is change these stage directions to be first making use of the NSparagraph style attribute centered and second to have a slightly different color and better font. So let's do that. And here I'm going to show you how I'm formatting these strings.

I do that in my data model in the attributed string for paragraph method which I'm pulling up now. And this is really very simple. What we're doing here is we get the speaker for the current paragraph, we get the text, and then we create an attributed string for the speaker and an attributed string for the text. Finally, we append those. So -- and then we return the value for current paragraph. Now, you can see here the attributes we are using are attributes that are apparently instance variables here, which are already predefined. That's a good idea because we don't want to create a new dictionary every time we format a simple paragraph. So let's look at where we define that. I'm doing that in my designated initializer here. In the knit with title method, that's my pages initializer.

And you can see here I'm creating the speaker attributes with my speaker color and the bold font, and I'm creating the text attributes with the text color and, well, the regular font here. And now I want to create my stage direction attributes. Now, as I said, I want these to be centered, so I have to create an NSParagraphStyle, in fact, an NSMutableParagraphStyle. I'm going to create that one.

That's my centered style. It's the default style, only it's mutable. And now I have to actually set its alignment to be actually centered. That's this line where I just set the alignment. I also have to create a font that's for this paragraph style I chose to use an italic font that's a bit lighter. That's the... HelveticaLightObliqueFont. Now, having created that, all that remains for me to do for this is to actually create the style dictionary and stow it away into my instance variable.

All right. Now that we have initialized our style dictionary, I'm going back to the attributed string method. And now I can format my paragraph. Of course, I only want to format it if it's actually a stage direction. Otherwise, the current formatting is fine. So I'm distinguishing the two cases. This one here is if the paragraph is a stage direction. And the other one is my old one, old case. Let me reformat that quickly. That's the case where we format as usual.

Okay. In the stage direction case, all that remains for me to do is actually format the text with my new paragraph style. And well, append it to my return value. Let me run that for you. And here we go. Let me zoom in. And now my paragraph is actually centered, and the font is changed, and the color, too. All right. Let me switch back to slides.

So to sum this up, you've seen how to actually draw text using the attributed string methods. You've seen how easy it is to enhance an existing label. So you have a label which is pretty common case. And you want to spruce it up just a little by adding extended attributes. And finally, you have seen how to use paragraph styles. And with that, I give back to Ian.

Thank you, Johannes. So you've seen a lot of things today. Thank you. You've seen the basics of using, creating, modifying, and querying attributed strings. You've seen how to do this. Aki took you on a whirlwind tour of the API. Next, you saw how to draw them and use them with paragraph styles. And finally, we showed you how they were adopted by UIKit.

If you'd like to know more, I'd recommend contacting our UI Frameworks evangelist, Jake Behrens. He'll be happy to help guide you in your quest for more information about attributed strings and their use with UIKit. Next, you can also visit the UIKit documentation online. And finally, if you haven't visited the Apple Developer Forums, I would urge you to. It's a great place to go to give and receive peer support and interact with employees like Aki, Johannes, and myself.

Next, we have a few related sessions. As Aki talked about earlier, we have our advanced attributed strings for iOS Talk. You're going to learn about shadow attribute, stroke attribute, and a host of other attributes that we talked about today but didn't go into depth for. Next, you should catch the video for keyboard input in iOS. They're going to talk more about UI text field, UI text view, and their adoption of attributed string. Finally, there's going to be a session on core text and fonts. Core text is the awesome, awesome framework which underlies all of this new technology. So I would urge you to go and see this session.