-
Notifications
You must be signed in to change notification settings - Fork 22
GSFancyText
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 "red 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.
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];
The CSS styled formatting string 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 |
** These attributes can only be used with
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 . E.g. dog will apply the style of class_name to dog, and will not create a new line.
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.)
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 initWithMarkupText:"<p id=title_line>the dummy title</p>"];
[fancyText changeToText:@"the <real> title" forID:"title_line"];
In this case the will not be treated as a tag, but a plain text.
or
fancyText = [GSFancyText initWithMarkupText:"<p id=title_line class=some_class>the dummy title</p>"];
[fancyText changeToStyledText:@"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.
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];
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.