GSFancyText

baoleihulu edited this page Nov 15, 2012 · 10 revisions
Clone this wiki locally

GSFancyText is an iOS rich text formatting and drawing framework. It supports a subset of CSS attributes, and a few markup tags to apply the styles to the text.

E.g. you can create an GSFancyText instance with the style ".red { color: #ff0000; }" and the markup string "<span class=red>red</span> text". Then you can either use the drawInRect:(CGRect) method or create a GSFancyTextView to present the styled text on the screen.

It has some advanced features, such as inserting customized drawing blocks (the lambda tag); generating accessibility label; re-using the parsing results; specifying line limit and truncation mode, etc.

Quick Start Example

NSString* styleSheet = @".green {color:#00ff00; font-weight:bold}";
[GSFancyText parseStyleAndSetGlobal: styleSheet];
GSFancyText* fancyText = [[GSFancyText alloc] initWithMarkupText: @"<span class=green>Hulu</span> Plus"];

Then you have 2 ways to present the fancy text to the user.

A. Through a GSFancyTextView object

GSFancyTextView* fancyView = [[GSFancyTextView alloc] initWithFrame: CGRectMake(0, 0, 100, 200) 
                                                          fancyText: fancyText];
[self.view addSubview: fancyView];

B. Insert some code into the drawRect method of your own customized UIView subclass

[fancyText drawInRect: rect];

Features

Formatting

Basic Formatting

The CSS styled formatting text is in the following format:

.class_name1 {
    attribute_name1 : value1;
    attribute_name2 : value2;
    attribute_name3 : value3;
}
.class_name2 {
    attribute_name1 : value1;
    attribute_name2 : value2;
    attribute_name3 : value3;
}

The following attributes are supported:

Attribute name Value format Default value
color rgb(255,255,255), #ffffff, red, blue, etc black, or #000000
font-family a string which is a valid iOS font family name Helvetica
font-weight can be either bold or normal normal
font-style can be either italic or normal normal
font-size a floating point number, unit is point 14 (UIFont systemFontSize)
text-align** can be either left, right, or center left
vertical-align can be either baseline, top, bottom, or middle baseline
line-height a percentage value like 200%, or a pixel value like 50px, or 50 100%
margin-top** a percentage value like 200%, or a pixel value like 50px, or 50 0
margin-bottom** a percentage value like 200%, or a pixel value like 50px, or 50 0
margin-left** a percentage value like 200%, or a pixel value like 50px, or 50 0
margin-right** a percentage value like 200%, or a pixel value like 50px, or 50 0
line-count** an integer. Specifies the maximum line count. 0 (which means no limit on line count)
truncation-mode** can be either head, tail, middle, or clip. Used when line-count limit is applied tail
text-shadow horizontal-offset vertical-offset [blur] [color] By default there is no text shadow. With text-shadow, the default blur distance is 0 and the default color is black.

** These attributes can only be used with <p> tags, not <span> tags.

The following markup tags are supported to apply styles to text

Tag Note
<span class = css_class_name> </span> Formats inline text with the CSS class
<p class = css_class_name> </p> Make the text inside a newline and format it
<strong> </strong> make text bold
<em> </em> make text italic
<lambda id = _id width = _width height = _height> A space for inserting customized drawing code

Note: Tags with unsupported tag name will be treated like <span>. E.g. <a class=class_name>dog</a> will apply the style of class_name to dog, and will not create a new line.

Using Lambda element

Lambda tags are used to insert inline drawings.

A quick start example of using Lambda:

GSFancyText* fancyText = [[GSFancyText alloc] initWithMarkupText: @"Hulu Plus <lambda id=tv width=40 height=40>"];
[fancyText defineLambdaID:@"tv" withBlock:^(CGRect rect) {
    UIImage* image = [UIImage imageNamed: @"tv"];
    [image drawInRect: CGRectMake(rect.origin.x, rect.origin.y, image.size.width, image.size.height);
}];

The image can be in the same line of the text, or the next line, depending on if there's enough space left after the text.

The drawing block can contain any drawing code, like line/path drawing, text drawing, multiple image drawing.

A lambda segment can also be formatted with a style class. E.g. we can set its vertical align to top. (Apparently it ignores attributes like font, color, truncation-mode, etc.)

Text/style switching

There are several reasons to do change some text and/or style after parsing is done.

  • re-use a parsed structure for better performance
  • make flexible changes at any time

Text switching can be based on ID, e.g.

fancyText = [[GSFancyText alloc] initWithMarkupText:@"<p id=title_line>the dummy title</p>"];
[fancyText changeNodeToText:@"the <real> title" forID:@"title_line"];

In this case the <real> will not be treated as a tag, but a plain text.

or

fancyText = [[GSFancyText alloc] initWithMarkupText:@"<p id=title_line class=some_class>the dummy title</p>"];
[fancyText changeNodeToStyledText:@"the <em>real</em> title" forID:@"title_line"];

In the above example, the real title will inherit the styles defined in some_class, but the word real can ignore the font-style attribute defined outside, since the em has higher priority.

Style switching can be based on ID, or class, for example

[fancyText changeAttribute:@"color"
                        to:GSRgb(0x999999)
                        on:GSFancyTextID
                  withName:@"title"];

This will change the color for the span or p with id=title.

There are a number of style changing methods, please refer to the API documentation.

Re-rendering

If your fancy text is ever going to change the form it's displayed (e.g. the line width may change after rotation, or the text content/style may change after some user action), it's recommended to use a GSFancyTextView object.

fancyView = [[GSFancyTextView alloc] initWithFrame:frame fancyText:fancyText];

When a change in fancyText is need, you can use fancyView.fancyText to refer to the fancy text object.

After any change is made (either view frame or fancy text), call

[fancyView updateDisplay];

Global and default styles

To set a global style:

NSString* styleSheet = @".green {color:#00ff00; font-weight:bold}";
[GSFancyText parseStyleAndSetGlobal: styleSheet];

If a global style is set, all GSFancyText objects will be applied with the classes defined there.

You can append specific styles on top of the global style:

fancyText = [[GSFancyText alloc] initWithMarkupText:@"..."];
[fancyText appendStyleSheet:@".red{color:red}"];

To remove existing styles and set to a new one, just directly set the style property of a fancy text to a parsed style dictionary:

fancyText.style = [GSFancyText parsedStyle:@".yellow{color:yellow}"];

In either the global style sheet or a specific style sheet, you can use class name "default" to apply styles to texts that don't have a class. E.g.

.default {color:white}

Then all texts will be in white unless otherwise specified.